在开发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

Webapi2.2WithTest.zip

  作者:HsutonWang

  出处:http://www.cnblogs.com/AntonWang/p/4192119.html/

  本文版权归作者和博客园共有,欢迎转载

weiapi2.2 HelpPage自动生成接口说明文档和接口测试功能的更多相关文章

  1. Swagger(webapi自动生成接口说明文档)

    1.引入Swagger.Net.UI和Swashbuckle包 2.卸载重复包Swagger.Net 3.多余的SwaggerUI文件夹 4.项目属性->勾选生成xml文档文件 5.添加类Swa ...

  2. .net core 使用swagger自动生成接口文档

     前言 swagger是一个api文档自动生动工具,还集成了在线调试. 可以为项目自动生成接口文档, 非常的方便快捷 Swashbuckle.AspNetCore 是一个开源项目,用于生成 ASP.N ...

  3. Spring Boot(九)Swagger2自动生成接口文档和Mock模拟数据

    一.简介 在当下这个前后端分离的技术趋势下,前端工程师过度依赖后端工程师的接口和数据,给开发带来了两大问题: 问题一.后端接口查看难:要怎么调用?参数怎么传递?有几个参数?参数都代表什么含义? 问题二 ...

  4. WebApi使用swagger ui自动生成接口文档

    之前就写到.最近正在使用webapi.这里介绍一个实用的东西swageer ui现在开发都是前后端分开.我们这里是给前端提供api.有时候对于一个api的描述,并不想专门写一份文档.很浪费时间.swa ...

  5. drf07 过滤 排序 分页 异常处理 自动生成接口文档

    4. 过滤Filtering 对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-fitlter扩展来增强支持. pip install django-filter 在配置文件sett ...

  6. Spring Boot Swagger2自动生成接口文档

    一.简介 在当下这个前后端分离的技术趋势下,前端工程师过度依赖后端工程师的接口和数据,给开发带来了两大问题: 1.问题一.后端接口查看难:要怎么调用?参数怎么传递?有几个参数?参数都代表什么含义? 2 ...

  7. Django框架深入了解_05 (Django中的缓存、Django解决跨域流程(非简单请求,简单请求)、自动生成接口文档)

    一.Django中的缓存: 前戏: 在动态网站中,用户所有的请求,服务器都会去数据库中进行相应的增,删,查,改,渲染模板,执行业务逻辑,最后生成用户看到的页面. 当一个网站的用户访问量很大的时候,每一 ...

  8. Django rest_framework 自动生成接口文档

    自动生成接口文档 REST framework可以自动帮助我们生成接口文档. 接口文档以网页的方式呈现. 自动接口文档能生成的是继承自APIView及其子类的视图. 1. 安装依赖 REST fram ...

  9. django自动生成接口文档

    我们在实际项目中,会需要将我们的一些接口的信息返回给前端,便于前后端的交互,在实际使用中,这种自动生成接口文档的模块很多,我主要是用REST framework自动生成接口文档,这个需要用到的是cor ...

随机推荐

  1. Silverlight中本地化的实现(语言切换)

    对于本地化这个功能我个人一直觉得在需求阶段就要确定该项目需不需要实行本地化,这样后面就可以减轻许多工作量,Sl中对本地化功能的支持与ASP.NET 中一样,主要使用CultureInfo类来完成的. ...

  2. project 2010 使用技巧

    快捷键 设置任务子任务 ALT+SHIFT+向右方向键 1.工作时间设置 新建一个日历后,可以在 “项目 >> 项目信息 >> 日历” 中进行选择

  3. PHP代码优化的53个细节

    PHP代码优化的53个细节,常见而重要的php优化策略. 用单引号代替双引号来包含字符串,这样做会更快一些.因为PHP会在双引号包围的字符串中搜寻变量,单引号则不会,注意:只有echo能这么做,它是一 ...

  4. LtUpload上传组件

    <?php/** * The Upload class * @author Alex Lee <iuyes@qq.com> * @license http://opensource. ...

  5. Sublime Text生成html标签快捷键

    Emmet Documentation Syntax   Child: > nav>ul>li <nav> <ul> <li></li> ...

  6. API - .addBack()

    从jQuery官网上提供的Example来看,这个API的意思是: 1   先圈定一个范围,[A] $( "div.after-addback" ) 2 再用find过滤一出一个更 ...

  7. Java 装箱 拆箱

    Java 自动装箱与拆箱   ??什么是自动装箱拆箱 基本数据类型的自动装箱(autoboxing).拆箱(unboxing)是自J2SE 5.0开始提供的功能. 一般我们要创建一个类的对象的时候,我 ...

  8. 在Linux 5/6上使用UDEV SCSI规则配置ASM DISK

    格式化磁盘(略) 识别磁盘(/sbin/scsi_id)  Oracle Linux 5用如下脚本: #!/bin/sh for i in b c d e f g do echo "KERN ...

  9. jQuery toggle方法的一个奇怪表现。

    function buildTree() { //$('.tree li:has(ul)').addClass('parent_li').find(' > span').attr('title' ...

  10. C 几种异常机制简单讲述

    引言 这是关于C中如何使用异常机制的讨论.顺带讲一讲C中魔法函数的setjmp内部机制.通过它实现高级的异常try...catch. 允许我先扯一段面试题. 对于计算机面试题. 算法题等.觉得还是有意 ...