Маршрут, который соответствует другому маршруту, игнорируя HttpMethodConstraint? - PullRequest
4 голосов
/ 02 марта 2011

У меня есть сайт ASP.net MVC 3 с такими маршрутами:

routes.MapRoute("Get", "endpoint/{id}",
    new { Controller = "Foo", action = "GetFoo" },
    new { httpMethod = new HttpMethodConstraint("GET") });

routes.MapRoute("Post", "endpoint/{id}",
    new { Controller = "Foo", action = "NewFoo" },
    new { httpMethod = new HttpMethodConstraint("POST") });

routes.MapRoute("BadFoo", "endpoint/{id}",
    new { Controller = "Error", action = "MethodNotAllowed" });

routes.MapRoute("NotFound", "", 
    new { controller = "Error", action = "NotFound" });

Итак, в двух словах, у меня есть Маршрут, который совпадает на некоторых HTTP-глаголах, таких как GET и POST, но на других HTTP-глаголах, таких как PUT и DELETE, он должен возвращать конкретную ошибку.

Мой маршрут по умолчанию - 404.

Если я удаляю маршрут "BadFoo", то PUT против конечной точки / {id} возвращает 404, потому что ни один из других маршрутов не совпадает, поэтому он идет к моему маршруту NotFound.

Дело в том, что у меня есть тонна маршрутов, таких как Get и Post, где у меня есть HttpMethodConstraint и где мне нужно будет создать маршрут, такой как маршрут BadFoo, просто чтобы поймать правильное совпадение в строке маршрута, но не в методе. , который взрывает мой маршрут без необходимости.

Как настроить маршрутизацию только с маршрутами Get, Post и NotFound, при этом различая HTTP 404 не найден (= недействительный URL) и HTTP 405 Метод недопустим (= действительный URL, неправильный HTTP метод)?

1 Ответ

4 голосов
/ 04 марта 2011

Вместо использования ограничения маршрута вы можете делегировать проверку метода HTTP во время выполнения MVC, используя пользовательские атрибуты ControllerActionInvoker и ActionMethodSelector, такие как [HttpGet], [HttpPost] и т. Д.:

using System;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MvcApplication6.Controllers {

   public class HomeController : Controller {

      protected override IActionInvoker CreateActionInvoker() {
         return new CustomActionInvoker();
      }

      [HttpGet]
      public ActionResult Index() {
         return Content("GET");
      }

      [HttpPost]
      public ActionResult Index(string foo) {
         return Content("POST");
      }
   }

   class CustomActionInvoker : ControllerActionInvoker {

      protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName) {

         // Find action, use selector attributes
         var action = base.FindAction(controllerContext, controllerDescriptor, actionName);

         if (action == null) {

            // Find action, ignore selector attributes
            var action2 = controllerDescriptor
               .GetCanonicalActions()
               .FirstOrDefault(a => a.ActionName.Equals(actionName, StringComparison.OrdinalIgnoreCase));

            if (action2 != null) {
               // Action found, Method Not Allowed ?
               throw new HttpException(405, "Method Not Allowed");
            }
         }

         return action;
      }
   }
}

Обратите внимание на мой последний комментарий «Действие найдено, метод не разрешен?», Я написал это как вопрос, потому что могут быть ActionMethodSelector атрибуты, не связанные с проверкой метода HTTP ...

...