zoukankan      html  css  js  c++  java
  • ASP.NET Web API编程——构建api帮助文档

    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路由。

     

     -----------------------------------------------------------------------------------------

    转载与引用请注明出处。

    时间仓促,水平有限,如有不当之处,欢迎指正。

     

  • 相关阅读:
    PMP:9.项目资源管理
    @JsonIgnore忽略JSON字段
    xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun
    android加载不到.so文件
    报错Failed to install the following SDK components: platforms;android-29 Android SDK Platform 29
    Mac 终端启动运行Redis
    Mac 命令行执行Sublime
    Bean转为Json指定字段名,防止被修改大小写
    Rest接口入参忽略大小写 使用jackson注解
    mongo批量导入
  • 原文地址:https://www.cnblogs.com/hdwgxz/p/7898983.html
Copyright © 2011-2022 走看看