when using asp.net applications that combines Razor Pages and Api Controllers.
how to globally check if the exception is thrown from an Api Controller ?
the idea is to use UseExceptionHandler midlleware but conditionally return an html response if the unhanded exception is thrown from a Razor Page and a json ProblemDetails response if the excpetion is thrown from an ApiController
For web Api, add attribute route with Api, and then check the request path in the middleware or exception handler like this:
app.UseExceptionHandler("/Error"); //handle the exception from the razor page
//handle the exception from the API.
app.UseWhen(context => context.Request.Path.StartsWithSegments("/api"), subApp =>
{
subApp.UseExceptionHandler(builder =>
{
builder.Run(async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync("{\"error\":\"Exception from API!\"}");
//await context.Response.WriteAsync("ERROR From API!<br><br>\r\n");
//await context.Response.WriteAsync("Home<br>\r\n");
//await context.Response.WriteAsync("</body></html>\r\n");
});
});
});
The result as below:
Besides, you can also use a custom exception handler page is to provide a lambda to UseExceptionHandler. Using a lambda allows access to the path of the request that made the error before returning the response.
For example:
//app.UseExceptionHandler("/Home/Error");
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
var exceptionHandlerPathFeature =
context.Features.Get<IExceptionHandlerPathFeature>();
//check if the handler path contains api or not.
if (exceptionHandlerPathFeature.Path.Contains("api"))
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; ;
context.Response.ContentType = "text/html";
await context.Response.WriteAsync("<html lang=\"en\"><body>\r\n");
await context.Response.WriteAsync("ERROR From API!<br><br>\r\n");
await context.Response.WriteAsync(
"Home<br>\r\n");
await context.Response.WriteAsync("</body></html>\r\n");
}
else
{
context.Response.Redirect("/Home/Error");
}
});
});
More detail information, see asp.net core app.UseExceptionHandler() to handle exceptions for certain endpoints?
Using ASP.Net Core, C#
I have a middleware where i check for particular cookie is present otherwise returning a 400 response. My problem is the middleware fires up starting the project itself and check the cookie is present or not and then shows the response text in the swagger index page, which i dont want.the middleware fies when swagger loads. I want this condition to be executed only for the requests.
public async Task InvokeAsync(HttpContext context)
{
var pl = context.Request.Cookies["pl"];
var sig = context.Request.Cookies["sig"];
if (string.IsNullOrWhiteSpace(pl) || string.IsNullOrWhiteSpace(sig))
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync("Invalid Data");
return;
}
// If success i process and do something
// Call the next delegate/middleware in the pipeline
await _next(context);
}
I can prevent firing the middleware when swagger loads and fire only for api requests. But is this better approach or any other better are there.
app.UseWhen(context => `context.Request.Path.ToString().Contains("/api"),HandleBranch);`
If I throw a BadLabUpdateException then I'd like the response headers of the request to include the exception details. I tried to do this in my Configure method:
app.UseExceptionHandler(errorApp => {
errorApp.Run(async context => {
var pathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
if (pathFeature is BadLabUpdateException ex) {
context.Response.StatusCode = 400;
context.Response.Headers.Add("X-Display-To-User", new StringValues(ex.Message));
}
});
});
That doesn't have any effect though.
The goal is essentially if I throw that specific exception from the server, then the message will contain the exact string that the client should display to the end user.
I need this to work both in development mode and production.
I am working on a ASP.Net Core Web API project and I want to log all the requests and 500 server errors.
I used custom middleware to log requests, and it is defined in the startup.cs as:
app.UseMiddleware<logMiddleware>();
I also defined an Exception handler to capture server errors in the startup.cs:
app.UseExceptionHandler(builder => {
builder.Run(async context => {
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null) {
logService logger = new logService(conf);
await logger.logError(error, context);
}
});
});
I keep the request information in error logs, so I don't need to save the request log when the response code is 500, so I added a check into request log function to filter errors:
public async Task logRequestAsync(HttpContext context) {
if (context.Response.StatusCode != 500) {
//do things
}
}
The problem is the Response.StatusCode returns as 200 instead of 500. Probably it runs before the API call's function is completed and the server error happens later in the runtime.
Is there a way to move the "request log" process to the point where the response is created instead of the beginning of the API request?
I'm using OpenIddict for auth in a .NET Core 2 API. Client side I'm relying on any API errors to follow a custom scheme. However, when e.g. a refresh token has been outdated, I can't seem to find out how to customize the error sent back.
The /token endpoint is never reached, so the error is not under "my control".
The result of the request is a status code 400, with the following JSON:
{"error":"invalid_grant","error_description":"The specified refresh token is no longer valid."}
I've tried to use a custom middleware to catch all status codes (which it does), but the result is returned before the execution of my custom middleware has completed.
How can I properly customize the error or intercept to change it? Thanks!
You can use OpenIddict's event model to customize the token response payloads before they are written to the response stream. Here's an example:
MyApplyTokenResponseHandler.cs
public class MyApplyTokenResponseHandler : IOpenIddictServerEventHandler<ApplyTokenResponseContext>
{
public ValueTask HandleAsync(ApplyTokenResponseContext context)
{
var response = context.Response;
if (string.Equals(response.Error, OpenIddictConstants.Errors.InvalidGrant, StringComparison.Ordinal) &&
!string.IsNullOrEmpty(response.ErrorDescription))
{
response.ErrorDescription = "Your customized error";
}
return default;
}
}
Startup.cs
services.AddOpenIddict()
.AddCore(options =>
{
// ...
})
.AddServer(options =>
{
// ...
options.AddEventHandler<ApplyTokenResponseContext>(builder =>
builder.UseSingletonHandler<MyApplyTokenResponseHandler>());
})
.AddValidation();
The /token endpoint is never reached, so the error is not under "my control".
In fact ,the /token is reached, and the parameter of grant_type equals refresh_token. But the rejection logic when refresh token expired is not processed by us. It is some kind of "hardcoded" in source code :
if (token == null)
{
context.Reject(
error: OpenIddictConstants.Errors.InvalidGrant,
description: context.Request.IsAuthorizationCodeGrantType() ?
"The specified authorization code is no longer valid." :
"The specified refresh token is no longer valid.");
return;
}
if (options.UseRollingTokens || context.Request.IsAuthorizationCodeGrantType())
{
if (!await TryRedeemTokenAsync(token))
{
context.Reject(
error: OpenIddictConstants.Errors.InvalidGrant,
description: context.Request.IsAuthorizationCodeGrantType() ?
"The specified authorization code is no longer valid." :
"The specified refresh token is no longer valid.");
return;
}
}
The context.Reject here comes from the assembly AspNet.Security.OpenIdConnect.Server.
For more details, see source code on GitHub .
I've tried to use a custom middleware to catch all status codes (which it does), but the result is returned before the execution of my custom middleware has completed.
I've tried and I'm pretty sure we can use a custom middleware to catch all status codes. The key point is to detect the status code after the next() invocation:
app.Use(async(context , next )=>{
// passby all other end points
if(! context.Request.Path.StartsWithSegments("/connect/token")){
await next();
return;
}
// since we might want to detect the Response.Body, I add some stream here .
// if you only want to detect the status code , there's no need to use these streams
Stream originalStream = context.Response.Body;
var hijackedStream = new MemoryStream();
context.Response.Body = hijackedStream;
hijackedStream.Seek(0,SeekOrigin.Begin);
await next();
// if status code not 400 , pass by
if(context.Response.StatusCode != 400){
await CopyStreamToResponseBody(context,hijackedStream,originalStream);
return;
}
// read and custom the stream
hijackedStream.Seek(0,SeekOrigin.Begin);
using (StreamReader sr = new StreamReader(hijackedStream))
{
var raw= sr.ReadToEnd();
if(raw.Contains("The specified refresh token is no longer valid.")){
// custom your own response
context.Response.StatusCode = 401;
// ...
//context.Response.Body = ... /
}else{
await CopyStreamToResponseBody(context,hijackedStream,originalStream);
}
}
});
// helper to make the copy easy
private async Task CopyStreamToResponseBody(HttpContext context,Stream newStream, Stream originalStream){
newStream.Seek(0,SeekOrigin.Begin);
await newStream.CopyToAsync(originalStream);
context.Response.ContentLength =originalStream.Length;
context.Response.Body = originalStream;
}