Как я могу вернуть пользовательский заголовок http в ядре asp.net?
Чао,
Я разрабатываю серию микросервисов с ядром aspnet. Я хочу вернуть пользовательский заголовок http на 500 ответов.
Я попытался создать специальное промежуточное программное обеспечение ASP.NET Core, которое обновляет свойство context.Response.Headers, но оно работает только при ответе 200.
Это мое пользовательское промежуточное ПО:
namespace Organizzazione.Progetto
{
public class MyCustomMiddleware
{
private readonly RequestDelegate _next;
public ExtractPrincipalMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
context.Response.Headers.Add("X-Correlation-Id", Guid.NewGuid());
await _next.Invoke(context);
return;
}
}
}
Это мой метод настройки:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMiddleware<MyCustomMiddleware>();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API");
});
}
Как я могу вернуть свой пользовательский заголовок для ответа 500, вызванного необработанным исключением (или, возможно, для всех ответов)?
Спасибо большое
2 ответа
Вы должны подписаться на httpContext.Response.OnStarting
public class CorrelationIdMiddleware
{
private readonly RequestDelegate _next;
public CorrelationIdMiddleware(RequestDelegate next)
{
this._next = next;
}
public async Task Invoke(HttpContext httpContext)
{
httpContext.Response.OnStarting((Func<Task>)(() =>
{
httpContext.Response.Headers.Add("X-Correlation-Id", Guid.NewGuid().ToString());
return Task.CompletedTask;
}));
try
{
await this._next(httpContext);
}
catch (Exception)
{
//add additional exception handling logic here
//...
httpContext.Response.StatusCode = 500;
}
}
}
И зарегистрируй свой Starup
app.UseMiddleware(typeof(CorrelationIdMiddleware));
Вы можете использовать глобальный фильтр:
public class CorrelationIdFilter : IActionFilter
{
/// <summary>
/// Called after the action executes, before the action result.
/// </summary>
/// <param name="context">The <see cref="T:Microsoft.AspNetCore.Mvc.Filters.ActionExecutedContext" />.</param>
public void OnActionExecuted(ActionExecutedContext context)
{
}
/// <summary>
/// Called before the action executes, after model binding is complete.
/// </summary>
/// <param name="context">The <see cref="T:Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext" />.</param>
/// <exception cref="InvalidOperationException"></exception>
public void OnActionExecuting(ActionExecutingContext context)
{
context.HttpContext.Response.Headers.Add("X-Correlation-Id", Guid.NewGuid().ToString());
}
}
Если вы делаете то же самое, но только в исключительных случаях, используйте IExceptionFilter:
public class CorrelationIdFilter : IExceptionFilter, IAsyncExceptionFilter
{
private readonly ILogger<CorrelationIdFilter> _logger;
/// <summary>
/// Initialize a new instance of <see cref="CorrelationIdFilter"/>
/// </summary>
/// <param name="logger">A logger</param>
public CorrelationIdFilter(ILogger<CorrelationIdFilter> logger)
{
_logger = logger;
}
/// <summary>
/// Called after an action has thrown an <see cref="T:System.Exception" />.
/// </summary>
/// <param name="context">The <see cref="T:Microsoft.AspNetCore.Mvc.Filters.ExceptionContext" />.</param>
/// <returns>
/// A <see cref="T:System.Threading.Tasks.Task" /> that on completion indicates the filter has executed.
/// </returns>
public Task OnExceptionAsync(ExceptionContext context)
{
Process(context);
return Task.CompletedTask;
}
/// <summary>
/// Called after an action has thrown an <see cref="T:System.Exception" />.
/// </summary>
/// <param name="context">The <see cref="T:Microsoft.AspNetCore.Mvc.Filters.ExceptionContext" />.</param>
public void OnException(ExceptionContext context)
{
Process(context);
}
private void Process(ExceptionContext context)
{
var e = context.Exception;
_logger.LogError(e, e.Message);
context.HttpContext.Response.Headers.Add("X-Correlation-Id", Guid.NewGuid().ToString());
if (e is InvalidOperationException)
{
context.Result = WriteError(HttpStatusCode.BadRequest, e);
}
else if (e.GetType().Namespace == "Microsoft.EntityFrameworkCore")
{
context.Result = WriteError(HttpStatusCode.BadRequest, e);
}
else
{
context.Result = WriteError(HttpStatusCode.InternalServerError, e);
}
}
private IActionResult WriteError(HttpStatusCode statusCode, Exception e)
{
var result = new ApiErrorResult(e.Message, e)
{
StatusCode = (int)statusCode,
};
return result;
}
}
/// <summary>
/// Api error result
/// </summary>
/// <seealso cref="Microsoft.AspNetCore.Mvc.ObjectResult" />
public class ApiErrorResult : ObjectResult
{
private readonly string _reasonPhrase;
/// <summary>
/// Initializes a new instance of the <see cref="ApiErrorResult"/> class.
/// </summary>
/// <param name="reasonPhrase">The reason phrase.</param>
/// <param name="value">The value.</param>
public ApiErrorResult(string reasonPhrase, object value) : base(value)
{
_reasonPhrase = reasonPhrase;
}
/// <inheritdoc />
public override async Task ExecuteResultAsync(ActionContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var reasonPhrase = _reasonPhrase;
reasonPhrase = reasonPhrase.Split(new string[] { Environment.NewLine }, StringSplitOptions.None)[0];
context.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = reasonPhrase;
await base.ExecuteResultAsync(context);
}
}
И зарегистрируйте свой фильтр с помощью:
services.AddMvc(configure =>
{
var filters = configure.Filters;
filters.Add<CorrelationIdFilter >();
})