ASP.NET Web API專案自動生成介面文件和測試頁面
阿新 • • 發佈:2019-01-29
在開發介面的時候,寫介面文件已是一件不可忽視的事情,有了更新也要同步更新很麻煩。ASP.NET 建立的Web API專案可以自己配置介面文件的XML顯示,這樣介面更新和註釋更新了重新發布就有了,確實方便不少,下來就介紹下怎麼配置生成API介面註釋文件。另外,如果在介面生成的同時能夠一併生成測試頁面也是不錯的選擇,能節省不少開發時間和人力成本。
建立Web API專案
修改預設的API路由配置
為什麼要修改呢?因為每一個Controller的預設路由都是指定到"api/{controller}/{id}"的,所以預設生成的控制器類似下面:
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; namespace WebAPI.Controllers { public class ValuesController : ApiController { // GET api/<controller> public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } // GET api/<controller>/5 public string Get(int id) { return "value"; } // POST api/<controller> public void Post([FromBody]string value) { } // PUT api/<controller>/5 public void Put(int id, [FromBody]string value) { } // DELETE api/<controller>/5 public void Delete(int id) { } } }
我們需要一個方法提供多種HTTP訪問方式,所以應該將action的配置加入,如:"api/{controller}/{action}/{id}"。
修改WebApiConfig.cs檔案:
程式碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web.Http; namespace WebAPI { public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API 配置和服務 // Web API 路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional } ); } } }
修改HelpPage配置啟動
修改HelpPageConfig.cs檔案,程式碼如下:
// Uncomment the following to provide samples for PageResult<T>. Must also add the Microsoft.AspNet.WebApi.OData // package to your project. ////#define Handle_PageResultOfT using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net.Http.Headers; using System.Reflection; using System.Web; using System.Web.Http; using WebAPI.Utils; namespace WebAPI.Areas.HelpPage { /// <summary> /// Use this class to customize the Help Page. /// For example you can set a custom <see cref="System.Web.Http.Description.IDocumentationProvider"/> to supply the documentation /// or you can provide the samples for the requests/responses. /// </summary> public static class HelpPageConfig { [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "WebAPI.Areas.HelpPage.TextSample.#ctor(System.String)", Justification = "End users may choose to merge this string with existing localized resources.")] [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "bsonspec", Justification = "Part of a URI.")] public static void Register(HttpConfiguration config) { //// Uncomment the following to use the documentation from XML documentation file. //config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml"))); #region *************************XML顯示介面引數和註釋*************************************** // 新增本專案或其他引用專案的XML配置:可加多個 config.SetDocumentationProvider(new MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/bin/WebAPI.XML"))); #endregion //// Uncomment the following to use "sample string" as the sample for all actions that have string as the body parameter or return type. //// Also, the string arrays will be used for IEnumerable<string>. The sample objects will be serialized into different media type //// formats by the available formatters. //config.SetSampleObjects(new Dictionary<Type, object> //{ // {typeof(string), "sample string"}, // {typeof(IEnumerable<string>), new string[]{"sample 1", "sample 2"}} //}); // Extend the following to provide factories for types not handled automatically (those lacking parameterless // constructors) or for which you prefer to use non-default property values. Line below provides a fallback // since automatic handling will fail and GeneratePageResult handles only a single type. #if Handle_PageResultOfT config.GetHelpPageSampleGenerator().SampleObjectFactories.Add(GeneratePageResult); #endif // Extend the following to use a preset object directly as the sample for all actions that support a media // type, regardless of the body parameter or return type. The lines below avoid display of binary content. // The BsonMediaTypeFormatter (if available) is not used to serialize the TextSample object. #region *************************預設JSON顯示介面*************************************** //config.SetSampleForMediaType( // new TextSample("Binary JSON content. See http://bsonspec.org for details."), // new MediaTypeHeaderValue("application/bson")); #endregion //// Uncomment the following to use "[0]=foo&[1]=bar" directly as the sample for all actions that support form URL encoded format //// and have IEnumerable<string> as the body parameter or return type. //config.SetSampleForType("[0]=foo&[1]=bar", new MediaTypeHeaderValue("application/x-www-form-urlencoded"), typeof(IEnumerable<string>)); //// Uncomment the following to use "1234" directly as the request sample for media type "text/plain" on the controller named "Values" //// and action named "Put". //config.SetSampleRequest("1234", new MediaTypeHeaderValue("text/plain"), "Values", "Put"); //// Uncomment the following to use the image on "../images/aspNetHome.png" directly as the response sample for media type "image/png" //// on the controller named "Values" and action named "Get" with parameter "id". //config.SetSampleResponse(new ImageSample("../images/aspNetHome.png"), new MediaTypeHeaderValue("image/png"), "Values", "Get", "id"); //// Uncomment the following to correct the sample request when the action expects an HttpRequestMessage with ObjectContent<string>. //// The sample will be generated as if the controller named "Values" and action named "Get" were having string as the body parameter. //config.SetActualRequestType(typeof(string), "Values", "Get"); //// Uncomment the following to correct the sample response when the action returns an HttpResponseMessage with ObjectContent<string>. //// The sample will be generated as if the controller named "Values" and action named "Post" were returning a string. //config.SetActualResponseType(typeof(string), "Values", "Post"); } #if Handle_PageResultOfT private static object GeneratePageResult(HelpPageSampleGenerator sampleGenerator, Type type) { if (type.IsGenericType) { Type openGenericType = type.GetGenericTypeDefinition(); if (openGenericType == typeof(PageResult<>)) { // Get the T in PageResult<T> Type[] typeParameters = type.GetGenericArguments(); Debug.Assert(typeParameters.Length == 1); // Create an enumeration to pass as the first parameter to the PageResult<T> constuctor Type itemsType = typeof(List<>).MakeGenericType(typeParameters); object items = sampleGenerator.GetSampleObject(itemsType); // Fill in the other information needed to invoke the PageResult<T> constuctor Type[] parameterTypes = new Type[] { itemsType, typeof(Uri), typeof(long?), }; object[] parameters = new object[] { items, null, (long)ObjectGenerator.DefaultCollectionSize, }; // Call PageResult(IEnumerable<T> items, Uri nextPageLink, long? count) constructor ConstructorInfo constructor = type.GetConstructor(parameterTypes); return constructor.Invoke(parameters); } } return null; } #endif } }
這裡修改支援了多個XML文件方法,擴充套件類如下:
using System;
using System.Linq;
using System.Reflection;
using System.Web.Http.Controllers;
using System.Web.Http.Description;
using WebAPI.Areas.HelpPage;
using WebAPI.Areas.HelpPage.ModelDescriptions;
namespace WebAPI.Utils
{
/// <summary>A custom <see cref='IDocumentationProvider'/> that reads the API documentation from a collection of XML documentation files.</summary>
public class MultiXmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider
{
/*********
** Properties
*********/
/// <summary>The internal documentation providers for specific files.</summary>
private readonly XmlDocumentationProvider[] Providers;
/*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
/// <param name='paths'>The physical paths to the XML documents.</param>
public MultiXmlDocumentationProvider(params string[] paths)
{
this.Providers = paths.Select(p => new XmlDocumentationProvider(p)).ToArray();
}
/// <summary>Gets the documentation for a subject.</summary>
/// <param name='subject'>The subject to document.</param>
public string GetDocumentation(MemberInfo subject)
{
return this.GetFirstMatch(p => p.GetDocumentation(subject));
}
/// <summary>Gets the documentation for a subject.</summary>
/// <param name='subject'>The subject to document.</param>
public string GetDocumentation(Type subject)
{
return this.GetFirstMatch(p => p.GetDocumentation(subject));
}
/// <summary>Gets the documentation for a subject.</summary>
/// <param name='subject'>The subject to document.</param>
public string GetDocumentation(HttpControllerDescriptor subject)
{
return this.GetFirstMatch(p => p.GetDocumentation(subject));
}
/// <summary>Gets the documentation for a subject.</summary>
/// <param name='subject'>The subject to document.</param>
public string GetDocumentation(HttpActionDescriptor subject)
{
return this.GetFirstMatch(p => p.GetDocumentation(subject));
}
/// <summary>Gets the documentation for a subject.</summary>
/// <param name='subject'>The subject to document.</param>
public string GetDocumentation(HttpParameterDescriptor subject)
{
return this.GetFirstMatch(p => p.GetDocumentation(subject));
}
/// <summary>Gets the documentation for a subject.</summary>
/// <param name='subject'>The subject to document.</param>
public string GetResponseDocumentation(HttpActionDescriptor subject)
{
return this.GetFirstMatch(p => p.GetDocumentation(subject));
}
/*********
** Private methods
*********/
/// <summary>Get the first valid result from the collection of XML documentation providers.</summary>
/// <param name='expr'>The method to invoke.</param>
private string GetFirstMatch(Func<XmlDocumentationProvider, string> expr)
{
return this.Providers
.Select(expr)
.FirstOrDefault(p => !String.IsNullOrWhiteSpace(p));
}
}
}
注意:開啟XML文件註釋的時候HelpPageConfig.cs需要註釋掉如下程式碼
#region *************************預設JSON顯示介面***************************************
//config.SetSampleForMediaType(
// new TextSample("Binary JSON content. See http://bsonspec.org for details."),
// new MediaTypeHeaderValue("application/bson"));
#endregion
編寫示例程式驗證API文件更新情況
PersonController.cs示例:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using WebAPI.Utils;
namespace WebAPI.Controllers
{
/// <summary>
/// 人物
/// </summary>
public class Person
{
/// <summary>
/// 唯一標識記錄ID
/// </summary>
public long Id { set; get; }
/// <summary>
/// 姓名
/// </summary>
public string Name { set; get; }
/// <summary>
/// 年齡
/// </summary>
public int Age { set; get; }
/// <summary>
/// 人物建構函式
/// </summary>
/// <param name="Id"></param>
/// <param name="Name"></param>
/// <param name="Age"></param>
public Person(long Id, string Name, int Age)
{
this.Id = Id;
this.Name = Name;
this.Age = Age;
}
}
/// <summary>
/// 結果物件
/// </summary>
public class Result
{
/// <summary>
/// 狀態:0失敗,1成功
/// </summary>
public int Status { set; get; }
/// <summary>
/// 資料
/// </summary>
public string Data { set; get; }
/// <summary>
/// 訊息
/// </summary>
public string Msg { set; get; }
/// <summary>
/// 結果建構函式
/// </summary>
/// <param name="Status"></param>
/// <param name="Data"></param>
/// <param name="Msg"></param>
public Result(int Status, string Data, string Msg)
{
this.Status = Status;
this.Data = Data;
this.Msg = Msg;
}
}
/// <summary>
/// 人物檢視控制器
/// </summary>
public class PersonController : ApiController
{
/// <summary>
/// 獲取所有人物
/// </summary>
/// <returns></returns>
public List<Person> GetPersons()
{
List<Person> perons = new List<Person>();
Person person = null;
for (var i=1;i<=10;i++)
{
person = new Person(i,"boonya"+1,new Random(100).Next());
perons.Add(person);
}
return perons;
}
/// <summary>
/// 獲取人物
/// </summary>
/// <param name="Id">唯一標識記錄ID</param>
/// <returns></returns>
public Person GetPerson(long Id)
{
return new Person(Id, "boonya" + Id, new Random(100).Next());
}
/// <summary>
/// 刪除人物
/// </summary>
/// <param name="Id">唯一標識記錄ID</param>
/// <returns></returns>
public Result DeletePerson(long Id)
{
return new Result(1,"","Id="+Id+"刪除成功");
}
/// <summary>
/// 新增人物
/// </summary>
/// <param name="Name">姓名</param>
/// <param name="Age">年齡</param>
/// <returns></returns>
public Result AddPerson(string Name,int Age)
{
Person person = new Person(new Random(100).Next(), Name, Age);
return new Result(1, JsonConvert.SerializeObject(person), "新增成功");
}
}
}
啟動專案執行,檢視API連結:詳細介面說明類似下面:
至此,我們看到介面的引數說明和返回值等,不用手動再去寫文件了。
注意:參考部落格內容裡面有怎麼生成專案的XML檔案。
配置自動生成介面測試頁面
新增引用NuGet安裝包:
安裝完成後執行就可以了:
點選“Test API”按鈕就可以測試了:
使用此安裝包不需要修改如下檔案: