自定义响应标头未出现在API响应上

时间:2019-06-19 17:03:12

标签: asp.net-core asp.net-core-mvc asp.net-core-webapi

Visual Studio 2019预览版16.2.0预览版2 .Net Core 3.0.0预览版6内部版本19307 Windows 10版本1809 OS Build 17763.402

我遇到了一个问题,我的自定义响应标头没有出现在API响应上,但是确实出现在了MVC响应上。

我创建了以下用作中间件的类。

public class HostHeaderMiddleware
{
    private readonly RequestDelegate _next;

    public HostHeaderMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        // process context.Request

        // since we are adding to the response headers, we can't do that once the response
        // has been started.  we need to hook the event.
        context.Response.OnStarting(() =>
        {
            context.Response.Headers.Add("X-Host", Environment.MachineName);
            return Task.CompletedTask;
        });

        // let the next object in the pipeline handle the context.
        await _next(context);

        // process context.Response
    }
}

public static class HostHeaderMiddlewareExtensions
{
    public static IApplicationBuilder UseHostHeaders(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<HostHeaderMiddleware>();
    }
}

对于我的启动课程,我有以下内容:

using System;
using FluentValidation.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace company.division.project.app
{
    public static class Consts
    {
        /// <summary>
        /// The pattern of a user id.
        /// </summary>
        public const string UserIdPattern = "[a-zA-Z0-9/-]{10,32}";

        /// <summary>
        /// The pattern of a list id.
        /// </summary>
        /// <remarks>A list id is a guid without all the formatting.</remarks>
        public const string ListIdPattern = "[a-fA-F0-9]{32}";
    }

    public class MprStartup<TStartup>
    {
        protected bool UsingFluentValidations { get; }
        protected bool RunAsService { get; }

        protected Type ServiceType { get; }

        public MprStartup(IConfiguration configuration)
        {
            Configuration = configuration;

            UsingFluentValidations = Configuration.GetValue<bool>("UsingFluentValidations");
            RunAsService = Configuration.GetValue<bool>("RunAsService");

            if (RunAsService)
            {
                var aqn = Configuration["MprServiceType"];
                if (!string.IsNullOrWhiteSpace(aqn))
                    ServiceType = Type.GetType(aqn);
            }
        }

        protected IConfiguration Configuration { get; }
        protected ILogger<TStartup> Logger { get; set; }

        // This method gets called by the runtime. Use this method to add services to the container.
        protected void ConfigureServices(IServiceCollection services)
        {
            services.Configure<MongoSettings>(Configuration.GetSection("MongoConnection"));
            services.AddSingleton<MongoSettings>(sp => sp.GetRequiredService<IOptions<MongoSettings>>().Value);

            services.Configure<RouteOptions>(options =>
                {
                    options.ConstraintMap.Add("userid", typeof(UserIdRouteConstraint));
                    options.ConstraintMap.Add("listid", typeof(ListIdRouteConstraint));
                });

            if (RunAsService)
            {
                // this would normally be: services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, WorkerService>()
                // since we are passing in a generic, the above call doesn't work, we need to duplicate the underlying call.
                services.TryAddEnumerable(ServiceDescriptor.Singleton(typeof(IHostedService), ServiceType));
            }

            var mvcBuiilder = services.AddMvc(options => options.EnableEndpointRouting = false)
                .ConfigureApiBehaviorOptions(options =>
                {
                    options.InvalidModelStateResponseFactory = InvalidModelStateResponseFactoryExtension.BnInvalidModelStateResponse;
                });

            if (UsingFluentValidations)
                mvcBuiilder.AddFluentValidation();

            services.AddHttpContextAccessor();

            services.AddControllers()
                .AddNewtonsoftJson(options =>
                {
                    options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
                    options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
                    options.SerializerSettings.DefaultValueHandling = DefaultValueHandling.Ignore;
                });

            services.AddSwaggerGen(options =>
                {
                    options.SwaggerDoc("v1",
                        new OpenApiInfo()
                        {
                            Title = "Mobile Personalized Recommendations API",
                            Version = "v1"
                        });

                    var xmlFile = Path.ChangeExtension(typeof(Startup).Assembly.Location, ".xml");
                    options.IncludeXmlComments(xmlFile);

                    options.SchemaGeneratorOptions.DescribeAllEnumsAsStrings = true;
                    options.SwaggerGeneratorOptions.DescribeAllParametersInCamelCase = true;
                });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        protected void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            Logger = app.ApplicationServices.GetRequiredService<ILogger<TStartup>>();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            beginConfigure?.Invoke(app, env);

            app.ConfigureExceptionHandler();

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });

            app.UseHostHeaders();

            app.UseMvc();

            var autoMapper = a.ApplicationServices.GetRequiredService<IMapper>();
            autoMapper.ConfigurationProvider.AssertConfigurationIsValid();

            app.UseSwagger();
            app.UseSwaggerUI(options =>
            {
                options.DisplayOperationId();
                options.SwaggerEndpoint("/swagger/v1/swagger.json", "Do something API, v1");
            });
        }
    }
}

我尚未明确启用CORS。

我在做什么错,导致自定义标头仅显示MVC响应而不显示API响应?

谢谢

1 个答案:

答案 0 :(得分:0)

我看到您在HostHeaderMiddleware之后调用UseEndpoints()。请将其向上移动(至少需要在UseEndpoints() 之前被调用)

app.UseHostHeaders();
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});
app.UseHostHeaders();

工作演示

enter image description here


请注意,UseMvc()中不需要ASP.NET Core 3