I have an ASP.NET Core app and a simple OWIN middleware to check some data. But I'd like to only run the middleware when a page is requested. Right now its running when assets are requested as well such as images, css, etc.
How can I make the owin middleware code only execute on page requests?
Registration:
app.UseSiteThemer();
Site Themer extension class:
public static class SiteThemerExtensions
{
public static IApplicationBuilder UseSiteThemer(this IApplicationBuilder builder)
{
return builder.UseMiddleware<SiteThemerMiddleware>();
}
}
OWIN Middleware:
public class SiteThemerMiddleware
{
private readonly RequestDelegate _next;
private readonly ISiteService _siteService;
public SiteThemerMiddleware(RequestDelegate next, ISiteService siteService)
{
_siteService = siteService;
_next = next;
//_logger = loggerFactory.CreateLogger<SiteThemerMiddleware>();
}
public async Task Invoke(HttpContext context)
{
await Task.Run(() =>
{
Console.Write("OWIN Hit");
});
//_logger.LogInformation("Handling request: " + context.Request.Path);
await _next.Invoke(context);
//_logger.LogInformation("Finished handling request.");
}
}
There are two aspects of ASP.NET Core pipeline you can use for you goal here: ordering and branching.
The rules around ordering are very simple - the order in which middlewares are added is the order in which they are going to be executed. This means that if middleware like yours is placed after some middleware which can end the pipeline (for example static files) it will not be invoked if it happens.
In order to branch the pipeline you can use Map or MapWhen methods. The first branches the pipeline based on path while the other based on predicate. Middlewares added with Map or MapWhen will be invoked only if the branch condition is met.
You can read more details about the pipeline here
Related
From the documentation of Prometheus, I implemented a middleware in order to create metrics. Prometheus out puts text file of these metrics by default in /metrics end point ... it works perfectly fine but the problem is that that middleware get called for each and every page hit which make app super slow...
how can I make that middleware to be called only when user request for /metrics ?
Im sorry if question is not that clear because this is my first experience with Prometheus on asp.net core app
I used Prometheus-net.AspNetCore library
MetricsMiddleware.cs
public class MetricsMiddleware
{
private readonly RequestDelegate _next;
public MetricsMiddleware(RequestDelegate next)
{
this._next = next;
}
public async Task Invoke(HttpContext httpContext)
{
await _next.Invoke(httpContext);
//custome metrics created here
}
public static class MetricsMiddlewareExtensions
{
public static IApplicationBuilder UseRequestMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestMiddleware>();
}
}
}
stratup.cs file:
public void Configure(IApplicationBuilder app, ....)
{
app.UseMetricServer();
app.UseMetricsMiddleware();
}
You can use Map function to apply a middleware to specifc route. Like code below.
public void Configure(IApplicationBuilder app)
{
app.Map("/metrics", innerApp =>
{
innerApp.UseMetricsMiddleware());
innerApp.UseMetricServer();
}
}
I have my own simple framework for routing/controllers in C# and .NET Core. I'm using EF Core for the ORM. In Startup.cs I'm configuring it like so:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<Context>(options =>
{
options.EnableSensitiveDataLogging();
options.UseSqlServer(System.Environment.GetEnvironmentVariable("SQL_SERVER_CONNECTION"));
});
}
I'm using dependency injection to get an instance of my DbContext. In my controller actions I do the following:
Action 1:
Do not use AsNoTracking() on my queries
Make changes to a model instance
Do not save changes
Action 2 (another HTTP request):
Do literally anything
Run SaveChangesAsync()on DbContext
The changes made in Action 1 are then persisted. If I severed any relations in Action 1 then I get an error.
I know that by default DbContext is scoped. Do I have to implement some of my own scoping code to ensure that I get a new instance with each HTTP request?
NOTE: I am NOT using MVC, I am using my own little library that I'm developing. I just learned that MVC probably uses IServiceScopeFactory to generate scopes. I am not sure how to use it in middleware though.
I got it. Here's how to wrap a scope with HttpContext in a middleware:
public class Middleware
{
private readonly RequestDelegate _next;
private IServiceScopeFactory _scopeFactory;
public Middleware(RequestDelegate next, IServiceScopeFactory scopeFactory)
{
_next = next;
_scopeFactory = scopeFactory;
}
public async Task Invoke(HttpContext context)
{
using (var scope = _scopeFactory.CreateScope())
{
context.RequestServices = scope.ServiceProvider;
// Do whatever you want here
if (_next != null)
await _next(context);
}
}
}
We need to do some work inside a middleware according to a parameter inside an appsettings.json. There parameter can change on runtime.
For that I can set reloadOnChange at settings file registration
builder.AddJsonFile("appsettings.json",
optional: false, reloadOnChange: true)
This work in the case I use IOptionsSnapshopt inside a controller, because a controller is created per request. But a middleware is perlifetime.
I found Asp.net core 2.0 middleware - accessing config settings where is written how to access parameter from appsettings. --> But this works not if the parameter changes on runtime.
As per the documentation ASP.NET Core Middleware: Per-request dependencies
Because middleware is constructed at app startup, not per-request, scoped lifetime services used by middleware constructors are not shared with other dependency-injected types during each request. If you must share a scoped service between your middleware and other types, add these services to the Invoke method's signature. The Invoke method can accept additional parameters that are populated by dependency injection.
For example, instead of in the constructor, add IOptionsSnapshot parameter to Invoke method.
public static class HelloWorldMiddlewareExtensions
{
public static IApplicationBuilder UseHelloWorld(this IApplicationBuilder builder)
{
return builder.UseMiddleware<HelloWorldMiddleware>();
}
}
public class HelloWorldMiddleware
{
private readonly RequestDelegate _next;
public HelloWorldMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context, IOptionsSnapshopt<AppSettings> options)
{
await context.Response.WriteAsync($"PropA: {options.Value.PropA}");
}
}
public class AppSettings
{
public string PropA { get; set; }
}
I would like to get the initial timestamp of the current HTTP request in an ASP.NET Core MVC controller.
This timestamp used to be accessible (pre ASP.NET Core) by HttpContext.Timestamp, but Timestamp doesn't seem to be a property of HttpContext anymore.
Where is this property moved to? Or - when it is no longer available - how can I get the timestamp of the HTTP request?
You can add your own middleware to the pipeline which adds additional data to the request. For example:
public void Configure(IApplicationBuilder app)
{
//Make sure this code is placed at the very start to ensure it
//executes as soon as possible
app.Use(async (context, next) =>
{
context.Items.Add("RequestStartedOn", DateTime.UtcNow);
await next();
};
//The rest of your code here...
}
Then later on in the pipeline:
var requestStartedOn = (DateTime)httpContext.Items["RequestStartedOn"];
As an aside, if you intend to reuse this code elsewhere, I would put it in it's own library. For example:
public class RequestTimestampMiddleware
{
private readonly RequestDelegate _next;
public RequestTimestampMiddleware(RequestDelegate next)
{
_next = next;
}
public Task Invoke(HttpContext context)
{
context.Items.Add("RequestStartedOn", DateTime.UtcNow);
// Call the next delegate/middleware in the pipeline
return this._next(context);
}
}
And then add an extension method to make it easy to use:
public static class RequestTimestampMiddlewareExtensions
{
public static IApplicationBuilder UseRequestTimestamp(this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestTimestampMiddleware>();
}
}
Now your Configure method will look a lot nicer:
public void Configure(IApplicationBuilder app)
{
app.UseRequestTimestamp();
//The rest of your code here...
}
I do development in asp.net. Recently I found out that in asp.net 5 there is no Global.asax file.
One of the thing to put in Global.asax file is URL rewriting.
With Global.asax file is gone. Where I can place URL rewriting code. I mean I do something like this in ASP.NET 4.0
HttpContext.Current.RewritePath(...);
I do not want to use URL rewriting modules. I just want to do it using HttpContext.Current.RewritePath method.
My question is where I can put the above code in ASP.NET 5?
Create and add a new middleware at the beginning of Configure method in your Startup (you want it to execute before any other middlewares). Example here
Implement invoke method as follows to do a url rewrite
public Task Invoke(HttpContext context)
{
// modify url
context.Request.Path = new PathString(context.Request.Path.Value + 'whatever');
// continue
return _next(context);
}
I came across this when I was analyzing aspnet/StaticFiles repo on Github.
As an alternative to explicitly creating a middleware class, IApplicationBuilder.Use can be used too:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//...built-in initialization...
app.Use(requestDelegate =>
{
return new RequestDelegate(context =>
{
// modify url (from mbudnik's answer)
context.Request.Path = new PathString(context.Request.Path.Value + 'whatever');
// continue with the pipeline
return requestDelegate(context);
});
});
}
In this case the middlewares are specified directly as instances of Func<RequestDelegate, RequestDelegate>, instead of custom classes.
You need OWIN middlewear here. Because it is the replacement for HttpModules in vNext.
Write below code in Configure method of Startup.cs file
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<MyMiddleware>();
}
}
and Your custom middlewear may look like:
public class MyMiddleware
{
private readonly RequestDelegate _test;
public MyMiddleware(RequestDelegate test)
{
_test = test;
}
public async Task Invoke(HttpContext context)
{
return _test.Invoke(context);
}
}