Почему мой атрибут запускается во всех действиях, включая те, у которых нет атрибута?
У меня есть контроллер в моем веб-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 для соответствия конфигурации маршрута
Ответы
Ответ 1
Итак, мой вопрос: почему срабатывает атрибут, хотя я не украсил его?
Как видно из вашего вопроса, "когда я пытаюсь получить доступ к конечной точке PUT" и того факта, что он соответствует действию GET (а затем впоследствии запускает его ограничение), , вы не выпустили PUT запрос на сервер. Большинство браузеров не могут выдавать запрос PUT, для этого вам нужен фрагмент кода или script.
Пример
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]);
}
Ссылка: Как сделать запрос HTTP PUT?
Ответ 2
Я думаю, что это из-за вашей сигнатуры действия в сочетании с маршрутом по умолчанию
В вашем маршруте по умолчанию вы указываете атрибут идентификатора как необязательный, однако в вашем действии вы используете дни параметра, в этом случае среда не может его решить. вам либо нужно добавить его как параметр строки запроса, например:
?days={days}
Или измените подпись, чтобы принять id как входной.
Так как он не может изменить действие с днями в URL-адресе, он вернет 404
Лично я не использую маршруты по умолчанию и всегда использую маршрутизацию атрибутов, чтобы предотвратить это поведение
Ответ 3
Итак, мой вопрос: почему срабатывает атрибут, хотя я не украсил его?
Любые методы контроллера, не имеющие атрибута маршрута, используют маршрутизацию на основе условных обозначений. Таким образом, вы можете комбинировать оба типа маршрутизации в одном проекте.
Пожалуйста, просмотрите эту ссылку:
attribute-routing-in-web-api-2
Также как метод не украшен атрибутом маршрута, когда среда веб-API получает HTTP-запрос, он пытается сопоставить URI с одним из шаблонов маршрутов в таблице маршрутизации. Если маршрут не совпадает, клиент получает ошибку 404. Вот почему вы получаете 404
См. также этот раздел: Маршрутизация в веб-интерфейсе ASP.NET