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动作以匹配路由配置
我认为这是因为你的操作签名与默认路由相结合
在默认路由中,您将 Id 属性指定为可选,但是在您的操作中,您使用参数 days,在这种情况下,框架无法解析它。您必须将其添加为查询字符串参数,例如:
?days={days}
Run Code Online (Sandbox Code Playgroud)
或者更改签名以接受 id 作为输入。
由于它无法解析 url 中包含天数的操作,因此将返回 404
就我个人而言,我不使用默认路由,并且始终使用属性路由来防止这种行为