【问题标题】:How can I return custom http header in asp.net core?如何在 asp.net 核心中返回自定义 http 标头?
【发布时间】:2018-10-19 13:39:01
【问题描述】:

喏,

我正在开发一系列带有 aspnet 核心的微服务。我想在 500 个响应上返回一个自定义的 http 标头。

我尝试创建一个自定义 ASP.NET Core Middleware 来更新 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 响应中返回我的自定义标头?

非常感谢

【问题讨论】:

  • 是什么导致了 500 响应?未捕获的异常?如果是这样,您可以编写一个修改响应标头的全局异常处理中间件。
  • 嗨,克里斯,500 响应是我未捕获的异常引起的。我更新了这个问题以澄清这个概念。谢谢

标签: c# asp.net-core middleware custom-headers


【解决方案1】:

你必须订阅 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));

【讨论】:

    【解决方案2】:

    您可以使用全局过滤器:

    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 >();
                })
    

    【讨论】:

    • 嗨,agua,感谢您的回复,我已经实施了您的解决方案,但在异常情况下,我获得了没有 CorrelationId 的回复。
    • MVC是否抛出异常?
    • 嗨,是的。我在操作开始时添加了一个 new Exception()
    • 我的错,您需要发送ObjectResult。我更新了回复
    猜你喜欢
    • 2023-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-13
    • 1970-01-01
    • 2021-02-10
    • 2013-05-15
    • 1970-01-01
    相关资源
    最近更新 更多