日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

浅析如何在Nancy中生成API文档

發布時間:2023/12/4 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 浅析如何在Nancy中生成API文档 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

前后端分離,或許是現如今最為流行開發方式,包括UWP、Android和IOS這樣的手機客戶端都是需要調用后臺的API來進行數據的交互。

但是這樣對前端開發和APP開發就會面臨這樣一個問題:如何知道每個API做什么?

可能,有人會在內部形成一份word文檔、pdf;有人會建立一個單獨的站點,然后將API的地址,參數等信息列在上面;有人會借助第三方的工具來生成一份文檔等。

當然,這基本是取決于不同公司的規范。

說起API文檔,就想到前段時間做的微信小程序,由于那個不完善的接口文檔,從而導致浪費了很大一部分時間去詢問接口相關的內容(用的是老的接口)。

為了處理這個問題,我認為,如果能在寫某個API的時候就順帶將這個API的相關信息一并處理了是最好不過!

不過這并不是讓我們寫好一個接口后,再去打開word等工具去編輯一下這個API的信息,這樣明顯需要花費更多的時間。

下面就針對這一問題,探討一下在Nancy中的實現。

如何實現

其實,想在Nancy中生成API文檔,是一件十分容易的事,因為作者thecodejunkie已經幫我們在Nancy內部提前做了一些處理

便于我們的后續擴展,這點還是很貼心的。

下面我們先來寫點東西,后面才能相應的API文檔。

