Ignoring Request in middleware - asp.net-core

So I want IIS to basically not do anything when certain urls are requested bacause I want react router to which I have rendered from serverside, to handle the request.
Used this link
I have created a middleware that checks each request. Now i dont know how to ignore or abort this request once I find the right urls.
public class IgnoreRouteMiddleware
{
private readonly RequestDelegate next;
// You can inject a dependency here that gives you access
// to your ignored route configuration.
public IgnoreRouteMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.Request.Path.HasValue &&
context.Request.Path.Value!="/")
{
// cant stop anything here. Want to abort to ignore this request
}
await next.Invoke(context);
}
}

If you want to stop a request, just don't call next.Invoke(context), because this will call the next middleware in the pipeline. Not calling it, just ends the request (and the previous middlewares code after it's next.Invoke(context) will be processed).
In your case, just move the call to the else branch or just negate the if expression
public class IgnoreRouteMiddleware
{
private readonly RequestDelegate next;
// You can inject a dependency here that gives you access
// to your ignored route configuration.
public IgnoreRouteMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
if (!(context.Request.Path.HasValue && context.Request.Path.Value!="/"))
{
await next.Invoke(context);
}
}
}
Also make sure to read the ASP.NET Core Middleware documentation for a better understanding on how middlewares work.
Middleware is software that is assembled into an application pipeline to handle requests and responses. Each component:
Chooses whether to pass the request to the next component in the pipeline.
Can perform work before and after the next component in the pipeline is invoked.
But if you want server-sided rendering, consider using Microsoft`s JavaScript/SpaServices library, which is already built in in the newer templates (ASP.NET Core 2.0.x) and register a fallback route such as.
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
The new templates also come with support for hot module replacement

Related

Register or remove middleware without deploying code

I have created a middleware which logs requests/response data in the database.
I want this middleware to work only when I want to troubleshoot defect or unwanted exception. The middleware should not log rest of the time.
I want a switch button which I can on or off on any controller without making any code changes and deployment.
Please suggests the ways to achieve the above.
In Program.cs, you can add conditionally a middleware like :
var builder = WebApplication.CreateBuilder(args);
...
var app = builder.Build();
if (app.Configuration.Get<bool>("MiddlewareLog.Enable"))
{
app.UseCustomeLoggerMiddleware();
}
...
To enable/disable the middleware, you only need to update the appsettings.json and restart the web api/app.
A solution is to enable/disable the middleware from a global setting. Then the controller's action can modify this global setting to enable/disable the middleware.
public class LoggerMiddleware
{
public static volatile bool Enable;
private readonly RequestDelegate _next;
public LoggerMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
if(Enable)
{
// Log
}
await _next(context);
}
}
[Route("logger")]
public class LoggerController : ControllerBase
{
[HttpGet]
public void EnableOrDisable(bool enable)
{
LoggerMiddleware.Enable = enable;
}
}
In the example, I use a static field, but it's possible to inject a singleton service in the middleware and the controller to share the setting.

Modify response using middleware in ASP.NET Core 3

My goal is to write a middleware that will take care of logging requests to my API and API's responses to those requests in a DB.
I already made a middleware that handles exceptions in a similar fashion, but I got stumped over this.
When you read MSDN about Middleware you can see this nice picture:
This makes you think that Middleware 2 receives the requests, does certain manipulations with it and passes it onto Middleware 3, then once all processing is done by middleware 3 it passes controls back to Middleware 2 for additional processing.
The only thing I do not understand is how to log the response if Middleware 2 Invoke() method is only called once during the request and not called during the response?
Startup.cs:
app.UseMiddleware<RequestLoggingMiddleware>();
Middleware:
public class RequestLoggingMiddleware
{
private readonly RequestDelegate nextMiddleware;
public RequestLoggingMiddleware(RequestDelegate nextMiddleware)
{
this.nextMiddleware = nextMiddleware;
this.options = options;
}
public async Task Invoke(HttpContext context)
{
System.Diagnostics.Debug.WriteLine("Middleware runs");
await nextMiddleware(context);
}
}
}
In the example above I only see "Middleware runs" once in a console, during the initial request but before the response is made. How do I get it to run during the response cycle?
To get the response, all you need to do is apply your same logic after the await nextMiddleware(context); line.
For example, to log the status code:
public class RequestLoggingMiddleware
{
private readonly RequestDelegate nextMiddleware;
public RequestLoggingMiddleware(RequestDelegate nextMiddleware)
{
this.nextMiddleware = nextMiddleware;
}
public async Task Invoke(HttpContext context)
{
System.Diagnostics.Debug.WriteLine("Middleware runs");
await nextMiddleware(context);
System.Diagnostics.Debug.WriteLine($"Response Code: {context.Response.StatusCode}");
}
}

asp .net core redirect inside midleware

I want to create a page allowing to create the database and initialize the default data of an application for each new deployment. for that I create a middleware which checks if it is possible to connect to the database. in the event that it is not possible to connect to the database (which means that the database does not yet exist), the middleware redirects to an action of the application responsible for collecting the default data and proceed with the creation of the database.
However, in the middleware the redirect does not work despite all attempts except for a redirect to an external site such as google.com.
I use asp.net core 2.2 and entityframework core
MissingDatabaseMiddleware.cs
public class MissingDatabaseMiddleware
{
private readonly RequestDelegate _next;
private readonly string _missingTenantUrl;
public MissingDatabaseMiddleware(RequestDelegate next, string missingTenantUrl)
{
_next = next;
_missingTenantUrl = missingTenantUrl;
}
public async Task Invoke(HttpContext httpContext, MydbContext context)
{
if (!context.Database.CanConnect())
{
httpContext.Response.Redirect(_missingTenantUrl, false);
return;
}
await _next.Invoke(httpContext);
}
}
startup.cs
public void Configure(IApplicationBuilder app, Microsoft.AspNetCore.Hosting.IHostingEnvironment env, MydbContext, ILoggerFactory loggerFactory){
app.UseMiddleware<MissingDatabaseMiddleware>("/Setup/AppSetup");
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
name: "areas",
template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
});
}
the solution is to add a check in the request url
public async Task Invoke(HttpContext httpContext, MemoryDbContext context)
{
if (!context.Database.CanConnect() && !httpContext.Request.GetEncodedUrl().Contains(_missingTenantUrl))
{
httpContext.Response.Redirect(_missingTenantUrl, false);
return;
}
await _next.Invoke(httpContext);
}

How to intercept response in ASP.NET 5 after all other middlewares?

In my application I need to add a header to almost all responses.
However, middleware won't solve this for me because some other middleware sets a completely fresh response, ends the pipeline and I don't get a look in:
app.Use((context, next) =>
{
context.Response.Headers.Add("MyHeader", "IsCool");
return next();
});
app.UseSomeOtherMiddleware(); // This ends the pipeline after removing my `MyHeader`
I can't add another middleware after the offending one, because the pipeline is finished.
I could add a web.config entry for it:
But as I said, this needs to be added to almost all responses. I need just a teeny bit of logic to determine if I add it, and the web.config solution doesn't afford me that.
So how can I do this in ASP.NET 5? How can I tap into the pipeline after everything is supposedly finished?
Correct implementation for RC2
public class CustomMiddleware
{
private readonly RequestDelegate _next;
public CustomMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
var sw = new Stopwatch();
sw.Start();
context.Response.OnStarting((state) =>
{
sw.Stop();
context.Response.Headers.Add("x-elapsed-time", sw.ElapsedTicks.ToString());
return Task.FromResult(0);
}, null);
await _next.Invoke(context);
}
}
You can register a callback with HttpContext.Response.OnStarting and modify the headers just before they are sent.
I think I solved this by creating a middleware as follows:
public class MyMiddleware
{
RequestDelegate _next;
public MyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
await _next(context);
context.Response.Headers.Add("MyHeader", "IsCool");
}
}
And using the following in Startup.cs:
app.UseMiddleware<MyMiddleware>();

What is an analog of WebActivator in ASP.NET 5

Previously (in asp.net 4.x) there was common practice to use WebActivator class for register bootstrap logic.
I understand that now we have Startup class where everything can be configured. But with WebActivator I had more options - it was possible to drop-in an assembly into an app (add a nuget) and the assembly registered everything it needs on its own. For this an assemble had assembly level attribute with a type which should be called:
[assembly: WebActivator.PreApplicationStartMethod(typeof (ModuleBootstrapper), "Start")]
What is recommended approach for such things ("lib initialization") in the new glory asp.net 5 now?
The functionality you can get with WebActivator is not possible under ASP.NET 5 and I strongly believe that it won't ever be because one of the great things about ASP.NET 5 pipeline is that you are responsible building up your request pipeline. So, the decision should be deliberately made. As an example:
I have a middleware:
public class MonitoringMiddlware
{
private RequestDelegate _next;
public MonitoringMiddlware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
// do some stuff on the way in
await _next(httpContext);
// do some stuff on the way out
}
}
I can package this up and publish to a NuGet feed. Consumer needs to pull this in and add this into the appropriate place inside the pipeline:
public class Startup
{
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// configure services here
}
public void Configure(IApplicationBuilder app)
{
app.UseStatusCodePages();
app.UseFileServer();
// I want middleware to sit here inside the pipeline.
app.UseMiddleware<MonitoringMiddlware>();
app.UseMvc(routes =>
{
routes.MapRoute("areaRoute", "{area:exists}/{controller}/{action}");
routes.MapRoute(
"controllerRoute",
"{controller}",
new { controller = "Home" });
});
}
}
So, whenever I come into this code, I can see how the pipeline is being built without any magic. In the WebActivator case, you would need to look into a few other places to figure out your pipeline and most of all, you wouldn't be making the decision where it sits.
So, it was not a bad thing to get rid of it.