如何在ASP.NET MVC路由中使用HttpMethodConstraint的自定义约束?

时间:2010-01-28 23:26:13

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

我的控制器只接受此网址上的POST:

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 Forbidden:

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

但是,如果我删除json = new JsonConstraint()约束,它可以正常工作。有人知道我做错了吗?

2 个答案:

答案 0 :(得分:8)

我把它放在评论中,但没有足够的空间。

编写自定义约束时,检查routeDirection参数并确保逻辑仅在正确的时间运行非常重要。

该参数告诉您在处理传入请求时是运行约束还是在某人生成URL时运行(例如当他们调用Html.ActionLink时)。

在您的情况下,我认为您希望将所有匹配的代码放在一个巨大的“if”中:

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

答案 1 :(得分:4)

我会调试JsonConstraint并查看内容类型是什么。

无论出于何种原因,它可能不是application/json

我知道那是RFC MIME类型,但是我已经看到了其他一些在我的时间流动的其他类型(例如text/x-json),正如previous question中提到的那样。 / p>

另外,我从未见过ContentType约束,所以我有兴趣看看它是否有效。您是否尝试过使用其他MIME类型以防它出错?

最后,我不是只有一个JsonConstraint,而是创建一个通用的ContentTypeConstraint。

<强>更新

我在使用ContentTypeConstraint代码的路由上拼凑了一个快速WebRequest方法,这似乎可以正常工作。

<强>枚举

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。此外,如果您在枚举类中使用描述属性,则可以取消切换案例。