У меня есть контроллер в моем веб-api. Позвольте называть его TimeController
.
У меня есть действие GET
и a 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 });
поэтому я могу получить доступ к нему спокойным образом.
Теперь я также хочу выполнить действие GET
с использованием настраиваемого атрибута Route. Я использую код, очень похожий на то, о чем говорит Ричард Таскер в этом сообщении .
(разница заключается в том, что я использую регулярное выражение для получения версии из заголовка 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
запускается, хотя я не украсил его.
Если я добавлю атрибут в действие PUT с версией 1 или добавлю встроенный атрибут 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 для соответствия конфигурации маршрута