在開發Webapi項目時每寫完一個方法時,是不是需要添加相應的功能說明和測試案例呢?為了更簡單方便的寫說明介面文檔和介面測試HelpPage提供了一個方便的途徑。 她的大致原理是:在編譯時會生成.dll程式集和.xml程式集說明文件,通過xml文件獲取Controller名稱、action名稱、參數
在開發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
作者:HsutonWang
出處:http://www.cnblogs.com/AntonWang/p/4192119.html/
本文版權歸作者和博客園共有,歡迎轉載