C# 如何:从多个源绑定参数

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

目前,我正在尝试创建一个基于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' \
--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) {
    //...
}