Error logging and handling in asp.net core mvc - asp.net-core

Let's say you made a bug in a code (like this for example 415 Unsupported Media Type asp.net core ). And get 415 error from a web request. How do we find more information about the error? How can we log it?
Seems that .UseExceptionHandler() In Startup.cs does not catch it.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
// app.UseExceptionHandler("/Home/Error");
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseStatusCodePages(async context =>
{
context.HttpContext.Response.ContentType = "text/plain";
var statusCodeData = context.HttpContext.Features.Get<IStatusCodePagesFeature>();
await context.HttpContext.Response.WriteAsync(
"Status code page, status code: " +
context.HttpContext.Response.StatusCode);
});
// app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}

Try with Middleware. Create new class for Middleware:
public class ErrorLoggingMiddleware
{
private readonly RequestDelegate _next;
public ErrorLoggingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception exception)
{
// Handle exception
await HandleExceptionAsync(context, exception, HttpStatusCode.InternalServerError);
}
}
private Task HandleExceptionAsync(HttpContext context, Exception exception, HttpStatusCode statusCode)
{
context.Response.StatusCode = (int)statusCode;
// TO DO: Log exceptions : Azure AppInsights or File
var message = "The requested resource was not found";
var response = new
{
ErrorDescription = $"{(int)statusCode} - {message}",
ErrorCode = message
};
string responseStringContent;
context.Response.ContentType = Application.Json;
responseStringContent =
JsonSerializer.Serialize(response, new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
return context.Response.WriteAsync(responseStringContent);
}
}
Then configure this class in Startup:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory fac)
{
...
app.UseMiddleware<ErrorLoggingMiddleware>();
...
}
Please find more details at:
https://blog.elmah.io/error-logging-middleware-in-aspnetcore/

Related

asp.net core web Api integration testing with startup having static file configuration

I was trying to implement integration testing for my api. But, it is saying System.IO.DirectoryNotFoundException : ..WebApi.IntegrationTest\bin\Debug\net6.0\Files\ while I run the test
here is my startup code
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseBlazorFrameworkFiles();
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), #"Files")),
RequestPath = new PathString("/Files")
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors("CorsPolicy");
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.Initialize(Configuration);
}
when I comment out UseStaticFiles it passes the test else it fails with the above. exception
here is my CustomWebApplicationFactory
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseInMemoryDatabase("ApplicationDbContextInMemoryTest");
})
.AddControllers()
.AddApplicationPart(typeof(Startup).Assembly);
var sp = services.BuildServiceProvider();
using (var scope = sp.CreateScope())
{
var scopedService = scope.ServiceProvider;
var context = scopedService.GetRequiredService<ApplicationDbContext>();
var logger = scopedService.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
context.Database.EnsureCreated();
try
{
Utilities.InitializeDbForTests(context);
}
catch (Exception ex)
{
logger.LogError(ex, $"An error occured seeding the database with test messages, Error: {ex.Message}");
}
}
});
}
protected override IHost CreateHost(IHostBuilder builder)
{
builder.UseContentRoot(Directory.GetCurrentDirectory());
return base.CreateHost(builder);
}
public HttpClient GetAnonymousClient()
{
return CreateClient();
}
}
I tried a lot to fix it but I was not able, please help me, thank you!

Rewrite URL in ASP.Net Core Razor Pages (not MVC)

