Пользовательский заголовок ответа не отображается в ответе API - PullRequest
0 голосов
/ 19 июня 2019

Visual Studio 2019 Preview 16.2.0 Preview 2 .Net Core 3.0.0 Preview 6 Build 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 голосов
/ 26 июня 2019

Я вижу, вы вызываете HostHeaderMiddleware после UseEndpoints().Пожалуйста, переместите его вверх (, по крайней мере, необходимо вызвать до UseEndpoints())

<b>app.UseHostHeaders();</b>
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});
<strike>app.UseHostHeaders();</strike>

Рабочая демонстрация

enter image description here


В качестве примечания вам не нужно UseMvc() в ASP.NET Core 3

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...