1 概要
创建ASP.NET Web Api 时模板自带Help Pages框架。
2 问题
1)使用VS创建Web Api项目时,模板将Help Pages框架自动集成到其中,使得Web Api项目引入了MVC框架开发包,使得项目看起来杂乱。
2)自带的Help Pages框架无法针对Odata控制器生成API文档。
3 问题解决方案
1)独立Help Pages项目,以插件形式添加服务
步骤1,添加类ServiceAssembliesResolver,获得服务集
- /// <summary>
- /// 获取插件服务
- /// </summary>
- public class ServiceAssembliesResolver : DefaultAssembliesResolver
- {
- public override ICollection<Assembly> GetAssemblies()
- {
- //获得已有的服务
- ICollection<Assembly> baseAssemblies = base.GetAssemblies();
- //初始化
- List<Assembly> assemblies = new List<Assembly>(baseAssemblies);
- //服务插件dll路径
- var path = WebConfigSetting.ServicesLocation;
- //加载每一个服务插件
- foreach (string file in Directory.GetFiles(path, "*.dll"))
- {
- var controllersAssembly = Assembly.LoadFrom(file);
- assemblies.Add(controllersAssembly);
- }
- return assemblies;
- }
- }
步骤2,替换现有服务
在WebApiConfig.Register方法中添加代码
- config.Services.Replace(typeof(IAssembliesResolver), new ServiceAssembliesResolver());
完整代码如下:
- namespace HY_WebApi.HelpPages {
- public static class WebApiConfig {
- public static void Register(HttpConfiguration config) {
- // Web API 配置和服务
- config.Services.Replace(typeof(IAssembliesResolver), new ServiceAssembliesResolver());
- // Web API 路由
- config.MapHttpAttributeRoutes();
- config.Routes.MapHttpRoute(name: "DefaultApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new {
- id = RouteParameter.Optional
- });
- //OData路由,将路由名称设置为控制器(去掉Controller)名称,以便生成Api帮助文档
- config.MapODataServiceRoute(routeName: "ODataSearch", routePrefix: "odata", model: GetEdmModel(), batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer));
- }
- private static IEdmModel GetEdmModel() {
- ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
- builder.EntitySet < Institution > ("ODataSearch");
- builder.Namespace = "Search";
- //builder.EntityType<Institution>().Collection.Function("GetByIdEq2").Returns<string>();
- return builder.GetEdmModel();
- }
- public class Institution {
- public int Id {
- get;
- set;
- }
- public string Name {
- get;
- set;
- }
- public string Address {
- get;
- set;
- }
- }
- }
- }
步骤3,添加MultiXmlDocumentationProvider类,读取多个XML文档
- /// <summary>
- /// 加载目录下的所有Xml文档
- /// </summary>
- public class MultiXmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider
- {
- private IList<XmlDocumentationProvider> _documentationProviders;
- public MultiXmlDocumentationProvider(string xmlDocFilesPath)
- {
- _documentationProviders = new List<XmlDocumentationProvider>();
- foreach (string file in Directory.GetFiles(xmlDocFilesPath, "*.xml"))
- {
- _documentationProviders.Add(new XmlDocumentationProvider(file));
- }
- }
- public string GetDocumentation(HttpParameterDescriptor parameterDescriptor)
- {
- return _documentationProviders.Select(x => x.GetDocumentation(parameterDescriptor)).FirstOrDefault(x => !string.IsNullOrEmpty(x));
- }
- public string GetDocumentation(Type type)
- {
- return _documentationProviders.Select(x => x.GetDocumentation(type)).FirstOrDefault(x => !string.IsNullOrEmpty(x));
- }
- //成员导航
- public string GetDocumentation(MemberInfo member)
- {
- return _documentationProviders
- .Select(x => x.GetDocumentation(member))
- .FirstOrDefault(x => !string.IsNullOrWhiteSpace(x));
- }
- //action 描述
- public string GetDocumentation(HttpActionDescriptor actionDescriptor)
- {
- return _documentationProviders.Select(x => x.GetDocumentation(actionDescriptor)).FirstOrDefault(x => !string.IsNullOrEmpty(x));
- }
- //Controller 描述
- public string GetDocumentation(HttpControllerDescriptor controllerDescriptor)
- {
- return _documentationProviders.Select(x => x.GetDocumentation(controllerDescriptor)).FirstOrDefault(x => !string.IsNullOrEmpty(x));
- }
- public string GetResponseDocumentation(HttpActionDescriptor actionDescriptor)
- {
- return _documentationProviders.Select(x => x.GetDocumentation(actionDescriptor)).FirstOrDefault(x => !string.IsNullOrEmpty(x));
- }
- }
步骤4,使用MultiXmlDocumentationProvider
将config.SetDocumentationProvider(new MultiXmlDocumentationProvider(WebConfigSetting.ServicesLocation));添加到Register方法中
步骤5,创建服务插件文件夹,将服务插件及其XML文档放在文件夹中。
2)重构ApiExplorer,获得Odata控制器的API文档
步骤1,重构ApiExplorer
- public class CustomApiExplorer : ApiExplorer
- {
- private HttpConfiguration configuration;
- public CustomApiExplorer(HttpConfiguration configuration)
- : base(configuration)
- {
- this.configuration = configuration;
- }
- public override bool ShouldExploreController(string controllerVariableValue, HttpControllerDescriptor controllerDescriptor, IHttpRoute route)
- {
- if (controllerDescriptor == null)
- {
- throw new ArgumentNullException("controllerDescriptor");
- }
- if (route == null)
- {
- throw new ArgumentNullException("route");
- }
- var c = controllerDescriptor.ControllerName;
- //获得OData路由
- IEdmModel edm = EdmModelCreater.GetEdmModel();
- List<string> collectionFromEdms = new List<string>();
- foreach (var item in edm.EntityContainer.Elements)
- {
- collectionFromEdms.Add(item.Name);
- }
- //如果是Odata控制器,那么忽略ApiExplorerSettingsAttribute
- ApiExplorerSettingsAttribute setting = controllerDescriptor.GetCustomAttributes<ApiExplorerSettingsAttribute>().FirstOrDefault();
- bool isOdataController = collectionFromEdms.Contains(controllerDescriptor.ControllerName);
- bool isBaseApi = controllerDescriptor.ControllerName != "BaseApi";
- return isBaseApi||isOdataController ||
- ((setting == null || !setting.IgnoreApi) && MatchRegexConstraint(route, RouteValueKeys.Controller, controllerVariableValue));
- }
- public override bool ShouldExploreAction(string actionVariableValue, HttpActionDescriptor actionDescriptor, IHttpRoute route)
- {
- if (actionDescriptor == null)
- {
- throw new ArgumentNullException("actionDescriptor");
- }
- if (route == null)
- {
- throw new ArgumentNullException("route");
- }
- //获得OData路由
- IEdmModel edm = EdmModelCreater.GetEdmModel();
- List<string> collectionFromEdms = new List<string>();
- foreach (var item in edm.EntityContainer.Elements)
- {
- collectionFromEdms.Add(item.Name);
- }
- //如果是Odata控制器,那么忽略ApiExplorerSettingsAttribute
- ApiExplorerSettingsAttribute setting = actionDescriptor.ControllerDescriptor.GetCustomAttributes<ApiExplorerSettingsAttribute>().FirstOrDefault();
- bool isOdataController = collectionFromEdms.Contains(actionDescriptor.ControllerDescriptor.ControllerName);
- bool isBaseApi = actionDescriptor.ControllerDescriptor.ControllerName != "BaseApi";
- return isBaseApi||isOdataController ||
- ((setting == null || !setting.IgnoreApi) && MatchRegexConstraint(route, RouteValueKeys.Action, actionVariableValue));
- }
- private static bool MatchRegexConstraint(IHttpRoute route, string parameterName, string parameterValue)
- {
- IDictionary<string, object> constraints = route.Constraints;
- if (constraints != null)
- {
- object constraint;
- if (constraints.TryGetValue(parameterName, out constraint))
- {
- // treat the constraint as a string which represents a Regex.
- // note that we don't support custom constraint (IHttpRouteConstraint) because it might rely on the request and some runtime states
- string constraintsRule = constraint as string;
- if (constraintsRule != null)
- {
- string constraintsRegEx = "^(" + constraintsRule + ")$";
- return parameterValue != null && Regex.IsMatch(parameterValue, constraintsRegEx, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
- }
- }
- }
- return true;
- }
- }
添加RouteValueKeys类
- internal static class RouteValueKeys
- {
- // Used to provide the action and controller name
- public const string Action = "action";
- public const string Controller = "controller";
- }
添加OdataRelativePath类
- public static class OdataRelativePath
- {
- public static void GetOdataRelativePath(CustomApiExplorer customApiExplorer, HttpConfiguration configuration)
- {
- IEdmModel edm = EdmModelCreater.GetEdmModel();
- List<string> collectionFromEdms = new List<string>();
- foreach(var item in edm.EntityContainer.Elements)
- {
- collectionFromEdms.Add(item.Name);
- }
- Collection<ApiDescription> apiColloction = customApiExplorer.ApiDescriptions;
- foreach (ApiDescription api in apiColloction)
- {
- string controllerName = api.ActionDescriptor.ControllerDescriptor.ControllerName;
- //去掉Odata中控制器的版本号
- var controllerSelector = configuration.Services.GetService(typeof(IHttpControllerSelector)) as VersionControllerSelector;
- string oldString = controllerSelector.RouteVersionSuffixMapping.First(m => m.Key.Contains("OdataRouteVersioning")).Value;
- controllerName = controllerName.Replace(oldString, "");
- if (collectionFromEdms.Contains(controllerName))
- {
- string actionName = api.ActionDescriptor.ActionName;
- var parameters = api.ActionDescriptor.GetParameters();
- string paramStr = null;
- foreach (var parameter in parameters)
- {
- var t = parameter.ParameterType;
- if (parameter.ParameterType.IsClass)
- {
- continue;
- }
- if (paramStr != null)
- {
- paramStr = string.Format("{0}&({1}={1})", paramStr, parameter.ParameterName);
- }
- else
- {
- paramStr = string.Format("({0}={0})", parameter.ParameterName);
- }
- }
- api.RelativePath = string.Format("{0}/{1}/{2}/Service.{3}{4}", "odata", "{Version}", controllerName, actionName, paramStr);
- }
- else
- {
- Regex reg=new Regex("[0-9]");
- Match match = reg.Match(api.RelativePath);
- if(match.Success)
- {
- api.RelativePath = api.RelativePath.Replace(string.Format("V{0}",match.Value),"");
- }
- }
- }
- }
- }
步骤2;根据OData路由拼出api的URI
使用OdataRelativePath.GetOdataRelativePath方法修改ApiExplorer.ApiDescriptions中的URI
例如在控制器中
- public ActionResult Index()
- {
- ViewBag.DocumentationProvider = Configuration.Services.GetDocumentationProvider();
- CustomApiExplorer customApiExplorer = new CustomApiExplorer(Configuration);
- OdataRelativePath.GetOdataRelativePath(customApiExplorer,Configuration);
- Collection<ApiDescription> apiDescriptions = new Collection<ApiDescription>();
- List<ApiDescription> list = new List<ApiDescription>();
- foreach (ApiDescription ad in customApiExplorer.ApiDescriptions)
- {
- if (ad.ActionDescriptor.ControllerDescriptor.ControllerName != "Metadata" && ad.ActionDescriptor.ActionName != "ToJson")
- {
- list.Add(ad);
- }
- }
- list = list.OrderBy(m => m.ActionDescriptor.ControllerDescriptor.ControllerName).ToList();
- list.ForEach(m =>
- {
- apiDescriptions.Add(m);
- });
- return View(apiDescriptions);
- }
注意:配置Odata路由时,将路由名称配置为控制器名称(不含Controller字符串),并且编写服务程序时,遵循一个实体对应一个控制器,对应一个Odata路由。
-----------------------------------------------------------------------------------------
来源: http://www.cnblogs.com/hdwgxz/p/7898983.html