一、为什么要生成说明文档
我们大家都知道,自己写的API要供他人调用,就需要用文字的方式将调用方法和注意事项等写成一个文档以更好的展示我们设计时的想法和思路,便于调用者更加高效的使用我们的API。
当然,您可以不借助任何工具,自己手工写文档,然后做成chm或者html的形式交给客户,就是效率有点低,并且在API更改后有需要手工更改说明文档。
那有没有一种既方便,又能在API发生改变时,自动更新说明文档呢?答案是肯定的。
二、自动生成说明文档的具体实现
我们这里主要是将GlobalConfiguration.Configuration.Services的描述的实现换一种实现,具体是实现IDocumentationProvider这个接口,然后在调用Replace方法替换实现。
百度搜XmlCommentDocumentationProvider,有很多结果哦。这些都源于微软官方的大牛这篇文章:
下面是XmlCommentDocumentationProvider的实现:
using System.Linq; using System.Reflection; using System.Text.RegularExpressions; using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.Description; using System.Web.Mvc; using System.Xml.XPath; namespace Deepleo.API { /// <summary> /// XmlCommentDocumentationProvider /// </summary> public class XmlCommentDocumentationProvider : IDocumentationProvider { readonly XPathNavigator _documentNavigator; private const string _methodExpression = "/doc/members/member[@name='M:{0}']"; private static readonly Regex _nullableTypeNameRegex = new Regex(@"(.*\.Nullable)" + Regex.Escape("`1[[") + "([^,]*),.*"); /// <summary> /// ctor /// </summary> /// <param name="documentPath">document path</param> public XmlCommentDocumentationProvider(string documentPath) { XPathDocument xpath = new XPathDocument(documentPath); _documentNavigator = xpath.CreateNavigator(); } public virtual string GetDocumentation(HttpActionDescriptor actionDescriptor) { XPathNavigator memberNode = GetMemberNode(actionDescriptor); if (memberNode != null) { XPathNavigator summaryNode = memberNode.SelectSingleNode("summary"); if (summaryNode != null) { return summaryNode.Value.Trim(); } } return ""; } public virtual string GetDocumentation(HttpParameterDescriptor parameterDescriptor) { ReflectedHttpParameterDescriptor reflectedParameterDescriptor = parameterDescriptor as ReflectedHttpParameterDescriptor; if (reflectedParameterDescriptor != null) { XPathNavigator memberNode = GetMemberNode(reflectedParameterDescriptor.ActionDescriptor); if (memberNode != null) { string parameterName = reflectedParameterDescriptor.ParameterInfo.Name; XPathNavigator parameterNode = memberNode.SelectSingleNode(string.Format("param[@name='{0}']", parameterName)); if (parameterNode != null) { return parameterNode.Value.Trim(); } } } return ""; } private XPathNavigator GetMemberNode(HttpActionDescriptor actionDescriptor) { ReflectedHttpActionDescriptor reflectedActionDescriptor = actionDescriptor as ReflectedHttpActionDescriptor; if (reflectedActionDescriptor != null) { string selectExpression = string.Format(_methodExpression, GetMemberName(reflectedActionDescriptor.MethodInfo)); XPathNavigator node = _documentNavigator.SelectSingleNode(selectExpression); if (node != null) { return node; } } return null; } private static string GetMemberName(MethodInfo method) { if (method.DeclaringType == null) return string.Empty; string name = string.Format("{0}.{1}", method.DeclaringType.FullName, method.Name); var parameters = method.GetParameters(); if (parameters.Length != 0) { string[] parameterTypeNames = parameters.Select(param => ProcessTypeName(param.ParameterType.FullName)).ToArray(); name += string.Format("({0})", string.Join(",", parameterTypeNames)); } return name; } private static string ProcessTypeName(string typeName) { //handle nullable var result = _nullableTypeNameRegex.Match(typeName); if (result.Success) { return string.Format("{0}{{{1}}}", result.Groups[1].Value, result.Groups[2].Value); } return typeName; } } }