一、需求及原因
最近在做个mvc(本人对mvc网站这块不熟)的项目维护,增加对外开放的API接口。本来项目里是有自己使用的api接口(采用mvc默认路由方式)流程的,准备依样画瓢,但领导提出需要考虑接口的规范化和版本管理。经过了解,mvc中有Web API功能,转为无页面API接口实现,故采用该方式
原来内部api格式:Api/{controller}/{action}/{id} 示例:/Api/ClientOpenApi/AjaxClientHeart;
新api格式:Api/{controller}/v1/{action}/{id} 示例:/Api/ClientOpenApi/v1/AjaxClientHeart;
二、问题1
直接修改api路由格式,即添加版本v1段,导致原api无法识别。
解决方式:修改前部api段,新api修改为 OpenApi/{controller}/v1/{action}/{id} ,区分识别段
三、问题2
如何区分版本好,即v1段。
网上搜索相关问题时,比较方便的是在方法上利用路由特性字段标明,但貌似只有MVC5才具备??,故作罢。
后续找到利用路由选择器,通过选择器解析请求URL中的版本段内容,去匹配相关的controller。参考 https://blog.csdn.net/weixin_30344995/article/details/99484427
具体实现
(1) 编写2个controller 继承 web api特有的APIController基类,并且controller命名遵循 {controller}+V1+Controller规则,如下
public class ResourcesV1Controller : ApiController public class ResourcesV2Controller : ApiController
(2)编写一个选择器类
具体业务参考上述链接文档。其中在解析版本号时代码有做调整。原因是版本号是从URL路径中解析,而非请求参数
public class OPAPIVersionSelector:DefaultHttpControllerSelector { private HttpConfiguration _config; public OPAPIVersionSelector(HttpConfiguration config) : base(config) { _config = config; } //设计就是返回HttpControllerDesriptor的过程 public override System.Web.Http.Controllers.HttpControllerDescriptor SelectController(HttpRequestMessage request) { //获取所有的controller键值集合 var controllers = GetControllerMapping(); //获取路由数据 var routeData = request.GetRouteData(); //从路由中获取当前controller的名称 var controllerName = (string)routeData.Values["controller"]; HttpControllerDescriptor descriptor = new HttpControllerDescriptor(); //if(controllers.TryGetValue(controllerName, out descriptor)) //{ //var version = "2"; //从QueryString中获取版本 var version = GetVersionFromQueryString(request); var newName = string.Concat(controllerName, "V", version); HttpControllerDescriptor versionedDescriptor; if(controllers.TryGetValue(newName, out versionedDescriptor)) { return versionedDescriptor; } return descriptor; //} return null; } //从QueryString中获取版本 private string GetVersionFromQueryString(HttpRequestMessage request) { string sPathAdnQuery = request.RequestUri.PathAndQuery.ToLower(); if (!string.IsNullOrEmpty(sPathAdnQuery)) { var arr = sPathAdnQuery.Split('/'); if (arr.Length >= 5) { if (arr[1] == "OpenApi" && arr[3].Contains("v")) { return arr[3].Substring(1); } } } //var query = HttpUtility.ParseQueryString(request.RequestUri.Query);//此部分是要求版本作为参数传过来,而不是URL //var version = query["v"]; //if(version != null) //{ // return version; //} return "1"; } }
(3) 修改WebApiConfig.cs内容,如下
public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.Routes.MapHttpRoute( name: "OPApiV1", routeTemplate: "OpenApi/{controller}/{ver}/{action}/{id}", defaults: new { id = RouteParameter.Optional } ); //config.Routes.MapHttpRoute( // name: "OPApiV2", // routeTemplate: "api/{controller}/v2/{action}/{id}", // defaults: new { id = RouteParameter.Optional } //); //注册版本选择器 config.Services.Replace(typeof(IHttpControllerSelector), new OPAPIVersionSelector(config)); } }
(4) 在Global.asax.cs中,注册webapi
WebApiConfig.Register(GlobalConfiguration.Configuration);
(5) 请求完整URL示例:http://192.168.99.101:8090/OpenApi/Resources/v1/GetFileInfo
(6) 在controller中获取post请求的body,此处要求body为json格式,并且需要有对应的Model匹配,如下
[HttpPost] public OPResponseBaseModel<List<OPResponseUserModel>> GetUserAndDepartmentInfo([FromBody]OPRequestParamBaseModel requestModel) { //your code }
另外,本人对MVC了解有很多不足,所以上述可能会有不对或不足之处,望后续阅者指正,或自己补足