C# 如何:从多个源绑定参数
目前,我正在尝试创建一个基于asp.net core 2.0的web api,我想创建一个嵌套路由。在put请求的情况下,它在路由中发送一部分信息,在正文中发送另一部分信息 要求 需要调用的url为C# 如何:从多个源绑定参数,c#,asp.net-core,asp.net-core-mvc,asp.net-core-2.0,C#,Asp.net Core,Asp.net Core Mvc,Asp.net Core 2.0,目前,我正在尝试创建一个基于asp.net core 2.0的web api,我想创建一个嵌套路由。在put请求的情况下,它在路由中发送一部分信息,在正文中发送另一部分信息 要求 需要调用的url为 https://localhost/api/v1/master/42/details 如果我们想在主节点42下创建一个新的细节,我希望在主节点id离开路由时在主体中发送细节数据 curl -X POST --header 'Content-Type: application/json' \ --h
https://localhost/api/v1/master/42/details
如果我们想在主节点42下创建一个新的细节,我希望在主节点id离开路由时在主体中发送细节数据
curl -X POST --header 'Content-Type: application/json' \
--header 'Accept: application/json' \
-d '{ \
"name": "My detail name", \
"description": "Just some kind of information" \
}' 'https://localhost/api/v1/master/42/details'
请求的后续响应将是
{
"name": "My detail name",
"description": "Just some kind of information",
"masterId": 42,
"id": 47
}
以及响应头中的位置url,如
{
"location": "https://localhost/api/v1/master/42/details/47
}
迄今为止所做的工作
为了使其正常工作,我创建了此控制器:
[Produces("application/json")]
[Route("api/v1/master/{masterId:int}/details")]
public class MasterController : Controller
{
[HttpPost]
[Produces(typeof(DetailsResponse))]
public async Task<IActionResult> Post([FromBody, FromRoute]DetailCreateRequest request)
{
if(!ModelState.IsValid)
return BadRequest(ModelState);
var response = await Do.Process(request);
return CreatedAtAction(nameof(Get), new { id = response.Id }, response);
}
}
问题
到目前为止,大部分的东西都像预期的那样工作。唯一真正不起作用的是将路由中的MasterId合并到来自主体的DetailCreateRequest
第一次尝试:在参数上使用两个属性
我试图通过这次行动号召将这两件事结合起来:
public async Task<IActionResult> Post([FromBody, FromRoute]DetailCreateRequest request)
public async Task<IActionResult> Post([FromRoute]int masterId, [FromBody]DetailCreateRequest request)
在第一个点,这看起来不错,因为现在我在控制器动作中有两个值。但这种方法的最大问题是模型验证。正如您在上面的代码中所看到的,我检查了ModelState.IsValid
,它是通过FluentValidation
中的一些检查填写的,但是这些检查无法真正完成,因为缺少主id,对象无法正确构建
(不工作)想法:使用合并参数创建自己的属性
尝试实现如下内容:
public async Task<IActionResult> Post([FromMultiple(Merge.FromBody, Merge.FromRoute)]DetailCreateRequest request)
它尝试将主体反序列化为所需的对象类型,然后尝试应用来自可用值提供程序的其他值。为了让这个模型绑定器正常工作,我必须用ModelBinderAttribute修饰目标类,并将MasterId设置为内部,这样swagger就不会宣布它,JsonConvert也不会反序列化它:
[ModelBinder(BinderType = typeof(MergeBodyAndValueProviderBinder))]
public class DetailCreateRequest
{
internal int MasterId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
在我的控制器中,动作方法参数仍然包含[FromBody]
标志,因为swagger使用它来宣布如何调用该方法,但它永远不会被调用,因为我的模型绑定器具有更高的优先级
public async Task<IActionResult> Post([FromBody]DetailCreateRequest request)
public异步任务发布([FromBody]DetailCreateRequest)
因此,它并不完美,但迄今为止效果良好。这看起来是一个正确的选择:
[HttpPost]
[Produces(typeof(DetailsResponse))]
public async Task<IActionResult> Post([FromRoute]int masterId, [FromBody]DetailCreateRequest request) {
//...
}
[HttpPost]
[产生(类型(细节响应))]
公共异步任务Post([FromRoute]int masterId[FromBody]DetailCreateRequest请求){
//...
}
但是,如果您在域模型验证方面遇到一些问题,请创建没有主Id的自定义Dto对象。
否则,您可以使用自定义模型绑定器,然后使用来自操作和绑定上下文的参数
public async Task<IActionResult> Post([FromBody]DetailCreateRequest request)
[HttpPost]
[Produces(typeof(DetailsResponse))]
public async Task<IActionResult> Post([FromRoute]int masterId, [FromBody]DetailCreateRequest request) {
//...
}