Asp.net Core Log all SQL queries in middleware - c#

I want to log every SQL request in a request to the database with a middleware. so far I have only managed to create an empty middleware that is hit by every request but I have no idea how to get the SQL queries from that request.
I have read a few articles (1) about how to log requests and responses with middleware but they are getting the whole request (the whole HTML page) and I only want the SQL queries. so how can I get those queries?
public class LoggingEveryRequestToLogDataBase
{
private readonly RequestDelegate _next;
public LoggingEveryRequestToLogDataBase(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context, ILogDbContext dbContext)
{
var request = new
{
ipAddress = context.Connection.RemoteIpAddress.ToString(),
path = context.Request.Host + context.Request.Path + context.Request.QueryString,
};
await _next(context);
}
}
public static class LoggingEveryRequestToLogDataBaseMiddleware
{
public static IApplicationBuilder LoggingEveryRequestToLogDataBase(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<LoggingEveryRequestToLogDataBase>();
}
}

Related

ASP.NET Core Own middleware component

How to get the second argument from HTTP Context? For example
localhost:45423/?login=login1/?password=password1
Here is what I coded but it doesn't work. It always says that I'm not logged even when I inputted the correct login and password
public class CheckData
{
private readonly RequestDelegate _next;
public CheckData(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var login = context.Request.Query["login"];
var password = context.Request.Query["password"];
if(login != "log1" && password != "pass1")
{
context.Response.StatusCode = 403;
await context.Response.WriteAsync("Sorry, you're not logged");
}
else
{
await _next.Invoke(context);
}
}
}
P.S: I've began learning ASP.NET Core today, only login works, but not login with password
You are passing the query parameters wrongly, For multiple query parameter you should pass like localhost:45423?login=login1&password=password1

Scoped service creating two different instances for a request

I'm fairly new to Asp.Net core 6 and am working on an GraphQL API that receives a bearer token in the request. The API then invokes another Web API and passes the same bearer token in the header. Below is what my code looks like-
Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<HeaderHandler>();
builder.Services.AddScoped<TokenContainer>();
//builder.Services.AddScoped<IFooGateway, FooGateway>();
builder.Services.AddHttpClient<IFooGateway, FooGateway>((c) =>
{
c.BaseAddress = new Uri(builder.Configuration["FooApiUrl"]);
})
.AddHttpMessageHandler<HeaderHandler>();
builder.Services.AddTransient<GraphApiService>();
var app = builder.Build();
app.UseMiddleware<HeaderMiddleware>();
app.MapGraphQL();
app.Run();
HeaderMiddleware.cs
public class HeaderMiddleware
{
//TokenContainer _tokenContainer;
private readonly RequestDelegate _requestDelegate;
public HeaderMiddleware()
{
}
public HeaderMiddleware(RequestDelegate requestDelegate)
{
_requestDelegate = requestDelegate;
//_tokenContainer = tokenContainer;
}
public async Task InvokeAsync(HttpContext context, TokenContainer tokenContainer)
{
var header = context.Request.Headers.Authorization;
tokenContainer.SetToken(header);
await _requestDelegate(context);
}
TokenContainer.cs:
public class TokenContainer
{
public string BearerToken { get; private set; }
public void SetToken(string token) => BearerToken = token;
}
HeaderHandler.cs:
public class HeaderHandler : DelegatingHandler
{
TokenContainer _tokenContainer;
public HeaderHandler()
{
}
public HeaderHandler(TokenContainer tokenContainer)
{
_tokenContainer = tokenContainer;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// for every request sent via the http client, intercept & add the bearer token header.
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", _tokenContainer.BearerToken);
// continue with request pipeline
return await base.SendAsync(request, cancellationToken);
}
}
FooGateway.cs:
public class FooGateway : IFooGateway
{
private readonly HttpClient _httpClient;
private readonly IConfiguration _configuration;
private readonly string _context = String.Empty;
public FooGateway(HttpClient httpClient, IConfiguration configuration)
{
_configuration = configuration;
_context = configuration["FooContext"];
_httpClient = httpClient;
}
public void DoSomething()
{
_httpClient.PostAsync("/blabla");
}
}
So, the idea was that the bearer token for every incoming request will be stored in a class called TokenContainer and the HttpHandler will add it to all the outgoing requests.
However, what is happening is that the token is stored in the TokenContainer but the HeaderHandler gets a different instance of TokenContainer in its constructor with its BearerToken property set to null.
Can someone please explain why the same instance of TokenContainer from the middleware is not being passed into the HeaderHandler?
The issue you are seeing is because the lifetime of the HttpMessageHandler is not the same as the lifetime of the request: usually, the same handler will be reused across many requests and be controlled separately on expiration timers and such.
You should not expect that a service injected into your message handler will be the same object that is injected outside it when it is registered as scoped.
https://andrewlock.net/understanding-scopes-with-ihttpclientfactory-message-handlers/#scope-duration-in-ihttpclientfactory
As the article suggests, to use the same scoped instance as you do outside the handler, you have to rely on IHttpContextAccessor to access the current HttpContext and fetch the service from there. So your handler implementation would look something like this:
public class HeaderHandler : DelegatingHandler
{
IHttpContextAccessor _httpContextAccessor;
public HeaderHandler()
{
}
public HeaderHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var tokenContainer = _httpContextAccessor
.HttpContext
.RequestServices
.GetRequiredService<TokenContainer>();
// for every request sent via the http client, intercept & add the bearer token header.
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", tokenContainer.BearerToken);
// continue with request pipeline
return await base.SendAsync(request, cancellationToken);
}
}
This should make sure that the TokenContainer instance is the same across your current request and http calls.
Remember that to add this functionality you need to add the accessor like this:
services.AddHttpContextAccessor();