public class ProductsModule : NancyModule{ ?
?public ProductsModule() : base("/products") ?
?
{ ? ? ? ?Get("/", _ =>{ ? ? ? ? ?
??return Response.AsText("product list");}, null, "GetProductList"); ? ?
??? ?Get("/{productid}", _ =>{ ? ? ? ? ?
??? ??return Response.AsText(_.productid as string);}, null, "GetProductByProductId"); ? ? ?
??? ?? ?Post("/", _ =>{ ? ? ? ? ?
??? ?? ? ?return Response.AsText("Add product");}, null, "AddProduct"); ? ? ? ?//省略部分..} }

基本的CURD,沒有太多的必要去解釋這些內容。當然這里需要指出一點。

正常情況下,我們基本都是只寫前面兩個參數的,后面兩個參數是可選的。由于我們后面需要用到每個路由的名字

所以我們需要用到這里的第4個參數(當前路由的名字),也就意味著我們要在定義的時候寫多一點東西!

注: 1.x和2.x的寫法是有區別的!示例用的2.x的寫法,所以各位要注意這點!

以GET為例,方法定義大致如下

API寫好了,下面我們先來簡單獲取一下這些api的相關信息!

最簡單的實現

前面也提到,我們是要把這個api和api文檔放到同一個站點下面,免去編輯這一步驟!

世間萬物都是相輔相成的,我們不想單獨編輯,自然就要在代碼里面多做一些處理!

新起一個Module名為DocModule,將api文檔的相關內容放到這個module中來處理。

public class DocMudule : NancyModule{
? ?private IRouteCacheProvider _routeCacheProvider; ?
? ?
? ? ?public DocMudule(IRouteCacheProvider routeCacheProvider) : base("/docs")
?
{ ? ?
? ? ?? ?this._routeCacheProvider = routeCacheProvider; ? ? ?
? ? ?? ? ?Get("/", _ =>{ ? ? ? ? ? ?var routeDescriptionList = _routeCacheProvider.GetCache().SelectMany(x => x.Value).Select(x => x.Item2).Where(x => !string.IsNullOrWhiteSpace(x.Name)).ToList(); ? ? ? ? ?
? ? ?? ? ??return Response.AsJson(routeDescriptionList);});} }

沒錯,你沒看錯,就是這幾行代碼,就可以幫助我們去生成我們想要的api文檔!其實最主要的是IRouteCacheProvider這個接口。

它的具體實現,會在后面的小節講到,現在先著重于使用!

先調用這個接口的GetCache方法,以拿到緩存的路由信息,這個路由信息有必要來看一下它的定義,因為不看它的定義,我們根本就沒有辦法繼續下去!

后續的查找都是依賴于這些緩存信息!

public interface IRouteCache : IDictionary<Type, List<Tuple<int, RouteDescription>>>,
ICollection<KeyValuePair<Type, List<Tuple<int, RouteDescription>>>>,
?IEnumerable<KeyValuePair<Type, List<Tuple<int, RouteDescription>>>>, IEnumerable

{ ? ?bool IsEmpty(); }

看了上面的定義,就可以清楚的知道要用SelectMany去拿到那個元組的內容。再取出元組的RouteDescription。

當然,這個時候我們取到的是所有的路由信息,這些信息都包含了什么內容呢?看看RouteDescription的定義就很清晰了。

public sealed class RouteDescription{ ? ?public RouteDescription(string name, string method, string path, Func<NancyContext, bool> condition); ? ?//The name of the routepublic string Name { get; set; } ? ?//The condition that has to be fulfilled inorder for the route to be a valid match.public Func<NancyContext, bool> Condition { get; } ? ?//The description of what the route is for.public string Description { get; set; } ? ?//Gets or sets the metadata information for a route.public RouteMetadata Metadata { get; set; } ? ?//Gets the method of the route.public string Method { get; } ? ?//Gets the path that the route will be invoked for.public string Path { get; } ? ?//Gets or set the segments, for the route, that was returned by the Nancy.Routing.IRouteSegmentExtractor.public IEnumerable<string> Segments { get; set; } }

在查詢之后,我還過濾了那些名字為空的,不讓它們顯示出來。為什么不顯示出來呢?理由也比較簡單,像DocModule,我們只定義了一個路由

而且這個路由在嚴格意義上并不屬于我們api的內容,而且這個路由也是沒有定義名字的,所以顯示出來的意義也不大。

過濾之后,就得到了最終想要的信息!簡單起見,這里是先直接 返回一個json對象,便于查看有什么內容,便于在逐步完善后再把它結構化。

下面是最簡單實現后的大致效果:

在圖中,可以看到GetProductListGetProductByProductId這兩個api的基本信息:請求的method,請求的路徑和路由片段。

但是這些信息真的是太少了!連api描述都見不到,拿出來,肯定被人狠狠的罵一頓!!

下面我們要嘗試豐富一下我們的接口信息!

豐富一點的實現

要讓文檔充實,總是需要一個切入點,找到切入點,事情就好辦了。仔細觀察上面的效果圖會發現,里面的metadata是空的。當然這個也就是豐富文檔內容的切入點了。

從前面的定義可以看到,這個metadata是一個RouteMetadata的實例

public class RouteMetadata{ ? ?
//Creates a new instance of the Nancy.Routing.RouteMetadata class.public RouteMetadata(IDictionary<Type, object> metadata); ?
?//Gets the raw metadata System.Collections.Generic.IDictionary`2.public IDictionary<Type, object> Raw { get; } ?
??//Gets a boolean that indicates if the specific type of metadata is stored.public bool Has<TMetadata>(); ? ?//Retrieves metadata of the provided type.public TMetadata Retrieve<TMetadata>(); }

這里對我們比較重要的是Raw這個屬性,因為這個是在返回結果中的一部分,它是一個字典,鍵是類型,值是這個類型對應的實例。

先定義一個CustomRouteMetadata,用于返回路由的Metadata信息(可根據具體情況進行相應的定義)。這個CustomRouteMetadata就是上述字典Type。

public class CustomRouteMetadata{ ? ?// group by the modulepublic string Group { get; set; } ? ?// description of the apipublic string Description { get; set; } ? ?// path of the apipublic string Path { get; set; } ? ?// http method of the apipublic string Method { get; set; } ? ?// name of the apipublic string Name { get; set; } ? ?// segments of the apipublic IEnumerable<string> Segments { get; set; } }

定義好我們要顯示的東西后,自然要把這些東西用起來,才能體現它們的價值。

要用起來還涉及到一個MetadataModule,這個命名很像NancyModule,看上去都是一個Module。

先定義一個ProductsMetadataModule,讓它繼承MetadataModule<RouteMetadata>,
具體實現如下:

public class ProductsMetadataModule : MetadataModule<RouteMetadata> { ? ?public ProductsMetadataModule() ? ?{ ? ? ? ? ? ?Describe["GetProductList"] = desc =>{ ? ? ? ? ? ? ? ?var dic = new Dictionary<System.Type, object>{{ ? ? ? ? ? ? ?
? ? ?typeof(CustomRouteMetadata), ? ? ? ? ? ? ?
? ? ?? ? ?new CustomRouteMetadata{Group = "Products",Description = "Get All Products from Database",Path = desc.Path,Method = desc.Method,Name = desc.Name,Segments = desc.Segments}}}; ? ? ? ? ? ?return new RouteMetadata(dic);};Describe["GetProductByProductId"] = desc =>{ ? ? ? ?
? ? ?? ? ?? ?var dic = new Dictionary<System.Type, object>{{ ? ? ? ? ? ? ? ? ?
? ? ?? ? ? ?typeof(CustomRouteMetadata), ? ? ? ? ? ?
? ? ?? ? ? ? ? ? ? ?new CustomRouteMetadata{Group = "Products",Description = "Get a Product by product id",Path = desc.Path,Method = desc.Method,Name = desc.Name,Segments = desc.Segments}}}; ? ? ? ? ? ?return new RouteMetadata(dic); ? ? ? ? ? ? ?}; ? ? ? ?//省略部分...} }

這里的寫法就和1.x里寫NancyModule的內容是一樣的,應該也是比較熟悉的。就不再累贅了。其中的desc是一個委托Func<RouteDescription, TMetadata>。

默認返回的是一個RouteMetadata實例,而要創建一個這樣的實例還需要一個字典,所以大家能看到上面的代碼中定義了一個字典。

并且這個字典包含了我們自己定義的信息,其中Group和Description是完全的自定義,其他的是從RouteDescription中拿。

當然,這里已經開了一個口子,想怎么定義都是可以的!

完成上面的代碼之后,再來看看我們顯示的結果

可以看到我們添加的metadata相關的內容已經出來了!可能這個時候,大家也都發現了,似乎內容有那么點重復的意思!

因為這些重復,就會讓人感覺這里比較臃腫,所以我們肯定不需要取出太多重復的東西,目前只需要metadata下面的這些就可以了。

下面來對其進行簡化!

簡化一點的實現

簡化分為兩步:

第一步簡化:DocModule的簡化。

其實,DocModule已經是相當的簡單了,但是還能在簡潔一點點。這里用到了RetrieveMetadata這個擴展方法來處理。

前面的做法是拿到路由的信息后,用了兩個Select來查詢,而且查詢出來的結果有那么一點臃腫,

而借助擴展方法,可以只取metadata里面的內容,也就是前面自定義的內容,這才是我們真正意義上要用到的。

下面是具體實現的示例:

Get("/", _ =>{//01//var routeDescriptionList = _routeCacheProvider// ? ? ? ? ? ? ? ? ? ? ? ? ? ?.GetCache()// ? ? ? ? ? ? ? ? ? ? ? ? ? ?.SelectMany(x => x.Value)// ? ? ? ? ? ? ? ? ? ? ? ? ? ?.Select(x => x.Item2)// ? ? ? ? ? ? ? ? ? ? ? ? ? ?.Where(x => !string.IsNullOrWhiteSpace(x.Name))// ? ? ? ? ? ? ? ? ? ? ? ? ? ?.ToList();//return Response.AsJson(routeDescriptionList);//02var routeDescriptionList = _routeCacheProvider.GetCache().RetrieveMetadata<RouteMetadata>().Where(x => x != null);return Response.AsJson(routeDescriptionList);});

經過第一步簡化后,已經過濾了不少重復的信息了,效果如下:

第二步簡化:Metadata的簡化

在返回Metadata的時候,我們是返回了一個默認的RouteMetadata對象,這個對象相比自定義的CustomRouteMetadata復雜了不少

而且從上面經過第一步簡化后的效果圖也可以發現,只有value節點下面的內容才是api文檔需要的內容。

所以還要考慮用自定義的這個CustomRouteMetadata去代替原來的。

修改如下:

public class ProductsMetadataModule : MetadataModule<CustomRouteMetadata> { ? ?public ProductsMetadataModule() ? ?{Describe["GetProductList"] = desc =>{ ? ? ? ? ? ?return new CustomRouteMetadata{Group = "Products",Description = "Get All Products from Database",Path = desc.Path,Method = desc.Method,Name = desc.Name,Segments = desc.Segments};};Describe["GetProductByProductId"] = desc =>{ ? ? ? ? ? ?return new CustomRouteMetadata{Group = "Products",Description = "Get a Product by product id",Path = desc.Path,Method = desc.Method,Name = desc.Name,Segments = desc.Segments};}; ? ? ? ?//省略部分..} }

由于MetadataModule<TMetadata> 中的TMetadata是自定義的CustomRouteMetadata,所以在返回的時候直接創建一個簡單的實例即可

不需要像RouteMetadata那樣還要定義一個字典。

同時,還要把DocModule中RetrieveMetadata的TMetadata也要替換成CustomRouteMetadata

var routeDescriptionList = _routeCacheProvider.GetCache() ? ? ? ? ? ? ? ? ? ? ? ? ? ?//.RetrieveMetadata<RouteMetadata>() ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?.RetrieveMetadata<CustomRouteMetadata>().Where(x => x != null);

經過這兩步的簡化,現在得到的效果就是我們需要的結果了!

最后,當然要專業一點,不能讓人只看json吧!怎么都要添加一個html頁面,將這些信息展示出來:

當然,現在看上去還是很丑,文檔內容也并不豐富,但是已經把最簡單的文檔做出來了,想要進一步豐富它就可以自由發揮了。

實現探討

既然這樣簡單的代碼就能幫助我們去生成api文檔,很有必要去研究一下Nancy幫我們做了什么事!

從最開始的IRouteCacheProvider入手,這個接口對應的默認實現DefaultRouteCacheProvider

public class DefaultRouteCacheProvider : IRouteCacheProvider, IDiagnosticsProvider{ ?
?/// <summary>/// The route cache factory/// </summary>protected readonly Func<IRouteCache> RouteCacheFactory; ? ?/// <summary>/// Initializes a new instance of the DefaultRouteCacheProvider class./// </summary>/// <param name="routeCacheFactory"></param>public DefaultRouteCacheProvider(Func<IRouteCache> routeCacheFactory) ?
?
{ ? ? ? ?this.RouteCacheFactory = routeCacheFactory;} ? ?/// <summary>/// Gets an instance of the route cache./// </summary>/// <returns>An <see cref="IRouteCache"/> instance.</returns>public IRouteCache GetCache() ? ?{ ? ?
?? ?return this.RouteCacheFactory();} ? ?//省略部分..}

里面的GetCache方法是直接調用了定義的委托變量。最終是到了IRouteCache的實現類RouteCache,這個類算是一個重點觀察對象!

內容有點多,就只貼出部分核心代碼了

它在構造函數里去生成了路由的相關信息。

public RouteCache( ? ?INancyModuleCatalog moduleCatalog, ? ?INancyContextFactory contextFactory, ? ?IRouteSegmentExtractor routeSegmentExtractor, ? ?IRouteDescriptionProvider routeDescriptionProvider, ? ?ICultureService cultureService, ? ?IEnumerable<IRouteMetadataProvider> routeMetadataProviders){
? ?this.routeSegmentExtractor = routeSegmentExtractor; ?
? ??this.routeDescriptionProvider = routeDescriptionProvider; ?
? ???this.routeMetadataProviders = routeMetadataProviders; ?
? ????var request = new Request("GET", "/", "http"); ?
? ?????using (var context = contextFactory.Create(request)){ ? ? ? ?this.BuildCache(moduleCatalog.GetAllModules(context));} }

具體的生成方法如下:遍歷所有的NancyModule,找到每個Module的RouteDescription集合(一個Module可以包含多個路由)

然后找到每個RouteDescription的描述,路由片段和metadata的信息。最后把這個Module路由信息添加到當前的對象中!

private void BuildCache(IEnumerable<INancyModule> modules){ ? ?

foreach (var module in modules){ ? ? ? ?var moduleType = module.GetType(); ? ?

? ?var routes =module.Routes.Select(r => r.Description).ToArray(); ? ?
? ? ? ?foreach (var routeDescription in routes){routeDescription.Description = this.routeDescriptionProvider.GetDescription(module, routeDescription.Path);routeDescription.Segments = this.routeSegmentExtractor.Extract(routeDescription.Path).ToArray();routeDescription.Metadata = this.GetRouteMetadata(module, routeDescription);} ? ? ? ?this.AddRoutesToCache(routes, moduleType);} }

前面提到RouteDescription的描述,路由片段和metadata的信息都是通過額外的方式拿到的,這里主要是拿metadata來做說明

畢竟在上面最后的一個例子中,用到的是metadata的內容。

先調用定義的私有方法GetRouteMetadata,這個方法里面的內容是不是和前面的MetadataModule有點類似呢,字典和創建RouteMetadata的實例。

private RouteMetadata GetRouteMetadata(INancyModule module, RouteDescription routeDescription){ ? ?var data = new Dictionary<Type, object>(); ? ?foreach (var provider in this.routeMetadataProviders){ ? ? ? ?var type = provider.GetMetadataType(module, routeDescription); ? ? ? ?var metadata = provider.GetMetadata(module, routeDescription); ? ? ? ?if (type != null && metadata != null){data.Add(type, metadata);}} ? ?return new RouteMetadata(data); }

重點的是provider。這個provider來源來IRouteMetadataProvider,這個接口就兩個方法。

Nancy這個項目中還有一個抽象類是繼承了這個接口的。但是這個抽象類是沒有默認實現的。

public abstract class RouteMetadataProvider<TMetadata> : IRouteMetadataProvider{ ? ?

public Type GetMetadataType(INancyModule module, RouteDescription routeDescription) ?
?
{ ? ? ? ?return typeof(TMetadata);
?} ?
?public object GetMetadata(INancyModule module, RouteDescription routeDescription) ?
?
{ ? ?
?? ?return this.GetRouteMetadata(module, routeDescription);} ?
?? ? ?protected abstract TMetadata GetRouteMetadata(INancyModule module, RouteDescription routeDescription); }

注:前面的原理分析都是基于Nancy這個項目。

這個時候,另外一個項目Nancy.Metadata.Modules就起作用了。我們編寫的MetadataModule也是要添加這個的引用才能正常使用的。

從上面編寫的MetadataModule可以看出這個項目的起點應該是MetadataModule,而且有關metadata的核心也在這里了。

public abstract class MetadataModule<TMetadata> : IMetadataModule where TMetadata :
class{ ?
?private readonly IDictionary<string, Func<RouteDescription, TMetadata>> metadata; ?
??protected MetadataModule() ?
?
{ ? ?
??? ?this.metadata = new Dictionary<string, Func<RouteDescription, TMetadata>>();} ?
?// Gets <see cref="RouteMetadataBuilder"/> for describing routes.public RouteMetadataBuilder Describe{ ? ? ? ?get { return new RouteMetadataBuilder(this); }} ?
?// Returns metadata for the given RouteDescription.public object GetMetadata(RouteDescription description) ?
?
{ ? ?
? ?if (this.metadata.ContainsKey(description.Name)){ ? ? ? ?
? ? ? ?return this.metadata[description.Name].Invoke(description);} ? ? ? ?return null;} ? ?// Helper class for configuring a route metadata handler in a module.public class RouteMetadataBuilder{ ? ? ? ?private readonly MetadataModule<TMetadata> parentModule; ?
? ? ? ?
? ? ? ?public RouteMetadataBuilder(MetadataModule<TMetadata> metadataModule) ? ?
? ?
{ ? ? ? ? ? ?this.parentModule = metadataModule;} ?
? ? ? ?// Describes metadata for a route with the specified name.public Func<RouteDescription, TMetadata> this[string name]{ ? ? ? ?
? ? ? ?? ?set { this.AddRouteMetadata(name, value); }} ? ? ?
? ? ? ?? ?
? ??protected void AddRouteMetadata(string name, Func<RouteDescription, TMetadata> value) ? ? ? ?{ ? ? ? ? ? ?this.parentModule.metadata.Add(name, value);}} ? ?//省略部分..}

到這里,已經將GetCache的內內外外都簡單分析了一下。至于擴展方法RetrieveMetadata就不在細說了,只是selectmany和select的一層封裝。

寫在最后

本文粗略講解了如何在Nancy中生成API文檔,以及簡單分析了其內部的處理。

下一篇將繼續介紹這一塊的內容,不過主角是Swagger。

原文地址:http://www.cnblogs.com/catcher1994/p/6791352.html


.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注

總結

以上是生活随笔為你收集整理的浅析如何在Nancy中生成API文档的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 成人动漫亚洲 | 成人性生生活性生交全黄 | 国产精品女人久久久 | 欧美一区二区三区免费视频 | 美女张开腿让人桶 | 日韩成人一区二区视频 | 中文字幕在线观看精品 | 伊人av综合网 | 亚洲女人被黑人巨大进入 | 国产在线一区二区 | 久久午夜福利电影 | 国产美女毛片 | 婷婷在线免费 | 一区二区三区久久久久 | 欧美女人一区二区 | 91九色在线 | 丰满少妇影院 | 无码人妻一区二区三区免费 | 国产精品免费视频一区二区三区 | 人妻精品一区二区三区 | 亚洲福利| 亚洲一区二区三区无码久久 | 精品国产乱码久久久人妻 | 欧美精品91 | 久久精品国产一区二区 | 日韩欧美小视频 | 精品爆乳一区二区三区无码av | 伊人88| 超碰av免费| 久久夜夜操 | 欧美,日韩,国产精品免费观看 | 国产做a视频 | 亚洲色图欧美色 | 人人舔 | 午夜一区不卡 | 国产亚洲精品久久久久丝瓜 | 超碰毛片 | av老司机在线播放 | 看黄色一级 | 日日夜夜精品免费 | 色婷婷国产 | 欧美香蕉 | 怡红院男人的天堂 | 国产夫妻在线视频 | 91吃瓜在线 | 欧美com | 成人中文字幕+乱码+中文字幕 | 综合伊人av | 麻豆免费网站 | 五月激情婷婷综合 | 色小说香蕉 | 婷婷亚洲天堂 | 黄色一级大片在线免费看国产 | 欧美不卡在线观看 | 亚卅色图 | 亚洲天天av | 国产中文一区二区三区 | 国产又粗又大又长 | 欧美激情免费视频 | 久久免费视频观看 | 最新中文字幕av | 麻豆md0034在线观看 | 午夜国产福利在线观看 | 澳门av在线| 特黄一级大片 | 国产精品伦一区 | 久久久久女人精品毛片九一 | 亚洲欧美999 | 在线va| 人妻精品无码一区二区三区 | 国产精品一区麻豆 | 涩涩屋视频在线观看 | 一区二区精品久久 | 日韩一区二区三区精品 | 又欲又污又肉又黄短文 | 国产成年视频 | 久久久国产精品无码 | 极品丰满少妇 | 老司机av影院 | 不卡视频在线观看 | 人妻一区二区视频 | 色婷婷av一区二区 | 成人黄色一级 | 中文字幕理论片 | 久久精品屋 | 精品视频在线观看一区 | 国产美女激情 | 久久黄色小说 | 黑人无套内谢中国美女 | 成年人午夜视频 | 亚洲高清视频在线观看 | 欧美毛茸茸 | 日本不卡在线观看 | 国产成人久久精品77777综合 | 熟女俱乐部一区二区 | 色屁屁www | 久免费一级suv好看的国产 | 污视频在线观看免费 | 国产91啪 |