在迁移到 .Net Core 3.1 时,.Net Core 2.2 中使用的 Services.AddMvc() 和 SuperJsonOutputFormatter 的替代方法是什么

what is the alternative for Services.AddMvc() and SuperJsonOutputFormatter used in .Net Core 2.2 while migrating to .Net Core 3.1

我正在迁移一个 .Net Core 2.2 web API 应用程序,它只有 API 控制器,没有视图。我使用 SuperJsonOutputFormatter 在我的项目中设置了自定义 API 响应。现在,我正在使用 NewtonsoftJsonOutputFormatter 为 API 创建自定义响应。但根据微软文档,services.AddMvc() 在 .Net Core 3.1 中已过时。那么,如何调用 startup.cs 中的 customformatter。我正在使用下面的代码

services.AddControllers().AddNewtonsoftJson(options =>
{
    options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
    options.SerializerSettings.Formatting = Formatting.Indented;
});
services.AddScoped<SuperJsonOutputFormatterFilter>();
services.AddMvc(opts =>
{
    opts.EnableEndpointRouting = false;
    var oldFormatter = opts.OutputFormatters.OfType<CustomOutputFormatter>().Single();
    opts.OutputFormatters.Remove(oldFormatter);
    var replacementJsonOutputFormatter =
       new CustomOutputFormatter(oldFormatter.serializerSettings, ArrayPool<char>.Shared);
    opts.OutputFormatters.Add(replacementJsonOutputFormatter);
}).SetCompatibilityVersion(CompatibilityVersion.Version_3_0);

配置服务如下

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
   if (env.IsDevelopment())
   {
      app.UseDeveloperExceptionPage();
   }
   else
   {
      app.UseHsts();
   }
   app.UseRouting();
   app.UseExceptionHandler("/Error");
   app.UseAuthentication();
   app.UseStaticFiles();
   app.UseHttpsRedirection();
   app.UseMvc();
}

上面的代码给出了一个运行时错误,无法构建某些服务,验证服务描述符时出错。如何在不使用 Services.AppMvc()

的情况下调用 customformatter

我的Error_Helper如下

错误描述Class

 public class ErrorDescription
    {
        public ErrorDescription(HttpStatusCode statusCode)
        {
            this.Code = (int)statusCode;
            this.Description = GetDescription((int)statusCode);
        }

        string GetDescription(int statusCode)
        {
            return statusCode switch
            {
                404 => "Employee ID not found",
                500 => "Internal server error",
                400 => "Device token already registered",
                406 => "No data found in table",
                _ => "",
            };
        }


        [JsonProperty("errorCode")]
        public int Code { get; set; }
        [JsonProperty("errorDescription")]
        public string Description { get; set; }
    }

格式过滤器Class

 public class CustomJsonOutputFormatterFilter : IAsyncActionFilter
    {
        private readonly CustomOutputFormatter _formatter;
        // inject SuperJsonOutputFormatter service
        public CustomJsonOutputFormatterFilter(CustomOutputFormatter formatter)
        {
            this._formatter = formatter;
        }
        // a helper method that provides an ObjectResult wrapper over the raw object
        private ObjectResult WrapObjectResult(ActionExecutedContext context, object obj)
        {
            var wrapper = new ObjectResult(obj);
            wrapper.Formatters.Add(this._formatter);
            context.Result = wrapper;
            return wrapper;
        }

        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            ActionExecutedContext resultContext = await next();
            // in case we get a 500
            if (resultContext.Exception != null && !resultContext.ExceptionHandled)
            {
                var ewrapper = this.WrapObjectResult(resultContext, new { });
                ewrapper.StatusCode = (int)HttpStatusCode.InternalServerError;
                resultContext.ExceptionHandled = true;
                return;
            }
            else
            {
                switch (resultContext.Result)
                {
                    case BadRequestObjectResult b:      // 400 with an object
                        var bwrapper = this.WrapObjectResult(resultContext, b.Value);
                        bwrapper.StatusCode = b.StatusCode;
                        break;
                    case NotFoundObjectResult n:        // 404 with an object
                        var nwrapper = this.WrapObjectResult(resultContext, n.Value);
                        nwrapper.StatusCode = n.StatusCode;
                        break;
                    case ObjectResult o:                // plain object
                        this.WrapObjectResult(resultContext, o.Value);
                        break;
                    case JsonResult j:                  // plain json
                        this.WrapObjectResult(resultContext, j.Value);
                        break;
                    case StatusCodeResult s:             // other statusCodeResult(including NotFound,NoContent,...), you might want to custom this case 
                        var swrapper = this.WrapObjectResult(resultContext, new { result="" });
                        swrapper.StatusCode = s.StatusCode;
                        break;
                }
            }
        }

    }

Custom Outputformatter Class,这个 class 调用 customformatterfilter

 public class CustomOutputFormatter : NewtonsoftJsonOutputFormatter
    {
        public CustomOutputFormatter(JsonSerializerSettings serializerSettings,
            ArrayPool<char> charPool) : base (serializerSettings, charPool)
        {
        }

        public JsonSerializerSettings serializerSettings { get; private set; }



        public override async Task WriteResponseBodyAsync(
            OutputFormatterWriteContext context,
            Encoding selectedEncoding)
        {

            if (context == null)
                throw new ArgumentNullException(nameof(context));
            if (selectedEncoding == null)
            if (selectedEncoding == null)
                throw new ArgumentNullException(nameof(selectedEncoding));
            using TextWriter writer = context.WriterFactory(context.HttpContext.Response.Body, selectedEncoding);
            var statusCode = context.HttpContext.Response.StatusCode;
            var rewrittenValue = new
            {
                status = IsSucceeded(statusCode),
                error = IsSucceeded(statusCode) ? null : new ErrorDescription((HttpStatusCode)statusCode),
                data = context.Object,
            };
            writer.Write(rewrittenValue);
            this.CreateJsonWriter(writer);
            await writer.FlushAsync();

        }

        private bool IsSucceeded(int statusCode)
        {
            // 204 is not an error but handled
            if (statusCode >= 400 || statusCode == 204) { return false; }
            return true;
        }
    }

我没有详细查看你的实现(看起来很复杂,你想达到什么目的?),但你可以像使用 [=12] 一样使用 UseControllers() =] 之前配置 MvcOptions 实例。例如:

services.AddControllers(options =>
{
    options.InputFormatters.Insert(0, new VcardInputFormatter());
    options.OutputFormatters.Insert(0, new VcardOutputFormatter());
})

这可能会解决您的问题 - 无需致电 AddMvc

但是,错误 "Some services are not able to be constructed" 表明您缺少服务依赖项。错误消息会告诉您是哪一个。这是 .NET Core 3.1 中的新功能,服务提供者验证,您可以 read about in this blog post.