How do I save the URL that was requested and forward to it after the user went through the captive portal?

I have the following code:
public class CookieCheckMiddleware
{
private readonly RequestDelegate _next;
public CookieCheckMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
if(httpContext.Request.Cookies["MyCookie"] == null && httpContext.Request.Path != "/WhereIShouldGo")
{
httpContext.Response.Redirect("/WhereIShouldGo");
}
await _next(httpContext); // calling next middleware
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class CookieCheckMiddlewareExtensions
{
public static IApplicationBuilder UseCookieCheckMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CookieCheckMiddleware>();
}
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseCookieCheckMiddleware();
...
}
It basically redirects to the captive portal if no cookie is set. Now I need to up a level - I need to somehow save the httpContext.Request.Path and forward to it RIGHT AFTER the user accepted the cookies. So setting a cookie beforehand is not an option, since the user hasn't accepted it yet... How could I accomplish that?
The solution was giving the request URL via the redirect like this:
httpContext.Response.Redirect("/Cookies?q="+ httpContext.Request.Path);
and then via JavaScript getting the GET parameter and redirecting after clicking on the button.
Case closed :-)

Measure request processing time with Azure Function App

I need to measure the time taken to process each request in Azure Function App, like below via ASP.NET (non ASP.NET CORE)
public class RequestLogHandler : DelegatingHandler
{
private readonly ILogger _logger;
public RequestLogHandler(ILogger logger)
{
_logger = logger;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
string path = request.GetOwinContext().Request.Path.ToString();
string method = request.Method.Method;
var sw = Stopwatch.StartNew();
var response = await base.SendAsync(request, cancellationToken);
sw.Stop();
int statusCode = (int)response.StatusCode;
_logger.HttpRequest(path, method, sw.ElapsedMilliseconds.ToString(), statusCode.ToString());
return response;
}
}
Setup
public class WebServer : IWebServer
{
private readonly ILogger _logger;
private readonly HttpConfiguration _httpConfiguration;
private IDisposable _server;
public WebServer(ILogger logger, HttpConfiguration httpConfiguration)
{
_logger = logger;
_httpConfiguration = httpConfiguration;
}
public void Start(string url)
{
_server = WebApp.Start(url, (appBuilder) =>
{
_httpConfiguration.MapHttpAttributeRoutes();
_httpConfiguration.MessageHandlers.Add(new RequestLogHandler(_logger));
appBuilder.UseWebApi(_httpConfiguration);
});
}
public void Dispose()
{
if(_server != null)
{
_server.Dispose();
_server = null;
}
}
}
You could use Application Insights to get the Request Duration, in the application insights page you could get the request duration details.
This is the Duration description: This field is required as request telemetry represents the operation with the beginning and the end. You also could use the Microsoft.ApplicationInsights NuGet package to send custom telemetry data to Application Insights including the Duration.

ASP.NET 5.0 Kestrel server with nginx

I am trying to host an ASP.NET 5.0 (beta 4) website on Ubuntu. I have configured Kestrel with nginx as a reverse proxy, but there are a couple of problems that prevent this from being used for a production site:
HTTP 404 error pages are blank - is there a way to configure either ASP.NET/Kestrel or nginx to send a custom page instead of a blank page?
How can I configure URL rewriting - for example, I have some static .htm pages in addition to the ASP.NET content, and I would like to rewrite these to serve them without the .htm extension
Thanks to the suggestion from Matt DeKrey, I got this working using two pieces of middleware.
For custom 404 error pages, I used:
public class CustomErrorMiddleware
{
private readonly RequestDelegate next;
public CustomErrorMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
context.Response.StatusCode = 404;
context.Response.ContentType = "text/html";
await context.Response.SendFileAsync("/errors/404.html");
}
}
While for URL rewriting I used:
public class UrlRewriteMiddleware
{
private readonly RequestDelegate next;
public UrlRewriteMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
// Redirect from /some/page.htm to /some/page
Regex r1 = new Regex("^/some/[a-zA-Z0-9]+\\.htm$");
if (r1.IsMatch(context.Request.Path.Value))
{
context.Response.Redirect(context.Request.Path.Value.Substring(0, context.Request.Path.Value.Length - 4));
return;
}
// Rewrite from /some/page to /some/page.htm
Regex r2 = new Regex("^/some/[a-zA-Z0-9]+$");
if (r2.IsMatch(context.Request.Path.Value))
context.Request.Path = new PathString(context.Request.Path.Value + ".htm");
await next(context);
}
}
Then Startup.cs is modified to use each of these. Middleware is run in the order it is specified in, so the URL rewriting needs to be first to modify the requests as they are received. The custom 404 error middleware needs to be last to catch any requests not handled by any other middleware. For example:
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<UrlRewriteMiddleware>();
app.UseStaticFiles();
app.UseMvc();
app.UseMiddleware<CustomErrorMiddleware>();
}

Categories

Resources