问题描述
我有一个 ASP.NET Core Web API(TargetFramework 是 net5.0
),我接受 2 种媒体类型:
application/json
application/vnd.my.customtype+json
[ApiController]
[ApiConventionType(typeof(DefaultApiConventions))]
[Route("")]
public class RootController : ControllerBase
{
[HttpGet]
[HttpHead]
[Route("",Name = "get_root")]
[ApiConventionMethod(typeof(DefaultApiConventions),nameof(DefaultApiConventions.Get))]
public IActionResult Get()
{
return this.Ok(new { Item1 = "item 1" });
}
}
如果我使用标头 GET
发出 Accept: application/json
请求并返回带有结果的 200 OK
,则一切正常:
GET https://localhost:5001/ HTTP/1.1
Accept: application/json
Host: localhost:5001
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Server: Kestrel
Content-Length: 18
{"item1":"item 1"}
如果我使用标头 GET
发出 Accept: application/vnd.my.customtype+json
请求并返回带有结果的 200 OK
,一切也都很好:
GET https://localhost:5001/ HTTP/1.1
Accept: application/vnd.my.customtype+json
Host: localhost:5001
HTTP/1.1 200 OK
Content-Type: application/vnd.my.customtype+json; charset=utf-8
Server: Kestrel
Content-Length: 18
{"item1":"item 1"}
返回 404 的相同控制器(例如,如果未找到资源)
[ApiController]
[ApiConventionType(typeof(DefaultApiConventions))]
[Route("")]
public class RootController : ControllerBase
{
[HttpGet]
[HttpHead]
[Route("",nameof(DefaultApiConventions.Get))]
public IActionResult Get()
{
return this.NotFound();
}
}
现在,如果我使用标头 GET
发出 Accept: application/json
请求,一切正常(我在响应正文中得到正确的 404 和问题详细信息):
GET https://localhost:5001/ HTTP/1.1
Accept: application/json
Host: localhost:5001
HTTP/1.1 404 Not Found
Content-Type: application/problem+json; charset=utf-8
Server: Kestrel
Content-Length: 161
{
"type":"https://tools.ietf.org/html/rfc7231#section-6.5.4","title":"Not Found","status":404,"traceId":"00-54d50dfe6a27674ba83a73324fcf2721-d4286734f860c94a-00"
}
如果我使用标头 GET
发出 Accept: vnd.my.customtype+json
请求,我会收到 406 Not Acceptable
响应:
GET https://localhost:5001/ HTTP/1.1
Accept: application/vnd.my.customtype+json
Host: localhost:5001
HTTP/1.1 406 Not Acceptable
Server: Kestrel
Content-Length: 0
在日志中,我得到了这个:
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 GET https://localhost:5001/ - -
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint 'Hooli.WebAPI.Controllers.RootController.Get (Hooli.WebAPI)'
info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[3]
Route matched with {action = "Get",controller = "Root"}. Executing controller action with signature Microsoft.AspNetCore.Mvc.IActionResult Get() on controller Hooli.WebAPI.Controllers.RootController (Hooli.WebAPI).
warn: Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor[1]
No output formatter was found for content types 'application/problem+json,application/problem+xml' to write the response.
info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[2]
Executed action Hooli.WebAPI.Controllers.RootController.Get (Hooli.WebAPI) in 0.8058ms
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint 'Hooli.WebAPI.Controllers.RootController.Get (Hooli.WebAPI)'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished HTTP/1.1 GET https://localhost:5001/ - - - 406 0 - 1.1703ms
注意 warn
行:No output formatter was found for content types 'application/problem+json,application/problem+xml' to write the response.
我不明白为什么这不会返回带有问题详细信息的正确 404
。
我的Startup.cs如下:
public class Startup
{
public Startup(IConfiguration configuration)
{
this.Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(
options =>
{
options.ReturnHttpNotAcceptable = true;
})
.AddNewtonsoftJson(
options =>
{
options.SerializerSettings.ContractResolver =
new CamelCasePropertyNamesContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy(true,true)
};
});
services.Configure<Mvcoptions>(
options =>
{
NewtonsoftJsonOutputFormatter newtonsoftJsonOutputFormatter =
options.OutputFormatters.OfType<NewtonsoftJsonOutputFormatter>().FirstOrDefault() ??
throw new ArgumentNullException(nameof(newtonsoftJsonOutputFormatter));
// add media types that we want to support by default
newtonsoftJsonOutputFormatter
.SupportedMediaTypes
.Add(MediaTypeHeaderValue.Parse("application/vnd.my.customtype+json"));
});
}
public void Configure(IApplicationBuilder app,IWebHostEnvironment env)
{
app.UseExceptionHandler(env.IsDevelopment() ? "/error-local-development" : "/error");
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
}
为了完整性,我的 Program.cs 文件:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
}
}
我已经将我的 API 精简到这个最基本的设置,以排除任何额外的中间件/配置。我返回什么状态代码并不重要,如果它是任何 4xx
响应,当 406
标头是自定义媒体类型时,我总是得到 Accept
。
例如
- this.BadRequest();
- this.NotFound();
- this.Unauthorized();
- 等
如果我将 options.ReturnHttpNotAcceptable = true
更改为 options.ReturnHttpNotAcceptable = false
,所有响应都会正常返回,但是当在 Accept
中传递无效媒体类型时,API 不会返回 406标题。
当自定义媒体类型位于 Accept
标头中时,如何让 API 返回正确的响应?
解决方法
试试这个:
添加一个类:
merged_df = workers_df.merge(rates_gp,how='inner',on='state')
merged_df
Employeeid state salary rate
0 11 AL 2000 0.0046
1 12 AL 3500 0.0046
2 13 AZ 1100 0.0033
3 14 AZ 4200 0.0033
并在您的服务中注册:
public class CustomOutputFormatter: OutputFormatter
{
public CustomOutputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/vnd.my.customtype+json"));
}
public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context)
{
var response = context.HttpContext.Response;
await response.WriteAsJsonAsync(context.Object);
}
}