Маршрут сломан под MVC3 - PullRequest
       15

Маршрут сломан под MVC3

2 голосов
/ 22 октября 2011

У меня есть этот маршрут, который работал до того, как мы обновили наше приложение до 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, а не идентификатор, указанный в URL. Это поведение настраивается?

РЕДАКТИРОВАТЬ 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 в качестве параметра моего метода).

Вот ссылка на мое решение для репродукции: http://www.mediafire.com/?77881176saodnxp

1 Ответ

4 голосов
/ 22 октября 2011

Нет, ничего не изменилось в этом отношении. Это те вопросы, для которых мне нравится называть 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-запрос отправляется действию CreateServiceItem контроллера Items, который возвращает строку id = 3, и именно эта строка получает предупреждение.

Вывод: либо вы не показали свой реальный код, либо проблема, как я бы хотел сказать, в вашем телевизоре.


UPDATE:

Теперь, когда вы опубликовали воспроизводимый пример, я вижу, в чем проблема и чем она отличается от моего тестового примера. В моем тестовом примере я выполнил запрос следующим образом:

$.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. Так работает связыватель модели: сначала он выглядит в данных POST. В ASP.NET MVC 2 это работало нормально, потому что не было JsonValueProviderFactory, поэтому в запросе POST не было параметра id. В ASP.NET MVC 3 это было введено, и теперь есть значение для этого.

Обходной путь, который я предлагаю вам, - обернуть ваш JSON в другой объект.

...