Why doesn't middleware execute after mapping controllers? - .net-core

I am building the most basic of an ASP.NET Core Web API, and after a lot of experimenting on my own, I found that running any middleware (custom, app.run, app.use) after the .MapControllers() method doesn't work at all.
internal class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseEndpoints(endpoint =>
{
endpoint.MapControllers();
});
}
}
I'm asking here because the documentation didn't clarify: how does the MapControllers() method affect other middleware in the pipeline?

I found that running any middleware (custom, app.run, app.use) after
the .MapControllers() method doesn't work at all.
The issue might relate the middleware order, using the MapController method, it already find the endpoint and return the response, so it will not invoke the next middleware.
You can refer the following code: in this sample, by using the MapControllser method, it can't match the /map1, so we can access it using https://localhost:44379/map1 (change the port to yours).
The result like this:
More detail information, see ASP.NET Core Middleware.

Related

No service for type 'CommanderContext' has been registered

I am using .NET 5.0 and HotChoclate framework to create a GraphQL API.
Below is my ConfigureServices Method
public void ConfigureServices(IServiceCollection services)
{
services.AddPooledDbContextFactory<CommanderContext>(options =>
{
options.UseSqlServer(_configuration.GetConnectionString("default"));
});
services.AddGraphQLServer()
.AddQueryType<Query>()
.AddMutationType<Mutation>()
.AddFiltering()
.AddSorting()
.AddProjections();
}
To resolve the concurrency issues. I am using AddPooledDbContextFactory() method.
https://chillicream.com/docs/hotchocolate/integrations/entity-framework.
Below is my Configure Method
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
InitializeDatabase(app);
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGraphQL();
});
}
private void InitializeDatabase(IApplicationBuilder app)
{
using var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope();
scope.ServiceProvider.GetRequiredService<CommanderContext>().Database.Migrate(); //Here I get the exception.
}
Since the application is in development, I want the migrations to be automatically applied to the database.
But when I try and get instance of my context class in InitializeDatabase() method, I get the above exception.
Now I have a little understanding that instead of getting a single instance my context class I am getting back a pool of context class.
My Question is: How to I automatically apply the migrations to my database.
Any help is appreciated.
Thanks
AddPooledDbContextFactory method registers IDbContextFactory<TContext> instead the DbContext itself, that's why you can't use GetRequiredService<TContext>. Instead, you should retrieve the factory, use it to obtain TContext instance, do your work and then Dispose it (which in fact will return it to the pool). You can't rely on DI to do that for you because it is not resolved (thus not maintained) by the DI system, but from another abstraction (called factory pattern).
Something like this:
using var context = scope.ServiceProvider
.GetRequiredService<IDbContextFactory<CommanderContext>>()
.CreateDbContext();
context.Database.Migrate();

Call SignalR Hub From Another .Net Project

I've got SignalR hubs in one .NetCore project and SignalR clients in another .NetCore project (to comply with internal infrastructure guidelines). I'm struggling with how to implement the client code that will provide the connection to the hubs. All my projects build fine, I'm just missing the connectivity part.
Client Project:
public class MyClientController
{
private readonly IHubContext<MyHub, IMyHubClient> _hub;
public MyClientController(IHubContext<MyHub, IMyHubClient> hub)
{
_hub = hub;
// THIS NEVER GETS CALLED/HIT BECAUSE I DON'T KNOW HOW TO REGISTER IT
_hub.Clients.All.BroadcastMessage("Notify", $"Hello everyone. This has been constructed");
}
}
I'm guessing I need to do some configuration in the Startup.Configure() method? I've installed the client package already,
EDIT: I added the following code, but it's complaining about the format (remember, this is not a relative path, it's in another service).
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<MyHub>("http://localhost:60913/myHub");
}
Am I on the right path here?
The connection is made by mapping a route to your hub class. The docs have a good example. This includes:
// Add to services collection
services.AddSignalR();
// Map the route "/chathub" to a ChatHub class
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapHub<ChatHub>("/chathub");
});
By the way, a hub can be a standalone class not associated to a controller.

Can I eliminate this linkage in UseExceptionHandler DI

