API веб-API 2 не распознает несколько атрибутов для маршрутизации (управление версиями)

Я пытаюсь реализовать как маршрутизацию атрибутов и VersionedRoute из примера RoutingConstaints, но когда я использую оба на контроллере, атрибут версионности больше не работает.

Что мне нужно изменить в атрибуте, чтобы он хорошо сочетался с маршрутизацией атрибутов?

Для примера кода загрузите пример проекта (или просто посмотрите на несколько файлов по ссылке выше), а затем измените маршруты следующим образом:

// When I use the RoutePrefix, VersionedRoute no longer works (Sending "Api-Version" through http header doesn't route correctly
// If I remove the RoutePrefix I can use VersionedRoute again
// What do I need to change in its code to be able to use both?

[VersionedRoute("api/Customers", 1)] // This route would be used as http://url/api/customers with a header of "api-version: 1"
[RoutePrefix("api/v1/Customers")] // This route would be used purely through url versioning of http://url/api/v1/Customers
public class CustomersV1Controller : ApiController {

    /* Other stuff removed */

    [VersionedRoute("api/Customer", 1)] // I'd rather not have to use this here at all and just use a single one on the class, but having both nor just one on either works right now.
    [Route("")]
    public IHttpActionResult Get()
    {
        return Json(_customers);
    }
}

VersionedRoute Code

VersionConstraint Code

Изменить: Пожалуйста, дайте мне знать, если вам нужно больше информации или даже постить идеи или вещи, чтобы попробовать:)

Edit2: вот пример того, что я пытаюсь сделать из блога Троя Ханта: http://www.troyhunt.com/2014/02/your-api-versioning-is-wrong-which-is.html

Edit3: Вот то, что я хотел бы закодировать, чтобы быть как можно ближе, так как это уменьшило бы много накладных и магических строк.

[VersionedRoute("api/Customers", 1)] // This route would be used as http://url/api/customers with a header of "api-version: 1"
[RoutePrefix("api/v1/Customers")] // This route would be used purely through url versioning of http://url/api/v1/Customers
public class CustomersV1Controller : ApiController {

    /* Other stuff removed */
    [Route("")]
    public IHttpActionResult Get()
    {
        // Removed
        return Ok(customers);
    }


    [Route("{id:int}")]
    public IHttpActionResult GetById(int id)
    {
        // Removed
        return Ok(customer);
    }
}

[VersionedRoute("api/Customers", 2)] // This route would be used as http://url/api/customers with a header of "api-version: 2"
[RoutePrefix("api/v2/Customers")] // This route would be used purely through url versioning of http://url/api/v2/Customers
public class CustomersV2Controller : ApiController {

    /* Other stuff removed */
    [Route("")]
    public IHttpActionResult Get()
    {
        // Removed
        return Ok(customersThatAreDifferentThanV1);
    }


    [Route("{id:int}")]
    public IHttpActionResult GetById(int id)
    {
        // Removed
        return Ok(customerThatIsDifferent);
    }
}

Редактировать: последний удар, пытаясь записать информацию о версии маршрута только один раз для маршрута, на уровне атрибута контроллера, а не для каждого действия.

1 ответ

Route а также VersionedRoute атрибуты работают хорошо вместе, но ваш RoutePrefix Атрибут также применяется к вашему VersionedRoute (попробуйте зайти в /api/v1/Customers/api/Customer - вы получите ответ, когда установлен заголовок api-версии)

Следующий код будет производить желаемое поведение в отношении двух URL-адресов в вашем примере, возвращая правильные ответы, но, очевидно, это не решит проблему с желанием получить один из них. VersionedRoute и один RoutePrefix на вершине класса. Для этого потребуется другой подход. Однако вы можете иметь отдельные контроллеры для разных версий API.

[RoutePrefix("api")]
public class CustomersV1Controller : ApiController
{
    /* Other stuff removed */

    [VersionedRoute("Customers", 1)]
    [Route("v1/Customers")]
    public IHttpActionResult Get()
    {
        return Json(_customers);
    }
}

Улучшение было бы создать свой собственный атрибут вместо Route так что вам не нужно каждый раз ставить префикс версии:

public class CustomVersionedRoute : Attribute, IHttpRouteInfoProvider
{
    private readonly string _template;

    public CustomVersionedRoute(string route, int version)
    {
        _template = string.Format("v{0}/{1}", version, route);
    }

    public string Name { get { return _template; } }
    public string Template { get { return _template ; } }
    public int Order { get; set; }
}

[RoutePrefix("api")]
public class CustomersV2Controller : ApiController
{
    /* Other stuff removed */

    [VersionedRoute("Customers", 2)]
    [CustomVersionedRoute("Customers", 2)]
    public IHttpActionResult Get()
    {
        return Json(_customers);
    }
}
Другие вопросы по тегам