Как использовать пользовательское ограничение с HttpMethodConstraint в маршрутизации ASP.NET MVC? - PullRequest
8 голосов
/ 29 января 2010

У меня есть контроллер, который принимает POST только для этого URL:

POST http://server/stores/123/products

POST должен иметь тип контента application/json, поэтому у меня в таблице маршрутизации есть следующее:

routes.MapRoute(null,
                "stores/{storeId}/products",
                new { controller = "Store", action = "Save" },
                new {
                      httpMethod = new HttpMethodConstraint("POST"),
                      json = new JsonConstraint()
                    }
               );

Где JsonConstraint:

public class JsonConstraint : IRouteConstraint
{
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        return httpContext.Request.ContentType == "application/json";
    }
}

Когда я использую маршрут, я получаю 405 Запрещено:

The HTTP verb POST used to access path '/stores/123/products' is not allowed

Однако, если я уберу ограничение json = new JsonConstraint(), оно будет работать нормально. Кто-нибудь знает, что я делаю не так?

Ответы [ 2 ]

8 голосов
/ 29 января 2010

Я бы добавил это в комментарии, но не хватает места.

При написании пользовательского ограничения очень важно проверить параметр routeDirection и убедиться, что ваша логика работает только в нужное время.

Этот параметр указывает, выполняется ли ваше ограничение при обработке входящего запроса или когда кто-то генерирует URL (например, когда он вызывает Html.ActionLink).

В вашем случае, я думаю, вы хотите поместить весь свой соответствующий код в гигантское "если":

public bool Match(HttpContextBase httpContext, Route route,
    string parameterName, RouteValueDictionary values,
    RouteDirection routeDirection) 
{
    if (routeDirection == RouteDirection.IncomingRequest) {
        // Only check the content type for incoming requests
        return httpContext.Request.ContentType == mimeType; 
    }
    else {
        // Always match when generating URLs
        return true;
    }
}
4 голосов
/ 29 января 2010

Я бы отладил JsonConstraint и посмотрел бы, какой тип контента.

Возможно, что по какой-то причине это может быть не application/json.

Я знаю, что это тип RFC MIME, но я видел несколько других, плавающих в моем времени (например, text/x-json), как упоминалось здесь в предыдущем вопросе .

Кроме того, я никогда не видел ограничения ContentType, поэтому мне было бы интересно посмотреть, работает ли оно. Вы пробовали это с другими типами MIME на случай, если он неисправен?

И, наконец, вместо того, чтобы иметь только одну JsonConstraint, я бы создал общий ContentTypeConstraint.

Обновление:

Я собрал быстрый метод WebRequest на маршруте, который использует код ContentTypeConstraint и который, кажется, работает правильно.

Enum

public enum ConstraintContentType
{
  XML,
  JSON,
}

Класс ограничений

public class ContentTypeConstraint : IRouteConstraint
{
  private string mimeType;

  public ContentTypeConstraint(ConstraintContentType constraintType)
  {
    //FYI: All this code could be redone if you used the Description attribute, and a ToDescription() method.
    switch (constraintType)
    {
      case ConstraintContentType.JSON:
        mimeType = "application/json";
        break;
      case ConstraintContentType.XML:
        mimeType = "text/xml";
        break;
      default:
        mimeType = "text/html";
        break;
    }
  }

  public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
  {
    //As suggested by Eilon
    if (routeDirection == RouteDirection.UrlGeneration)
      return true;

    return httpContext.Request.ContentType == mimeType;
  }
}

Это будет вызвано, на вашем примере, как:

contentType = new ContentTypeConstraint(ConstraintContentType.JSON)

Это ограничение использовалось много раз для большего, чем просто JSON. Кроме того, с делом переключения можно покончить, если вы используете атрибуты description в классе enum.

...