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've just recently switched over from ASP.NET MVC to using .Core 2 and I can't figure out how to get the current URL in Core. I could get it easily enough using the Request in previous asp.net versions, but since that's no long valid in .Net Core I'm at a loss.
I haven't been able to find any way from my google searching as of now.
Any help is appreciated. Thanks!
In the ConfigureServices method of your Startup.cs file, add the line:
services.AddHttpContextAccessor();
and you will now have access to the IHttpContextAccessor interface throughout your code when using dependency injection.
Usage as follows:
public class CustomerRepository
{
private readonly IHttpContextAccessor _context;
public CustomerRepository(IHttpContextAccessor context)
{
_context = context;
}
public string BaseUrl()
{
var request = _context.HttpContext.Request;
// Now that you have the request you can select what you need from it.
return string.Empty;
}
}
Hope this answers your question :)
Try:
public class Startup {
public Startup(IConfiguration configuration, IHostingEnvironment env) {
Configuration = configuration;
HostingEnvironment = env;
var url = configuration[WebHostDefaults.ServerUrlsKey];
}
Please note that you can get more than one url.
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 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
Is it possible to "disable" authentication in ASP.NET Core application without changing its logic?
I have a .net website which uses an external identity server app for authentication.
Anyway I would like to be able to mock the authentication when I'm developing it (ASPNETCORE_ENVIRONMENT = Development), airing access to all actions ignoring the authorization attributes.
Is it possible to do it just mocking some services in the service collection?
On updating to net core 3.1, the mvc AllowAnonymousFilter was not working for us any more. We found conditionally adding a custom IAuthorizationHander to be the simplest way forward to conditionally bypass auth.
eg.
/// <summary>
/// This authorisation handler will bypass all requirements
/// </summary>
public class AllowAnonymous : IAuthorizationHandler
{
public Task HandleAsync(AuthorizationHandlerContext context)
{
foreach (IAuthorizationRequirement requirement in context.PendingRequirements.ToList())
context.Succeed(requirement); //Simply pass all requirements
return Task.CompletedTask;
}
}
Then register this handler conditionally in Startup.ConfigureServices.
private readonly IWebHostEnvironment _env;
public Startup(IWebHostEnvironment env)
{
_env = env;
}
public void ConfigureServices(IServiceCollection services)
{
{...}
//Allows auth to be bypassed
if (_env.IsDevelopment())
services.AddSingleton<IAuthorizationHandler, AllowAnonymous>();
}
Note AddAuthentication and AddAuthorization services are still registered and configured as per prod code (which is nice).
To allow our unit test to bypass auth, we added a new anonymous testbase with a startup class that added this line without any conditions. Nice and simple!
You can bypass authorization in development environment by applying AllowAnonymousAttribute to your endpoints.
Example 1 dotnet new webapi template, .NET 6 (ASP.NET Core 6) and newer
Use AllowAnonymous method in Program.cs to apply AllowAnonymousAttribute to all controllers:
if (app.Environment.IsDevelopment())
app.MapControllers().AllowAnonymous();
else
app.MapControllers();
Example 2 dotnet new webapi template, .NET Core 3.0 - .NET 5 (ASP.NET Core 3.0-5)
Use WithMetadata method in Startup.Configure() to apply AllowAnonymousAttribute to all controllers:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//...
app.UseEndpoints(endpoints =>
{
if (env.IsDevelopment())
endpoints.MapControllers().WithMetadata(new AllowAnonymousAttribute());
else
endpoints.MapControllers();
});
}
Example 3 dotnet new webapi -minimal template, .NET 6 (ASP.NET Core 6) and newer
Use AllowAnonymous method to apply AllowAnonymousAttribute to a minimal API endpoint:
var hiEndpoint = app
.MapGet("/hi", () => "Hello!")
.RequireAuthorization();
if (app.Environment.IsDevelopment())
hiEndpoint.AllowAnonymous();
Details
endpoints and app from the examples above, both implement IEndpointRouteBuilder which has multiple Map extension methods like MapControllers() and MapGet(...) that return IEndpointConventionBuilder.
WithMetadata (available since .NET Core 3.0) and AllowAnonymous (available since .NET 5) are extensions for IEndpointConventionBuilder and can be called upon the results of those Map methods.
AllowAnonymousAttribute's description from the docs:
Specifies that the class or method that this attribute is applied to does not require authorization.
Another solution you may want to consider is using the IPolicyEvaluator. This means that you can keep all the existing security elements.
public class DisableAuthenticationPolicyEvaluator : IPolicyEvaluator
{
public async Task<AuthenticateResult> AuthenticateAsync(AuthorizationPolicy policy, HttpContext context)
{
// Always pass authentication.
var authenticationTicket = new AuthenticationTicket(new ClaimsPrincipal(), new AuthenticationProperties(), JwtBearerDefaults.AuthenticationScheme);
return await Task.FromResult(AuthenticateResult.Success(authenticationTicket));
}
public async Task<PolicyAuthorizationResult> AuthorizeAsync(AuthorizationPolicy policy, AuthenticateResult authenticationResult, HttpContext context, object resource)
{
// Always pass authorization
return await Task.FromResult(PolicyAuthorizationResult.Success());
}
}
In the Startup.cs, ensure this appears at the top of the ConfigureServices method. Eg.
public void ConfigureServices(IServiceCollection services)
{
if (env.IsDevelopment())
{
// Disable authentication and authorization.
services.TryAddSingleton<IPolicyEvaluator, DisableAuthenticationPolicyEvaluator>();
}
...
Rather than Startup.cs (and thanks to the comments below) if you are using Core 3.1 and you wish to use the WebApplicationFactory, you can do the following:
public class MyWebApplicationFactory : WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services =>
{
// Disable Authentication.
services.RemoveAll<IPolicyEvaluator>();
services.AddSingleton<IPolicyEvaluator, DisableAuthenticationPolicyEvaluator>();
});
}
}
I've found sollution for this problem on illucIT Blog.
This code must work:
if (env.IsDevelopment()) {
services.AddMvc(opts =>
{
opts.Filters.Add(new AllowAnonymousFilter());
});
} else {
services.AddMvc();
}
It's tricky to give a detailed answer without more details on your end, but I have previously achieved this by conditionally registering:
the external authentication middleware
the global policy that requires an authenticated request
it looked something like:
public class Startup
{
public Startup(IHostingEnvironment env)
{
Environment = env;
}
public IHostingEnvironment Environment { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(x =>
{
if (!Environment.IsDevelopment())
{
var authenticatedUserPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
x.Filters.Add(new AuthorizeFilter(authenticatedUserPolicy));
}
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseStaticFiles();
if (!Environment.IsDevelopment())
{
// Register external authentication middleware
}
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
In my case, the authorization filter was applied globally, so every single action of the MVC app required an authenticated user.
If you have different requirements - fine-grained [Authorize] attributes on some actions - then you could probably achieve the same result by changing how the associated authorization policies are built. They could basically contain no requirements at all.
AuthorizationPolicy yourCustomPolicy = null;
if (Environment.IsDevelopment())
{
yourCustomPolicy = new AuthorizationPolicyBuilder().Build();
}
else
{
yourCustomPolicy = new AuthorizationPolicyBuilder()
// chaining appropriate methods to suit your needs
.Build();
}
In ASP.NET Core 6, we managed to disable the authorization without changing any other part from the productive code, just the following logic in Program.cs:
if (!builder.Environment.IsDevelopment())
{
app.MapControllers();
}
else
{
app.MapControllers().AllowAnonymous();
}
This is to clarify #Kirill Lutsenko's answer about the method he found on the IllucIT blog post (note that in my case this is for .NET Core 2.0. I see other answers saying the AllowAnonymousFilter method won't work in .NET Core 3.1):
The Startup class has an overloaded constructor. One of the overloads takes an IHostingEnvironment parameter. You need to use this version of the constructor.
In the Startup class create a property of type IHostingEnvironment. Call it, say, Environment. Then set that property in the constructor.
Then, in the ConfigureServices method, you can use Environment.IsDevelopment().
public class Startup
{
public Startup(IHostingEnvironment environment)
{
Environment = environment;
}
public IHostingEnvironment Environment { get; }
public IServiceProvider ConfigureServices(IServiceCollection services)
{
//...
services.AddMvc(options =>
{
// This uses the Environment property populated in the constructor.
if (Environment.IsDevelopment())
{
options.Filters.Add(new AllowAnonymousFilter());
}
// Set other options here. For example:
options.ModelBinderProviders.Insert(0, new UTCDateTimeModelBinderProvider());
//...
});
//...
}
}
As a side note, in real life we use a different overload of the constructor, which takes both an IConfiguration object and an IHostingEnvironment object as parameters. That allows us to configure services based on an appsettings.json configuration file.
For example:
public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment environment)
{
Configuration = configuration;
Environment = environment;
}
public IConfiguration Configuration { get; }
public IHostingEnvironment Environment { get; }
public IServiceProvider ConfigureServices(IServiceCollection services)
{
//...
// Data access via Entity Framework
services.AddDbContext<ContainersDbContext>(options =>
{
options.UseNpgsql(Configuration.GetConnectionString("OrdersDatabase"));
});
//...
}
}