为ASP.NET WEB API生成人性化说明文档

一、为什么要生成说明文档

我们大家都知道,自己写的API要供他人调用,就需要用文字的方式将调用方法和注意事项等写成一个文档以更好的展示我们设计时的想法和思路,便于调用者更加高效的使用我们的API。

当然,您可以不借助任何工具,自己手工写文档,然后做成chm或者html的形式交给客户,就是效率有点低,并且在API更改后有需要手工更改说明文档。

那有没有一种既方便,又能在API发生改变时,自动更新说明文档呢?答案是肯定的。

 

二、自动生成说明文档的具体实现

我们这里主要是将GlobalConfiguration.Configuration.Services的描述的实现换一种实现,具体是实现IDocumentationProvider这个接口,然后在调用Replace方法替换实现。

百度搜XmlCommentDocumentationProvider,有很多结果哦。这些都源于微软官方的大牛这篇文章:

http://blogs.msdn.com/b/yaohuang1/archive/2012/05/21/asp-net-web-api-generating-a-web-api-help-page-using-apiexplorer.aspx

下面是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;
        }
    }
}
View Code

这个代码是通用的,直接copy过去就ok了。

这篇博客园大牛张善友老师的博客有更详细的说明:http://www.cnblogs.com/shanyou/archive/2012/06/02/2532370.html

我主要说说后面的怎么实现:

第一步:需要在Project的Build里面打开生成xml注释文件选项,如下图所示:

 第二步:改造HomeController.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Http.Description;
using System.Web.Http;

namespace MvcApplication1.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            var config = GlobalConfiguration.Configuration;
            config.Services.Replace(typeof(IDocumentationProvider),
                                    new XmlCommentDocumentationProvider(HttpContext.Server.MapPath("~/bin/MvcApplication1.XML")));
            var apiExplorer = config.Services.GetApiExplorer();
            return View(apiExplorer);
        }
    }
}

代码的大意就是读取那个xml文件,然后替换掉以前的实现,换成我们的XmlCommentDocumentationProvider。

下面最后一步,修改View.

@model System.Web.Http.Description.IApiExplorer
@{
    ViewBag.Title = "API说明文档";
}
<div id="body">
    <section class="featured">
        <div class="content-wrapper">
            <hgroup class="title">
                <h1>API说明文档</h1>
            </hgroup>
        </div>
    </section>
    <section class="content-wrapper main-content clear-fix">
        <ul>
        @foreach (var api in Model.ApiDescriptions)
        {
            <li>
                <p>@api.HttpMethod : <b>@ViewBag.Domain@api.RelativePath</b></p>
                <blockquote>
                    <b>Description:</b>@api.Documentation<br />
                    @if (api.ParameterDescriptions.Count > 0)
                    {
                        <b>Parameters</b>
                        <ul>
                            @foreach (var parameter in api.ParameterDescriptions)
                            {
                                <li>@parameter.Name: @parameter.Documentation {@parameter.Source}</li>
                            }
                        </ul>
                    }
                </blockquote>
            </li>
            <hr/>
        }
        <li>
          <small>来源:http://www.cnblogs.com/deepleo/p/XmlCommentDocumentationProvider.html</small>  
        </li>
        </ul>
    </section>
</div>

大功告成,在线浏览效果:http://weishangapi.deepleo.com

演示代码下载:http://files.cnblogs.com/deepleo/WebApplication1.rar

可以看到,你在代码里面的注释,会自动显示到说明文档里面,这样你就可以专注于你的API设计和实现了,无需要手工更改说明文档。

posted @ 2014-08-22 15:36 夜の魔王 阅读(...) 评论(...) 编辑 收藏