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

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

转载与引用请注明出处。

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

asp.net web api 构建api帮助文档的更多相关文章

  1. ASP.NET Core WebApi构建API接口服务实战演练

    一.ASP.NET Core WebApi课程介绍 人生苦短,我用.NET Core!提到Api接口,一般会想到以前用到的WebService和WCF服务,这三个技术都是用来创建服务接口,只不过Web ...

  2. Web Api 自动生成帮助文档

    Web Api 自动生成帮助文档   新建Web Api项目之后,会在首页有API的导航菜单,点击即可看到API帮助文档,不过很遗憾,Description 是没有内容的. 怎么办呢? 第一步: 如果 ...

  3. ASP.NET Web Pages (Razor) API Quick Reference

    ASP.NET Web Pages (Razor) API Quick Reference By Tom FitzMacken|February 10, 2014 Print This page co ...

  4. 第三方API接口测试问题反馈文档

    大家在给甲方做大型项目的时候,有时候参与的厂商比较多,而公司负责的部分又需要第三方厂商提供接口支持. 例如我们做医疗行业的,给医院提供医保控费系统服务的,就需要HIS厂商提供接口给我们采集数据.有时候 ...

  5. ASP.NET WebAPI使用Swagger生成测试文档

    ASP.NET WebAPI使用Swagger生成测试文档 SwaggerUI是一个简单的Restful API测试和文档工具.简单.漂亮.易用(官方demo).通过读取JSON配置显示API .项目 ...

  6. Java实现web在线预览office文档与pdf文档实例

    https://yq.aliyun.com/ziliao/1768?spm=5176.8246799.blogcont.24.1PxYoX 摘要: 本文讲的是Java实现web在线预览office文档 ...

  7. (转)WEB页面导出为Word文档后分页&横向打印的方法

    <html>    <HEAD>        <title>WEB页面导出为Word文档后分页&横向打印的方法 </title>    < ...

  8. Jenkins构建项目帮助文档

    Jenkins构建项目帮助文档 主要步骤 一.配置jdk 1.1.   下载jdk,安装到自己电脑磁盘的Java目录下(比如:D:\Java\jdk). 1.2.   Jdk环境变量的配置: 1. 鼠 ...

  9. Web 前端 UI 组件库文档自动化方案 All In One

    Web 前端 UI 组件库文档自动化方案 All In One 需求 自动化 动态 好用 markdown element-ui 中示例和说明按照一定规则写在md文件中,调用md-loader将md文 ...

随机推荐

  1. SAP smartform 实现打印条形码

    先在SE73里定义一个新的BARCODE,注意一定要用新的才可以,旧的是打印不出来的. 然后定义一个SMARTFORM的样式,把你定义的BARCODE放到字符样式里面去. 再做SMARTFORM就可以 ...

  2. Pyhton编程(三)之Pycharm安装及运算符

    一:上节题目解答 1)使用while循环输出 1 2 3 4 5 6 8 9 10(注意:没有7) n=1while n<11: if n==7: pass //pass代码段指代空代码.. e ...

  3. Django安装以及介绍

    安装django说先需要安装python环境,因为他是依赖于python环境运行的 最好再安装pycharm,一款强大的开发工具,里面有各种开发工具的集成 在Windows先安装: 首先进入cmd命令 ...

  4. hashlib模块--摘要算法

    算法介绍: Python的hashlib提供了常见的摘要算法:MD5,SHA()等. 摘要算法,又称哈希算法,散列算法.通过一个函数,吧任意长度的字符串转换为固定长度的字符串(16进制) 摘要算法就是 ...

  5. JavaScript正则表达式之分组匹配 / 反向引用

    语法 元字符:(pattern) 作用:用于反复匹配的分组 属性$1~$9 如果它(们)存在,用于得到对应分组中匹配到的子串 \1或$1 用于匹配第一个分组中的内容 \2或$2 用于匹配第一个分组中的 ...

  6. 手把手教你解决无法创建 JPA 工程的问题

    原创播客,如需转载请注明出处.原文地址:http://www.cnblogs.com/crawl/p/7703803.html ------------------------------------ ...

  7. Nginx监控-Nginx+Telegraf+Influxb+Grafana

    搭建了Nginx集群后,需要继续深入研究的就是日常Nginx监控. Nginx如何监控?相信百度就可以找到:nginx-status 通过Nginx-status,实时获取到Nginx监控数据后,如何 ...

  8. Quart.Net分布式任务管理平台

           无关主题:一段时间没有更新文章了,与自己心里的坚持还是背驰,虽然这期间在公司做了统计分析,由于资源分配问题,自己或多或少的原因,确实拖得有点久了,自己这段时间也有点松懈,借口就不说那么多 ...

  9. Android的主线程和子线程

    在一个Android 程序开始运行的时候,会单独启动一个Process.默认的情况下,所有这个程序中的Activity或者Service(Service和 Activity只是Android提供的Co ...

  10. Java调用C++类库--JNI

    JNI是Java平台中的一个重要的功能,这里我把我做的Demo总结一下,分享一下,我会把每个步骤尽量的详细的展现出来. 这里我就不讲解JNI的原理了,google,百度一下,到处都是 好了,直接来讲步 ...