【问题标题】:Route broken under MVC3在 MVC3 下路由中断
【发布时间】:2011-10-21 20:28:46
【问题描述】:

在我们将应用程序升级到 MVC3 之前,我有这条路线有效:

routes.MapRoute(
  "Versioned API with Controller, ID, subentity",
  "api/{version}/{controller}/{id}/{subentity}/",
  new { controller = "API", action = "Index" },
  new { id = @"\d+", subentity = "serviceitem|accessitem" }
),

我正在尝试通过 POST 到以下网址来访问这条路线:

/api/1.0/items/3/serviceitem

我的控制器方法有这个签名:

[ActionName("Index"), AcceptVerbs(HttpVerbs.Post)]
public ActionResult CreateServiceItem(int id)

当我尝试使用此方法时,我收到以下错误:

The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult CreateServiceItem(Int32)' in 'API.Controllers.ItemsController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter. Parameter name: parameters

在 mvc2 和 mvc3 之间是否有某种语法变化?

编辑:更新信息!

我想我找到了罪魁祸首。我将一些 JSON 数据的数据发布到 URL。我的 JSON 对象恰好如下所示:

{ Id: null, OtherProperty: 'foo' }

MVC3 正在使用我的 JSON 对象中的 ID,而不是 URL 中指定的 ID。此行为是否可配置?

编辑 2:可重现的示例:

我们在应用程序中使用 Ext,所以我的示例是使用 Ext,但我可以重现它,这是在我的 /Views/Home/Index.aspx 中:

Ext.onReady(function() {
  var w = new Ext.Window({
    title: 'Hi there',
    height: 400,
    width: 400,
    items: [
      new Ext.Button({
        text: 'Click me',
        handler: function() {
          var obj = {
            Id: null,
            Text: 'Hi there'
          };
          Ext.Ajax.request({
            url: '/item/3/serviceitem',
            method: 'POST',
            jsonData: Ext.util.JSON.encode(obj),
            headers: { 'Content-type': 'application/json' },
            success: function(result, request) {
              console.log(result);
            },
            failure: function(result, request) {
              console.log(result);
            }
          });
        }
      })
]
  });

  w.show();
});

在我的 Global.asax 中,我有以下路线:

routes.MapRoute(
  "Test",
  "{controller}/{id}/{subentity}",
  new { action = "Index" },
  new { id = @"\d+", subentity = "serviceitem" }
);

在我的 /Controllers/ItemController 中,我有这个方法:

[ActionName("Index")]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CreateServiceItem(int id)
{
  string data;
  using (StreamReader sr = new StreamReader(Request.InputStream))
  {
    data = sr.ReadToEnd();
  }
  return Json(new
  {
    DT = DateTime.Now,
    Id = id,
    PostedData = data
  });
}

当我点击按钮时,导致带有指定 JSON 数据的控制器 POST,我得到与上面相同的错误。如果我不发送 JSON 数据,它可以工作(因为 MVC 将使用 URL 部分作为我的方法的参数)。

这是我的复制解决方案的链接:http://www.mediafire.com/?77881176saodnxp

【问题讨论】:

标签: asp.net-mvc-3 asp.net-mvc-routing


【解决方案1】:

不,在这方面没有任何改变。我喜欢将这类问题称为status no repro

步骤:

  1. 使用默认模板创建一个新的 ASP.NET MVC 3 项目
  2. 在项目中添加ItemsController

    public class ItemsController : Controller
    {
        [ActionName("Index")]
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult CreateServiceItem(int id)
        {
            return Content(id.ToString());
        }
    }
    
  3. 修改Application_Start中的路由注册,如下所示:

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
        routes.MapRoute(
          "Versioned API with Controller, ID, subentity",
          "api/{version}/{controller}/{id}/{subentity}",
          new { controller = "API", action = "Index" },
          new { id = @"\d+", subentity = "serviceitem|accessitem" }
        );
    
        routes.MapRoute(
            "Default",
            "{controller}/{action}/{id}",
            new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
    
  4. 修改~/Views/Home/Index.cshtml 视图,使其如下所示:

    <script type="text/javascript">
        $.ajax({
            url: '/api/1.0/items/3/serviceitem',
            type: 'POST',
            success: function (result) {
                alert(result);
            }
        });
    </script>
    
  5. 点击 F5 运行项目

  6. 正如预期的那样,默认浏览器会启动,一个 AJAX 请求被发送到 Items 控制器的 CreateServiceItem 操作,该操作返回 id=3 字符串,并且正是该字符串得到警报。

结论:要么你没有展示你的实际代码,要么问题出在你的电视上。


更新:

现在您已经发布了一个可重现的示例,我明白了问题所在以及它与我的测试用例有何不同。在我的测试用例中,我执行了这样的请求:

$.ajax({
    url: 'item/3/serviceitem',
    type: 'POST',
    contentType: 'application/json',
    data: JSON.stringify({ model: { id: null, text: 'Hi there' } }),
    success: function (result) {
        console.log(result);
    }
});

在您的请求中,您正在这样做:

$.ajax({
    url: 'item/3/serviceitem',
    type: 'POST',
    contentType: 'application/json',
    data: JSON.stringify({ id: null, text: 'Hi there' }),
    success: function (result) {
        console.log(result);
    }
});

不再有模型名称变量。因此,当您尝试调用以下控制器操作时会发生冲突:

public ActionResult CreateServiceItem(int id)

如果模型未在您的请求中包含在适当的对象中,则模型绑定器首先从 POST 请求中选择值,因为它的优先级高于 url 的值。这就是模型绑定器的工作方式:它首先查看已发布的数据。在 ASP.NET MVC 2 中这工作正常,因为没有 JsonValueProviderFactory 所以 POST 请求中没有 id 参数。在 ASP.NET MVC 3 中引入了这一点,现在它有了价值。

我建议您的解决方法是将 JSON 包装在另一个对象中。

【讨论】:

  • 如果您在请求中发布一些 JSONData 怎么办?
  • @Jonas,啊,现在你开始谈论一些 JSON 数据。这种改变你最初问的问题。你应该已经展示了你的代码。无论如何,对我来说仍然很好。请求$.ajax({ url: '/api/1.0/items/3/serviceitem', type: 'POST', data: JSON.stringify({ model: { foo: 'bar'} }), contentType: 'application/json', success: function (result) { alert(result); } }); 和控制器动作签名:public ActionResult CreateServiceItem(int id, MyViewModel model) 和视图模型:public class MyViewModel { public string Foo { get; set; } } => 没问题。
  • 如果您的 JSON 数据包含名为“ID”的属性会怎样?
  • @Jonas,对我来说仍然可以正常工作。 id 操作参数从路由分配3,而我的视图模型Id 属性分配给我在JSON.stringify 方法中给它的任何值。所以说真的,如果您还有其他问题,请出示您的代码并解释您的问题,因为正如我所说:status no repro。
  • 但是如果你的 JSON 数据看起来像这样:{Id: null} 它还能工作吗?
猜你喜欢
  • 2012-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-03
  • 2012-04-08
  • 2012-03-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多