I inject a custom error logging and reporting service into my services collection during Startup.ConfigureServices.
services.AddTransient<IErrorService, ErrorService>();
I then add a global error trap in Startup.Configure.
app.UseExceptionHandler("/Home/Error");
Then in Home.Error, I have my DI for ErrorService, and I pass it my context, and all works perfectly.
Is there some way to eliminate the linkage to Home.Error, and just invoke my ErrorService right in Configure? But I can't figure out how to get linkage to ErrorService at this point in Startup.
Note, two of the other services I inject are my EmailService and my ProgramSettings. I need both of these in ErrorService, so I do need ErrorService invoked in such a way that standard .NET Core DI is wired up.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseExceptionHandler(a => {
a.Run(ctx => {
var exceptionFeature = ctx.Features.Get<IExceptionHandlerPathFeature>();
... execute ErrorService here somehow????????
ctx.Response.StatusCode = StatusCodes.Status500InternalServerError;
return Task.CompletedTask;
});
});

Push data server to client web app, how to?

(Regarding Web apps, asp.net 5)
I'm new to this stuff, i want to push data from the server to the client. Displaying 'real-time' data on a website. (something like a chat window).
How should i realize this? I've read about SingalR, but most information seems to be outdated? Is this still a good choice?
For example, one of the problems i run into is when i try to add this:
app.MapSignalR();
to my startup.cs
Error CS1061 'IApplicationBuilder' does not contain a definition for 'MapSignalR' and no extension method 'MapSignalR' accepting a first argument of type 'IApplicationBuilder' could be found
Can someone point me in the right direction?
I believe you can do this with a bridge between IAppBuilder and IApplicationBuilder as shown here:
public static IApplicationBuilder UseAppBuilder(this IApplicationBuilder app, Action<IAppBuilder> configure)
{
app.UseOwin(addToPipeline =>
{
addToPipeline(next =>
{
var appBuilder = new AppBuilder();
appBuilder.Properties["builder.DefaultApp"] = next;
configure(appBuilder);
return appBuilder.Build<AppFunc>();
});
});
return app;
}
Which you would then call from startup to start SignalR:
public void Configure(IApplicationBuilder app)
{
app.UseAppBuilder(appBuilder =>
{
appBuilder.MapSignalR();
appBuilder.Run(context =>
{
});
});
}
Some context, excerpted from the link above:
[In ASP.NET 5, now ASP.NET Core 1.0] the Startup class conventions
have been refined. Katana [the previous version] would search for an
assembly that specified the OwinStartup attribute and then fallback
to searching all assemblies for a class named Startup or
AssemblyName.Startup. ASP.NET does not define an attribute and it only
searches the primary application assembly for a type named Startup (in
any namespace). The Configuration(IAppBuilder app) method is now
Configure(IApplicationBuilder app)...
Middleware that can’t remove their IAppBuilder or OwinMiddleware
dependencies can use a modified approach to run in ASP.NET 5. See this
linked sample for an IApplicationBuilder extension that provides an
IAppBuilder for use with Katana v3 based middleware. This extension
creates a new AppBuilder instance and then wraps the OWIN pipeline
builder pattern around it to integrate into ASP.NET 5.

Global exception handling in ASP.NET 5

How can I attach my own logging logic to an ASP.NET 5 application to handle each exception thrown in the business logic and lower layers?
I tried with own ILoggerProvider implementation and loggerfactory.AddProvider(new LoggerProvider(Configuration)) in Startup.cs. But it seems that it intercepts inner ASP.NET stuff, and not my thrown exceptions in lower layers.
Worked it out, by using two options:
1) ILoggerProvider
Implement your own ILoggerProvider and ILogger from the namespace Microsoft.Framework.Logging Then attach it to the MVC Framework in Startup.cs add following code:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerfactory)
{
loggerfactory.AddProvider(new YourCustomProvider());
}
But this above option, seems to only call the Write function of the ILogger on MVC specific events, routing related and so on, it wasn't called when I threw exceptions on my lower layers, so the second option worked out:
2) Global Filter
Register your own ActionFilterAttribute implementation in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().Configure<MvcOptions>(options =>
{
options.Filters.Add(new YourCustomFilter());
});
}
It's important, that the custom filter class implements the IExceptionFilter interace:
public class YourCustomFilter : ActionFilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext context)
{
///logic...
}
}
(EDIT:)
And then in the Startup class we add the filter:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.Filters.Add(new YourCustomFilter());
});
}
If you want a really global exception trap framework, not just at the controller level, take a look at one of my open source projects. I plan to make it into a Nuget Package soon after the holidays. I'm also planning to update my blog showing why I developed it.
The open source project is on github:
https://github.com/aspnet-plus/AspNet.Plus.Infrastructure
Take a look at the sample for usage.

Resources