为同一端点使用任何类型的内容类型
Posted
技术标签:
【中文标题】为同一端点使用任何类型的内容类型【英文标题】:Consume any type of content-type for same endpoint 【发布时间】:2019-02-07 15:56:29 【问题描述】:我有一个 asp.net core (v2.1) webapi 项目,它公开了这个功能:
[HttpPost]
[Route("v1/do-something")]
public async Task<IActionResult> PostDoSomething(ModelData model)
//...
还有这个模型:
public class ModelData
[Required]
public string Email get; set;
我想让这个端点灵活,从内容类型的角度来看。因此,应该可以在正文中发送此端点不同的内容类型。
例如,那些“BODY”参数将被允许:
// application/x-www-form-urlencoded
email="abc123@gmail.com"
// application/json
"email": "abc123@gmail.com"
与旧的 .net 框架相比,在 dotnet core 中这是不允许开箱即用的。我发现我需要添加Consume
属性和[FormForm]
属性。但是,如果我在模型参数中添加 [FormForm]
属性,它就不再适用于 JSON(例如)——因为它应该是 [FromBody]
。
我认为使用这样的代码是可以的:
[HttpPost]
[Route("v1/do-something")]
public async Task<IActionResult> PostDoSomething([FromBody] [FromForm] ModelData model)
//...
但正如您所料,此代码不起作用。
因此,为了实现这种灵活性,我必须复制所有端点——这听起来是个非常糟糕的主意。
[HttpPost]
[Route("v1/do-something")]
[Consume ("application/json")]
public async Task<IActionResult> PostDoSomething([FromBody] ModelData model)
//...
[HttpPost]
[Route("v1/do-something")]
[Consume ("application/x-www-form-urlencoded")]
public async Task<IActionResult> PostDoSomething([FromForm] ModelData model)
//...
// ... Other content types here ...
这听起来很容易。但似乎更复杂。
我错过了什么?如何使端点以任何内容类型工作?
【问题讨论】:
【参考方案1】:这是基于内容类型绑定的a custom model binder。
public class BodyOrForm : IModelBinder
private readonly IModelBinderFactory factory;
public BodyOrForm(IModelBinderFactory factory) => this.factory = factory;
public async Task BindModelAsync(ModelBindingContext bindingContext)
var contentType =
bindingContext.ActionContext.HttpContext.Request.ContentType;
BindingInfo bindingInfo = new BindingInfo();
if (contentType == "application/json")
bindingInfo.BindingSource = BindingSource.Body;
else if (contentType == "application/x-www-form-urlencoded")
bindingInfo.BindingSource = BindingSource.Form;
else
bindingContext.Result = ModelBindingResult.Failed();
var binder = factory.CreateBinder(new ModelBinderFactoryContext
Metadata = bindingContext.ModelMetadata,
BindingInfo = bindingInfo,
);
await binder.BindModelAsync(bindingContext);
通过以下操作进行了测试。
[HttpPost]
[Route("api/body-or-form")]
public IActionResult PostDoSomething([ModelBinder(typeof(BodyOrForm))] ModelData model)
return new OkObjectResult(model);
这是demo is on GitHub。
【讨论】:
太棒了!一个完美的解决方案。以上是关于为同一端点使用任何类型的内容类型的主要内容,如果未能解决你的问题,请参考以下文章
RestEasy 客户端提供找不到内容类型应用程序/xml 类型的编写器
Sharepoint rest api - 创建字段并添加到所有内容类型