【问题标题】:Why does ASP.NET Web Api model binding uses the parameter type to determine the source of the value?为什么 ASP.NET Web Api 模型绑定使用参数类型来确定值的来源?
【发布时间】:2013-11-29 02:35:19
【问题描述】:

几天以来,我一直在尝试创建自己的 Web api 控制器。根据其他约定,我需要使用发布请求来创建对象。具体来说,我有这个控制器有这个动作:

public class ReservationController : ApiController
{
    [HttpPost]
    public void Create(int roomId, DateTime arrivalDate)
    {
      //do something with both parameters   
    }
}

当我向它发出 post 请求时,此代码不起作用,我收到如下 404 异常:

在控制器“Some”上找不到与请求匹配的操作。

原因是从查询字符串中读取简单类型,从正文中读取复杂类型,根据aricle。 Web api 使用参数将操作与请求匹配,因此无法将我的操作映射到请求。

我确实知道我可以使用 [frombody] 标签,但您只能将其应用于一个参数,而我有 2 个。我也知道我可以创建一个包含两个参数的包装器对象,但我是不愿意为我的所有电话使用包装器。

所以我知道我可以通过这些方法解决这个问题。我也认为这是由于 post 请求的正文只能读取一次造成的。但我的实际问题是:

为什么参数的来源是由它的类型而不是由它的可用性决定的,尤其是当约定规定您应该发出例如创建的 post 请求时?在MVC中是这样,为什么web api中没有呢?

最好的问候,

BHD

最终更新 由于我得到了一些支持,可能更多的人面临同样的问题。最后是这样的:Web-Api != MVC。这根本不是一回事,我猜 web api 团队做出了与 mvc 团队不同的设计决策。

【问题讨论】:

    标签: c# asp.net rest asp.net-web-api model-binding


    【解决方案1】:

    您实际上可以直接在 WebAPI 中直接执行此操作,至少在 2.2(.Net 版本 4.5.2)中。你的控制器是正确的。使用您的控制器,如果您使用这样的 HTTP POST 调用它(通过 Fiddler 测试):

    http://localhost:58397/api/Reservation?roomId=123&arrivalDate=2015-12-17
    

    您将获得 roomId = 123 和arrivalDate = 17.12.2015 的正确值。

    我怀疑您对 WebAPI 的调用有问题。如果您仍然无法正常工作,可以发布该电话。

    【讨论】:

    • 我的全部观点是,参数来自哪里(查询字符串或正文)无关紧要,您仍在将参数添加到查询字符串中。在 mvc 中,我也可以将它们放在 body 中,但在 api 项目中我不能。但正如我得出的结论,mvc 和 api 库是不一样的(不幸的是)
    • @BlackHawkDesign:您是说您希望控制器从其中任何一个中获取参数,以便您可以首先仅使用查询字符串参数调用该方法,然后仅使用正文参数单独调用该方法,并且最终采用相同的方法并得到相同的结果?
    • 我确实说它应该从任何一个中选择(这就是 mvc 的工作原理),但不是因为你提到的原因。但是参数的类型不应该确定来源。 (我的问题是它为什么起作用)。
    【解决方案2】:

    您可以使用参数上的[FromBody] 属性强制它从HTTP POST 的正文而不是Uri 读取。这与 [FromUri] 属性相反。

    [HttpPost]
    public void SomeAction([FromBody] int id)
    {
        //do something with id    
    }
    

    您确定您实际上是在将 id 放入正文中吗?也可能是路由问题。如果这仍然不起作用,那么也许您应该使用Fiddler 并在此处复制您的 HTTP 消息的 RAW 输出。

    如果您将多个值打包到正文中,例如使用 JSON,那么您应该使用应该自动反序列化为的模型:

    public class PostModel
    {
        public int ID { get; set; }
        public int SomeOtherID { get; set; }
    }
    
    [HttpPost]
    public void SomeAction(PostModel postModel)
    {
        //do something with postModel.ID and postModel.SomeOtherID
    }
    

    【讨论】:

    • 这可能适用于这个简单的示例,但是当我有 2 个参数时,我只能应用该标签一次,应用它两次会导致错误:无法绑定多个参数('id' 和 'id2 ') 到请求的内容。
    • 你不能应用它两次,因为你没有两个 HTTP 正文。你只有 一个 身体。如果您将多个值打包到正文中,那么 1. 您是如何做到的? JSON?格式很重要。 2. 您需要创建一个模型对象来反序列化这个复杂的主体。我会用一个例子来更新我的答案。
    • 我认为真正的问题是您如何发送请求以及为什么不使用查询字符串变量?这很容易做到。如果您不确定如何修改,请发布您用于提交 POST 消息的代码。
    • 感谢您添加反序列化示例。如果我没有偶然发现这一点,我仍然会用头敲击键盘。猜猜我是唯一认为这种设计是脑损伤的人。 (叹息)。
    【解决方案3】:

    您似乎对 Web API 的实际工作方式存在根本性的误解。

    Web API 路由是由冗长而不是方法名称驱动的。 “SomeMethod”实际上将 Web API 的有用信息转化为零。结果,如果我发帖

    api/some/some?id=1
    

    api/some/somemethod?id=1
    

    甚至

    api/some/?id=1
    

    而 SomeMethod 端点是唯一可用的 POST,它将到达该端点。

    因此,首先,确保您在该 api 控制器上只有一个 POST。如果这样做,使用上述任一查询字符串从任何测试客户端向其 POST 都可以正常工作。

    【讨论】:

    • 如果有多个 POST 方法,我认为他会收到一个不同的错误,指出找到了多个匹配项。不是他遇到的错误。
    • @TrevorElliott 这是可能的,但我也无法在本地重现他的错误。我的猜测是他正在向一个看起来像 api/some/somemethod/1 或 api/some/somemethod/?id=1 的 url 发送调用,这两者都不是 Web API 中的正确路由
    • 我知道这一切,请查看我更新的问题。可能不清楚。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-10
    • 1970-01-01
    • 2012-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多