为什么我的属性会被触发所有操作,包括那些没有属性的操作?

时间:2016-04-01 08:40:57

标签: c# asp.net asp.net-mvc custom-attributes

我的网络API中有一个控制器。我们称之为TimeController

我有GET行动和PUT行动。它们看起来像这样:

public class TimeController : ApiController
{
    [HttpGet]
    public HttpResponseMessage Get()
    {
        return Request.CreateResponse(HttpStatusCode.OK, DateTime.UtcNow);
    }

    [HttpPut]
    public HttpResponseMessage Put(int id)
    {
        _service.Update(id);
        return Request.CreateResponse(HttpStatusCode.OK);
    }
}

我还有一个路线配置如下:

routes.MapHttpRoute("DefaultApi", "{controller}/{id}", new { id = RouteParameter.Optional });

所以我可以安静地访问它。

现在,我还想使用自定义Route属性对GET操作进行版本控制。我使用的代码与Richard Tasker在此blog post中谈到的代码非常相似。

(不同之处在于我使用正则表达式从accept头获取版本。其他所有内容都差不多)

所以我的控制器现在看起来像这样:

public class TimeController : ApiController
{
    private IService _service;

    public TimeController(IService service)
    {
        _service = service;
    }

    [HttpGet, RouteVersion("Time", 1)]
    public HttpResponseMessage Get()
    {
        return Request.CreateResponse(HttpStatusCode.Ok, DateTime.UtcNow);
    }

    [HttpGet, RouteVersion("Time", 2)]
    public HttpResponseMessage GetV2()
    {
        return Request.CreateResponse(HttpStatusCode.Ok, DateTime.UtcNow.AddDays(1));
    }

    [HttpPut]
    public HttpResponseMessage Put(int id)
    {
        _service.Update(id);
        return Request.CreateResponse(HttpStatusCode.OK);
    }
}

但是,现在当我尝试访问PUT端点时,我从服务器获得了404响应。如果我在调试模式中单步执行代码,我可以看到RouteVersion属性被触发,即使我还没有用它来修饰动作。

如果我将属性添加到版本为1的PUT操作,或者我添加内置的Route属性,如下所示:Route("Time")那么它可以正常工作。

所以我的问题是:即使我没有用它来装饰动作,为什么属性会被触发?

编辑:以下是属性的代码:

public class RouteVersion : RouteFactoryAttribute
{
    private readonly int _allowedVersion;

    public RouteVersion(string template, int allowedVersion) : base(template)
    {
        _allowedVersion = allowedVersion;
    }

    public override IDictionary<string, object> Constraints
    {
        get
        {
            return new HttpRouteValueDictionary
            { 
                {"version", new VersionConstraint(_allowedVersion)}
            };
        }
    }
}

public class VersionConstraint : IHttpRouteConstraint
{
    private const int DefaultVersion = 1;
    private readonly int _allowedVersion;
    public VersionConstraint(int allowedVersion)
    {
        _allowedVersion = allowedVersion;
    }

    public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
    {
        if (routeDirection != HttpRouteDirection.UriResolution)
        {
            return true;
        }
        int version = GetVersionFromHeader(request) ?? DefaultVersion;
        return (version == _allowedVersion);
    }

    private int? GetVersionFromHeader(HttpRequestMessage request)
    {
        System.Net.Http.Headers.HttpHeaderValueCollection<System.Net.Http.Headers.MediaTypeWithQualityHeaderValue> acceptHeader = request.Headers.Accept;
        var regularExpression = new Regex(@"application\/vnd\.\.v([0-9]+)",
            RegexOptions.IgnoreCase);

        foreach (var mime in acceptHeader)
        {
            Match match = regularExpression.Match(mime.MediaType);
            if (match.Success)
            {
                return Convert.ToInt32(match.Groups[1].Value);
            }
        }
        return null; 
    }
}

Edit2 :我认为有一些混乱,所以我更新了Put动作以匹配路线配置

3 个答案:

答案 0 :(得分:1)

  

所以我的问题是:即使我没有用它装饰动作,为什么属性会被触发?

从您尝试访问PUT端点时的问题表达方式以及它与GET操作匹配(然后随后运行其约束)的事实很清楚 你有没有向服务器发出PUT请求 。大多数浏览器都无法发出PUT请求,您需要一段代码或脚本来执行此操作。

实施例

using (var client = new System.Net.WebClient())
{
    // The byte array is the data you are posting to the server
    client.UploadData(@"http://example.com/time/123", "PUT", new byte[0]);
}

参考:How to make a HTTP PUT request?

答案 1 :(得分:0)

我认为这是因为你的行动签名与默认路线相结合

在默认路由中,您将Id属性指定为可选,但在您的操作中,您使用参数days,在这种情况下,框架无法解析它。您必须将其添加为查询字符串参数,例如:

?days={days}

或者更改签名以接受id作为输入。

由于它无法在网址中使用天数恢复操作,因此会返回404

我个人不使用默认路由,并始终使用属性路由来防止这种行为

答案 2 :(得分:0)

  

所以我的问题是:即使我没有用它装饰动作,为什么属性会被触发?

任何没有路由属性的控制器方法都使用基于约定的路由。这样,您可以在同一个项目中组合两种类型的路由。

请看这个链接: attribute-routing-in-web-api-2

此外,由于方法未使用route属性进行修饰,因此当Web API框架收到HTTP请求时,它会尝试将URI与路由表中的某个路由模板进行匹配。如果没有路由匹配,则客户端收到404错误。这就是你得到404

的原因

请同时查看此内容:Routing in ASP.NET Web API