weiapi2.2 HelpPage自动生成接口说明文档和接口测试功能

在开发Webapi项目时每写完一个方法时,是不是需要添加相应的功能说明和测试案例呢?为了更简单方便的写说明接口文档和接口测试HelpPage提供了一个方便的途径。

她的大致原理是:在编译时会生成.dll程序集和.xml程序集说明文件,通过xml文件获取Controller名称、action名称、参数信息和备注信息等。这样接口说明文档就可以放到备注信息了,个人觉得确实粗暴简单 。那接口测试在哪呢?这里用到nuget第三方程序包:webapitestclient

先上效果图吧!

案例是用VS2013创建的,已创建好HelpPage,但wepapi版本是1.0 。wepapi2功能增强,为更上节奏进入nuget升级。

其他的互相依赖项也会升级!

设置xml说明文档路径:

web项目属性设置生成的xml路径:

遗憾webapitestclient只支持最低版本的HelpPage,升级webapi还得修改部分代码!说明:webapi1可以获取action的备注说明但不能获取controller的备注说明 webapi2是可以。

升级后,XmlDocumentationProvider类需要会多出两个实现方法:Controller和action描述方法.

XmlDocumentationProvider.cs 
public class XmlDocumentationProvider : IDocumentationProvider
    {        private XPathNavigator _documentNavigator;        private const string TypeExpression = "/doc/members/member[@name='T:{0}']";        private const string MethodExpression = "/doc/members/member[@name='M:{0}']";        private const string ParameterExpression = "param[@name='{0}']";        /// <summary>
        /// Initializes a new instance of the <see cref="XmlDocumentationProvider"/> class.        /// </summary>
        /// <param name="documentPath">The physical path to XML document.</param>
        public XmlDocumentationProvider(string documentPath="")
        {            //if (documentPath.IsNullOrWhiteSpace())            //    documentPath = HttpContext.Current.Server.MapPath(ConfigurationManager.AppSettings["webApiDescription"]);
            if (documentPath == null)
            {                throw new ArgumentNullException("documentPath");
            }
            XPathDocument xpath = new XPathDocument(documentPath);
            _documentNavigator = xpath.CreateNavigator();
        }        private XPathNavigator GetTypeNode(Type type)
        {            string controllerTypeName = GetTypeName(type);            string selectExpression = String.Format(CultureInfo.InvariantCulture, TypeExpression, controllerTypeName);            return _documentNavigator.SelectSingleNode(selectExpression);
        }        private static string GetTagValue(XPathNavigator parentNode, string tagName)
        {            if (parentNode != null)
            {
                XPathNavigator node = parentNode.SelectSingleNode(tagName);                if (node != null)
                {                    return node.Value.Trim();
                }
            }            return null;
        }        public virtual string GetDocumentation(HttpControllerDescriptor controllerDescriptor)
        {
            XPathNavigator typeNode = GetTypeNode(controllerDescriptor.ControllerType);            return GetTagValue(typeNode, "summary");
        }        public virtual string GetDocumentation(HttpActionDescriptor actionDescriptor)
        {
            XPathNavigator methodNode = GetMethodNode(actionDescriptor);            if (methodNode != null)
            {
                XPathNavigator summaryNode = methodNode.SelectSingleNode("summary");                if (summaryNode != null)
                {                    return summaryNode.Value.Trim();
                }
            }            return null;
        }        public virtual string GetDocumentation(HttpParameterDescriptor parameterDescriptor)
        {
            ReflectedHttpParameterDescriptor reflectedParameterDescriptor = parameterDescriptor as ReflectedHttpParameterDescriptor;            if (reflectedParameterDescriptor != null)
            {
                XPathNavigator methodNode = GetMethodNode(reflectedParameterDescriptor.ActionDescriptor);                if (methodNode != null)
                {                    string parameterName = reflectedParameterDescriptor.ParameterInfo.Name;
                    XPathNavigator parameterNode = methodNode.SelectSingleNode(String.Format(CultureInfo.InvariantCulture, ParameterExpression, parameterName));                    if (parameterNode != null)
                    {                        return parameterNode.Value.Trim();
                    }
                }
            }            return null;
        }        public string GetResponseDocumentation(HttpActionDescriptor actionDescriptor)
        {
            XPathNavigator methodNode = GetMethodNode(actionDescriptor);            return GetTagValue(methodNode, "returns");
        }        private XPathNavigator GetMethodNode(HttpActionDescriptor actionDescriptor)
        {
            ReflectedHttpActionDescriptor reflectedActionDescriptor = actionDescriptor as ReflectedHttpActionDescriptor;            if (reflectedActionDescriptor != null)
            {                string selectExpression = String.Format(CultureInfo.InvariantCulture, MethodExpression, GetMemberName(reflectedActionDescriptor.MethodInfo));                return _documentNavigator.SelectSingleNode(selectExpression);
            }            return null;
        }        private static string GetMemberName(MethodInfo method)
        {            string name = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", method.DeclaringType.FullName, method.Name);
            ParameterInfo[] parameters = method.GetParameters();            if (parameters.Length != 0)
            {                string[] parameterTypeNames = parameters.Select(param => GetTypeName(param.ParameterType)).ToArray();
                name += String.Format(CultureInfo.InvariantCulture, "({0})", String.Join(",", parameterTypeNames));
            }            return name;
        }        private static string GetTypeName(Type type)
        {            if (type.IsGenericType)
            {                // Format the generic type name to something like: Generic{System.Int32,System.String}
                Type genericType = type.GetGenericTypeDefinition();
                Type[] genericArguments = type.GetGenericArguments();                string typeName = genericType.FullName;                // Trim the generic parameter counts from the name
                typeName = typeName.Substring(0, typeName.IndexOf('`'));                string[] argumentTypeNames = genericArguments.Select(t => GetTypeName(t)).ToArray();                return String.Format(CultureInfo.InvariantCulture, "{0}{{{1}}}", typeName, String.Join(",", argumentTypeNames));
            }            return type.FullName;
        }
    }

