在MVC3下破解的路由

时间:2011-10-21 20:28:46

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

在我们将应用程序升级到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命令到达以下URL:

/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
  });
}

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

以下链接指向我的repro解决方案:http://www.mediafire.com/?77881176saodnxp

1 个答案:

答案 0 :(得分:4)

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

步骤:

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

    public class ItemsController : Controller
    {
        [ActionName("Index")]
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult CreateServiceItem(int id)
        {
            return Content(id.ToString());
        }
    }
    
  4. 修改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 }
        );
    }
    
  5. 修改~/Views/Home/Index.cshtml视图,使其如下所示:

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

  7. 正如预期的那样,默认浏览器启动时,会向CreateServiceItem控制器的Items操作发送一个AJAX请求,该操作返回id = 3字符串,此字符串会被警告。< / p>

  8. 结论:要么你没有展示你的实际代码,要么就像我想说的那样,问题出在你的电视上。


    更新:

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

    $.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的值。这就是模型绑定器的工作原理:它首先查看POSTed数据。在ASP.NET MVC 2中,这工作正常,因为没有JsonValueProviderFactory因此POST请求中没有id参数。在ASP.NET MVC 3中引入了这个,现在它有一个值。

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