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...
}
Related
I am trying to add a custom middleware to the pipeline (to be easier I will pick the .NET Core documentation example).
Let's say we want to have the Spanish culture set whenever a call to API is fired.
Here's the code which runs perfectly:
public class RequestCultureMiddleware
{
private readonly RequestDelegate _next;
public RequestCultureMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
CultureInfo.CurrentCulture = new CultureInfo("es-ES");
CultureInfo.CurrentUICulture = new CultureInfo("es-ES");
// Call the next delegate/middleware in the pipeline
await _next(context);
}
}
public static class RequestCultureMiddlewareExtensions
{
public static IApplicationBuilder UseRequestCulture(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestCultureMiddleware>();
}
}
and the Startup class:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
//here is our custom middleware!
app.UseRequestCulture();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
That's fine, but as you can see, the RequestCultureMiddleware does not implement an interface or a base class/abstract class. You just need to remember when defining a middleware to create a constructor that receives the next middleware and also you need to create a method called specifically "InvokeAsync" with "HttpContext" as a parameter.
I tried to find a contract... a base class or an interface and guess what, we have "IMiddleware" which is part of "Microsoft.AspNetCore.Http" assembly. Wow, that's perfect. Let's implement it.
The interface looks like this:
namespace Microsoft.AspNetCore.Http
{
//
// Summary:
// Defines middleware that can be added to the application's request pipeline.
public interface IMiddleware
{
//
// Summary:
// Request handling method.
//
// Parameters:
// context:
// The Microsoft.AspNetCore.Http.HttpContext for the current request.
//
// next:
// The delegate representing the remaining middleware in the request pipeline.
//
// Returns:
// A System.Threading.Tasks.Task that represents the execution of this middleware.
Task InvokeAsync(HttpContext context, RequestDelegate next);
}
}
And here is the implementation:
public class RequestCultureMiddleware : IMiddleware
{
public Task InvokeAsync(HttpContext context, RequestDelegate next)
{
CultureInfo.CurrentCulture = new CultureInfo("es-ES");
CultureInfo.CurrentUICulture = new CultureInfo("es-ES");
// Call the next delegate/middleware in the pipeline
return next(context);
}
}
public static class RequestCultureMiddlewareExtensions
{
public static IApplicationBuilder UseRequestCulture(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestCultureMiddleware>();
}
}
}
But, when running the API I am getting the following error at run-time:
System.InvalidOperationException: No service for type 'WebApplication1.RequestCultureMiddleware' has been registered.
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.AspNetCore.Http.MiddlewareFactory.Create(Type middlewareType)
at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass5_1.<<UseMiddlewareInterface>b__1>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
How exactly I am supposed to register this middleware if not by using the extension "UseMiddleware"?
Thanks.
I'm sure this problem has been solved long ago after 5 months, but I'm writing this advice just in case.
The problem is the "InvokeAsync" method of your custom middleware program is not be executed even though you built in it in "Configure" method of Startup.
I had the same problem the other day and solved it by, but I putting built in code right before the app.UseEndpoints method.
in your case
app.UseAuthorization();
app.UseRequestCulture(); // <- this way.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
By the way, if you put it after the app.UseEndpoints method, the constructor will be called, but the InvokeAsync method will not be executed.
You're using factory-based middleware. As described in those docs, you've missed an important step:
... the IMiddlewareFactory instance registered in the container is used to resolve the IMiddleware implementation instead of using the convention-based middleware activation logic. The middleware is registered as a scoped or transient service in the app's service container.
In your case, that registration would look something like this:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddTransient<RequestCultureMiddleware>();
}
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'm implementing some middleWare which I only want to run on a specific route
/api
There are some other middleware's which always should run, some before and some after the api specific middleware. My code in Startup.cs :
app.UseStaticFiles();
app.UseIdentity();
//some more middleware is added here
//my own middleware which should only run for routes at /api
app.UseCookieAuthMiddleware();
//some more middleware is added here
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
app.UseKendo(env);
The order of loading the middleware is important so I want to keep this order.
app.UseCookieAuthMiddleware(); should only run on urls which start with /api e.g. "/api, /api/test, /api?test".
I saw app.Map was an option but how does one make sure all the other middleware's after app.Map are added to.
edit: example on when map skips the lines below:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseIdentity();
//some more middleware is added here
app.Map("/api", HandleApiRoutes);
//lines below are skipped ONLY when map is used.
//some more middleware should be added here
app.UseKendo(env);
}
private static void HandleApiRoutes(IApplicationBuilder app)
{
app.UseCookieAuthMiddleware();
}
My middleware:
public class CookieCheckMiddleWare
{
private readonly RequestDelegate _next;
public CookieCheckMiddleWare(RequestDelegate next)
{
_next = next;
}
public Task Invoke(HttpContext context)
{
Debug.WriteLine("Middleware is called" );
//Call the next delegate/middleware in the pipeline this._next(context) is called no errors.
return this._next(context);
}
}
public static class CookieCheckMiddleWareMiddlewareExtensions
{
public static IApplicationBuilder UseCookieAuthMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CookieCheckMiddleWare>();
}
}
I can simply solve the problem by always running the middleware and do a check in the middleware.invore to check if the reques url is like "/api":
public Task Invoke(HttpContext context)
{
var url = context.Request.Path.Value.ToString();
var split = url.Split('/');
if (split[1] == "api") //should do a extra check to allow /api?something
{
//middleware functions
}
}
But I like to know how to propperly use the map function.
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
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);
}
}