修改获取Controller信息:

HelpController.cs

Index.cshtml

ApiGroup.cshtml

public ActionResult Index()
        {
            ViewBag.DocumentationProvider = Configuration.Services.GetDocumentationProvider();            return View(Configuration.Services.GetApiExplorer().ApiDescriptions);
        }
@model Collection<ApiDescription>@{
    ViewBag.Title = "ASP.NET Web API Help Page";    // Group APIs by controller
    ILookup<System.Web.Http.Controllers.HttpControllerDescriptor, ApiDescription> apiGroups = Model.ToLookup(api => api.ActionDescriptor.ControllerDescriptor);
}<header>
    <div class="content-wrapper">
        <div class="float-left">
            <h1>@ViewBag.Title</h1>
        </div>
    </div>
</header>
<div id="body">
    <section class="featured">
        <div class="content-wrapper">
            <h2>Introduction</h2>
            <p>
                Provide a general description of your APIs here.            </p>
        </div>
    </section>
    <section class="content-wrapper main-content clear-fix">        <!--遍历Controller -->
        @foreach (var group in apiGroups)
        {
            @Html.DisplayFor(m => group, "ApiGroup")
        }    </section>
</div>
@model IGrouping<System.Web.Http.Controllers.HttpControllerDescriptor, ApiDescription>@{    var controllerDocumentation = ViewBag.DocumentationProvider != null ? 
        ViewBag.DocumentationProvider.GetDocumentation(Model.Key) : 
        null;
}<!--Controller名称 --><h2 id="@Model.Key.ControllerName">@Model.Key.ControllerName</h2><!--Controller说明备注 -->@if (!String.IsNullOrEmpty(controllerDocumentation))
{    <p>@controllerDocumentation</p>}<table class="help-page-table">
    <thead>
        <tr><th>API</th><th>Description</th></tr>
    </thead>
    <tbody>    <!--遍历Action -->
    @foreach (var api in Model)
    {        <tr>
            <td class="api-name"><a href="@Url.Action("Api", "Help", new { apiId = api.GetFriendlyId() })">@api.HttpMethod.Method @api.RelativePath</a></td>
            <td class="api-documentation">
            @if (api.Documentation != null)
            {                <p>@api.Documentation</p>
            }            else
            {                <p>No documentation available.</p>
            }            </td>
        </tr>
    }    </tbody>
</table>

效果如下:

接下来添加接口测试功能.

nuget添加webapitestclient:

进入"获取单个商品信息"接口页面,会多出 "Test Api"按钮,也可以自己修改位置!

点击"Test Api"按钮 弹出调用窗口 :

输入参数调用,输出json数据:

  共享Demo

http://files.cnblogs.com/files/AntonWang/Webapi2.2WithTest.zip

原文发布于微信公众号 - 我为Net狂(dotNetCrazy)

原文发表时间:2016-02-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏岑玉海

C#向excel中写入数据的三种方式

第一种:将DataGrid中的数据以流的形式写到excel中,格式以html的形式存在             Response.Clear();       ...

32440
来自专栏菩提树下的杨过

Silverlight:用Enter键替换Tab键切换焦点

业务系统中,很多录入人员习惯于用Enter键来代替Tab键切换控件焦点(虽然我个人并不觉得这样录入速度会变得有多高效,呵呵),有需求了,自然就得想办法满足。 思...

246100
来自专栏林德熙的博客

win10 uwp 异步转同步 使用的条件使用方法使用Task.Wait 时需要小心死锁

在本文开始,我必须告诉大家,这个方法可能立即死锁,所以使用的时候需要满足下面的条件

24320
来自专栏DOTNET

asp.net web api 构建api帮助文档

1 概要 创建ASP.NET Web Api 时模板自带Help Pages框架。 2 问题 1)使用VS创建Web Api项目时,模板将Help Pages框...

31580
来自专栏菩提树下的杨过

温故而知新:设计模式之Builder

Builder模式主要用于以下场景: 需要创建一个较复杂的大对象实例,并且构成该对象的子对象可能经常会发生变化,但是组成大对象的算法却相对稳定。 比如:我们做b...

20290
来自专栏马洪彪

C#解析PDF

C#解析PDF的方式有很多,比较好用的有ITestSharp和PdfBox。 PDF内容页如果是图片类型,例如扫描件,则需要进行OCR(光学字符识别)。 文本内...

58590
来自专栏智能大石头

老瓶装新酒 - C#调用WM手机发送短信(源码)

一些系统,需要能够发送短信,量很小,平均每日10条。 运营商平台太贵,白名单很严格,小额只能发省内; 各短信平台有各种限制,大事件前后会关闭; 飞信以前可以用W...

25050
来自专栏Java成神之路

Java钉钉开发_02_免登授权(身份验证)

将所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式 (即 key1=value1&key2=value2…)拼接成字符串...

42220
来自专栏C/C++基础

DataSet之间的赋值

DataSet 一个具有很多内置方法的程序集,在绑定数据中,使用率非常之高,虽然没有自定义泛型灵活性高,强类型的数据严谨性,而且一次性加载所有数据也稍微影响性能...

11220
来自专栏木宛城主

工欲善其事,必先利其器:分享一套Code Smith 搭建N层架构模板

 开篇 平常开发时,由于冗余代码过多,程序员做重复的工作过多势必会影响开发效率。倘若对重复性代码简单的复制、粘贴,虽然也能节省时间,但也需仔细一步步替换,这无...

19980

扫码关注云+社区

领取腾讯云代金券