Управление версиями веб-API с наследованием - PullRequest
0 голосов
/ 12 июня 2018

Я пытаюсь заставить работать версии API Web с унаследованным классом.Я работаю с двумя очень простыми вариантами стандартного Values контроллера.

[ApiVersion("1.0")]
[RoutePrefix("api/v{version:apiVersion}/Values")]
[ControllerName("Values")]
public class ValuesController : ApiController
{
    // GET api/values
    [Route("")]
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }

    // GET api/values/5
    [Route("{id:int}")]
    public virtual string Get(int id)
    {
        return "value from 1";
    }
}

[ApiVersion("2.0")]
[RoutePrefix("api/v{version:apiVersion}/Values")]
[ControllerName("Values")]
public class Values2Controller : ValuesController
{
    //Want to use the method in the base class
    //public IEnumerable<string> Get()
    //{
    //    return new string[] { "value2-1", "value2-2" };
    // }

    [Route("{id:int}")]
    // GET api/values/5
    public new string Get(int id)
    {
        return "value from 2";
    }
} 

Моя начальная конфигурация также довольно проста.

public static void Register(HttpConfiguration config)
{
    var constraintResolver = new DefaultInlineConstraintResolver()
    {
        ConstraintMap = {["apiVersion"] = typeof(ApiVersionRouteConstraint)}
    };
    config.MapHttpAttributeRoutes(constraintResolver);
    config.AddApiVersioning(o => { o.AssumeDefaultVersionWhenUnspecified = true; });
}

Не переопределенные маршруты работают точнокак и следовало ожидать http://localhost:32623/api/v1.0/Values/12 -> "значение от 1" http://localhost:32623/api/v2.0/Values/12 -> "значение от 2"

Вызов v1 по умолчанию Get Маршрут http://localhost:32623/api/v1.0/Values -> Значение1, Значение2

Однако попытка использовать тот же маршрут на дочернем контроллере приводит к ошибке.

http://localhost:32623/api/v2.0/Values

<Message>
The HTTP resource that matches the request URI 'http://localhost:32623/api/v2.0/Values' does not support the API version '2.0'.
</Message>
<InnerError>
<Message>
No route providing a controller name with API version '2.0' was found to match request URI 'http://localhost:32623/api/v2.0/Values'.
</Message>
</InnerError>

В сообщении об ошибке указывается, что переопределенный элемент ожидает "1.0", и я могу обойти это с помощью метода, подобного этому, в дочернем классе.

[Route("")]
public override IEnumerable<string> Get()
{
    return base.Get();
}

Но это кажется далеко не идеальным для больших приложений.Есть ли способ заставить эту работу работать так, как мне хотелось бы, без этих «пустых» переопределений?

1 Ответ

0 голосов
/ 15 июня 2018

Вам нужно переписать DefaultDirectRoutePrivider, чтобы разрешить наследование маршрута:

public class WebApiCustomDirectRouteProvider : DefaultDirectRouteProvider {
    protected override IReadOnlyList<IDirectRouteFactory>
        GetActionRouteFactories(HttpActionDescriptor actionDescriptor) {
        // inherit route attributes decorated on base class controller's actions
        return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>(inherit: true);
    }
}

И после этого вам нужно будет настроить его в конфигурации веб-API вместе с пользовательским маршрутом.ограничение

public static void Register(HttpConfiguration config) {
    var constraintResolver = new DefaultInlineConstraintResolver() {
        ConstraintMap = {["apiVersion"] = typeof(ApiVersionRouteConstraint)}
    };
    var directRouteProvider = new WebApiCustomDirectRouteProvider();
    // Attribute routing. (with inheritance)
    config.MapHttpAttributeRoutes(constraintResolver, directRouteProvider);
    config.AddApiVersioning(_ => { _.AssumeDefaultVersionWhenUnspecified = true; });
}

Так что теперь у контроллера-наследующего значения теперь будут доступны базовые маршруты в производных контроллерах

Для демонстрационных целей

[ApiVersion("1.0")]
[RoutePrefix("api/v{version:apiVersion}/Values")]
[ControllerName("Values")]
public class ValuesController : ApiController {

    [HttpGet]
    [Route("")] // GET api/v1.0/values
    public virtual IHttpActionResult Get() {
        return Ok(new string[] { "value1", "value2" });
    }

    [HttpGet]
    [Route("{id:int}")] // GET api/v1.0/values/5
    public virtual IHttpActionResult Get(int id) {
        return Ok("value from 1");
    }
}

[ApiVersion("2.0")]
[RoutePrefix("api/v{version:apiVersion}/Values")]
[ControllerName("Values")]
public class Values2Controller : ValuesController {

    //Will have inherited GET "api/v2.0/Values" route

    // GET api/v2.0/values/5 (Route also inherited from base controller)
    public override IHttpActionResult Get(int id) {
        return Ok("value from 2");
    }
} 

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

...