代码之家  ›  专栏  ›  技术社区  ›  Joe

具有继承的web api版本控制

  •  2
  • Joe  · 技术社区  · 6 年前

    我正试图让web api版本控制与继承的类一起工作。我正在研究两种非常简单的股票 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 回复  |  直到 6 年前
        1
  •  2
  •   Nkosi    6 年前

    您需要做的是覆盖 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);
        }
    }
    

    完成后,您将需要在web 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");
        }
    } 
    

    您将注意到子级中的路由未用于重写操作,因为它也将从基本控制器继承。