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

Pie*_*arg 11 c# asp.net asp.net-mvc custom-attributes

我的web 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我也有一个路由配置如下:

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

所以我可以以一种宁静的方式访问它.

现在,我还想GET使用自定义Route属性对操作进行版本控制.我使用的代码与Richard Tasker在这篇博客文章中谈到的代码非常相似.

(不同之处在于我使用正则表达式从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);
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,现在当我尝试访问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; 
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Edo*_*ost 0

我认为这是因为你的操作签名与默认路由相结合

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

?days={days}
Run Code Online (Sandbox Code Playgroud)

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

由于它无法解析 url 中包含天数的操作,因此将返回 404

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