在开发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. ASP.NET相关技术整理

  2. AppSetting ,connectionStrings配置节

    <appSettings> <!-- 当前使用的学校代码 --> <add key="DefaultCompanyID" value="cs ...

  3. Centos 7.1+CDH5.7.2全部署流程

    前期准备: JDK环境 版本:jdk-8u101-linux-x64.rpm 下载地址:oracle官网 mysql rpm包:http://dev.mysql.com/get/Downloads/M ...

  4. Nginx+Tomcat+Memcached集群

    Tomcat集群session同步方案有以下几种方式: 使用tomcat自带的cluster方式,多个tomcat间自动实时复制session信息,配置起来很简单.但这个方案的效率比较低,在大并发下表 ...

  5. PHP数组在HTML之中的应用

    <select name="data[status]" id="" <?php if(in_array($list['status'],array( ...

  6. leetCode刷题记录

    (1)Linked List Cycle Total Accepted: 13297 Total Submissions: 38411 Given a linked list, determine i ...

  7. Mongodb的索引--学习笔记(未完)

    全文索引 建立方法: --在articles集合的key字段上创建全文索引 db.articles.ensureIndex({key:"text"}) --在articles集合的 ...

  8. 第六章 管理类型(In .net4.5) 之 创建类型

    1. 概述 本章内容包括 C#5中如何更好的创建类型以及如何扩展现有类型. 2. 主要内容 2.1 如何选择类型 C#类型系统包括三种类型:值类型.引用类型.指针类型.(指针类型用于非托管代码,很少使 ...

  9. git传输协议原理

    git精神:distributed-is-the-new-centralized 转自:http://git-scm.com/book/zh/v1/Git-%E5%86%85%E9%83%A8%E5% ...

  10. automapper的简单用法

    AutoMapper对象转换方面(Object-Object Mapping)对象映射工具,实现对象和对象之间的转化.主要应用在项目的dto,model,entity或viewmodel之间转换,其实 ...