By seo friendly, I am trying to use the rewrite middleware to rewrite an url to a seo friendly url, but I am not successful.
As an example, what I want to do is rewrite the url https://example.com/1 to https://example.com/test-1 and the url https://example.com/1/2 to https://example.com/test-1/test-2.
I leave the Startup and ChangeURL classes that I have made.
Startup.cs:
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.AddRazorPages();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
var options = new RewriteOptions();
options.Rules.Add(new ChangeUrl());
app.UseRewriter(options);
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
}
ChangeURL.cs:
public class ChangeUrl : IRule
{
public void ApplyRule(RewriteContext context)
{
var request = context.HttpContext.Request;
Match m1 = Regex.Match(request.Path.Value, #"^/(\d+)");
Match m2 = Regex.Match(request.Path.Value, #"^/(\d+)/(\d+)");
if (m1.Success)
{
request.Path = "/test-1";
}
else if (m2.Success)
{
request.Path = "/test-1/test-2";
}
context.Result = RuleResult.ContinueRules;
return;
}
}
I appreciate all the help you can give me.
As far as I know, asp.net core contains the url rewrite module which is used to url rewrite it.
More details, you could refer to below codes:
// Add below codes into the Configure method
var options = new RewriteOptions()
.AddRewrite(#"^(\d+)/(\d+)", "test-$1/test-$2",
skipRemainingRules: true)
.AddRewrite(#"^(\d+)", "test-$1",
skipRemainingRules: true);
app.UseRewriter(options);
More details, you could refer to this artcle.
public void ApplyRule(RewriteContext context)
{
var request = context.HttpContext.Request;
Match m1 = Regex.Match(request.Path.Value, #"^/(\d+)");
Match m2 = Regex.Match(request.Path.Value, #"^/(\d+)/(\d+)");
if (m1.Success)
{
request.Path = "/home/Privacy";
}
if (m2.Success)
{
request.Path = "/home/account";
}
context.Result = RuleResult.ContinueRules;
return;
}

Asp Net Core, Logic for custom request headers

I'd like to create an Asp.Net Core MVC that can deny requests that lack specific headers.
For Example:
I want to access a Controller and and only allow Requests whose Header contains a specific (custom made) Authorization Type and Token.
I've done some research on it but I could not find anything on this topic that gave me an idea on how to even start.
You could custom a middleware to check the request header type and value:
public class SimpleHeaderAuthorizationMiddleware
{
private readonly RequestDelegate _next;
public SimpleHeaderAuthorizationMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
string authHeader = context.Request.Headers["Accept"];
if (!string.IsNullOrEmpty(authHeader))
{
if (authHeader == "specific_value")
{
//do your stuff....
//throw new Exception("The HTTP header value is not correct!");
context.Response.StatusCode = 403;
}
await _next(context);
}
else
{
//reject the request if do not provide Authorization header
//throw new Exception("Necessary HTTP header not present!");
context.Response.StatusCode = 401;
}
}
}
Create Middleware extension method:
public static class SimpleHeaderAuthorizationMiddlewareExtension
{
public static IApplicationBuilder UseSimpleHeaderAuthorization(this IApplicationBuilder app)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
return app.UseMiddleware<SimpleHeaderAuthorizationMiddleware>();
}
}
The following code calls the middleware from Startup.Configure:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseSimpleHeaderAuthorization(); //add this...
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}

ASP.Net Core 3.1 Aplication lost session vars after deploy

There is a problem, the application uses the session, when launched on the local machine, the session lasts a long time. But when we deploy the server, session data disappears with inconsistent regularity. Sometimes it may take half an hour, sometimes already at the second transition, the application considers that there is no variable in the session. What could be the problem?
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDistributedMemoryCache();
services.AddSession();
services.AddControllersWithViews().AddJsonOptions(options =>
{
options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);
});
services.Configure<WebEncoderOptions>(options =>
{
options.TextEncoderSettings = new TextEncoderSettings(UnicodeRanges.All);
});
services.AddMemoryCache();
services.AddHttpClient();
}
// 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())
{
System.Diagnostics.Debug.WriteLine("This is development env");
app.UseDeveloperExceptionPage();
// HACK
// Set worker threads as HElios sets them too low for IIS Express
// https://github.com/aspnet/Home/issues/94
int newLimits = 100 * Environment.ProcessorCount; // this is actually # cores (including hyperthreaded cores)
int existingMaxWorkerThreads;
int existingMaxIocpThreads;
System.Threading.ThreadPool.GetMaxThreads(out existingMaxWorkerThreads, out existingMaxIocpThreads);
System.Threading.ThreadPool.SetMaxThreads(Math.Max(newLimits, existingMaxWorkerThreads), Math.Max(newLimits, existingMaxIocpThreads));
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseSession();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Set up session var
[HttpPost]
public async Task<JsonResult> UserCheck(string surname)
{
Boolean status = false;
String error_msg = "Что то пошло не так";
try
{
Guid? UserId = null;
if (String.IsNullOrWhiteSpace(surname))
{
error_msg = "Не указана фамилия";
}
else
{
UserId = await apm.GetUser(surname);
if (UserId != null && UserId != Guid.Empty)
{
HttpContext.Session.SetString("UserId", UserId.ToString());
status = true;
error_msg = "";
}
}
return Json(new { status, error_msg, guid = UserId });
}
catch (Exception e)
{
return Json(new { status, error_msg });
}
}
Get session vars
public IActionResult Test()
{
string strUserId = HttpContext.Session.GetString("UserId");
if (String.IsNullOrWhiteSpace(strUserId))
{
return RedirectToAction("Index", "Home");
}
else
{
return View();
}
}

How to get route data from Identity Server 4 endpoints

I have a ResponseTimeMiddleware.cs responsible for getting response time metrics (I am using datadog) for every request made. Which is tagged by controller and action names. However when we hit the "connect/token" endpoint, the context.GetRouteData() is null, probably because identity server is doing it behind the scenes. Is there a way I could get this information or some other unique information where I could tag with?
here's my code:
public class ResponseTimeMiddleware
{
// other code..
public Task InvokeAsync(HttpContext context)
{
var request = context.Request;
var watch = new System.Diagnostics.Stopwatch();
watch.Start();
context.Response.OnStarting(() =>
{
watch.Stop();
var routeData = context.GetRouteData();
var responseTime = watch.ElapsedMilliseconds.ToString();
var tags = new[] { $"statusCode:{context.Response.StatusCode.ToString()}", $"controller:{routeData.Values["controller"]}", $"action:{routeData.Values["action"]}" };
context.Response.Headers[ResponseHeaderResponseTime] = responseTime;
DogStatsd.Timer("response.time", responseTime, tags: tags);
return Task.CompletedTask;
});
return nextDelegate(context);
}
}
This is my Startup:
public class Startup
{
// other code..
public static void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseMiddleware<ResponseTimeMiddleware>();
app.UseMvcWithDefaultRoute();
app.UseStaticFiles();
app.UseEndpointRouting();
app.UseCookiePolicy();
app.UseCors("CorsPolicy");
app.UseIdentityServer();
// This method gets called by the runtime. Use this method to add services to the container.
public async void ConfigureServices(IServiceCollection services)
{
services.AddDataDogStatsd(Configuration, "identity");
// other code
}
}
Use context.Request.Path conditionally if your routeData is null. It is the closest I can think of since Identity Server 4 middleware has internal routing logic for the standard OAuth protocol routes.