ABP+AdminLTE+Bootstrap Table權(quán)限管理系統(tǒng)第五節(jié)--WBEAPI及SwaggerUI

ABP+AdminLTE+Bootstrap Table權(quán)限管理系統(tǒng)一期
Github:https://github.com/Jimmey-Jiang/ABP-ASP.NET-Boilerplate-Project-CMS
前往博客園總目錄:ABP+AdminLTE+Bootstrap Table權(quán)限管理系統(tǒng)一期

一,Web API

ABP的動(dòng)態(tài)WebApi實(shí)現(xiàn)了直接對(duì)服務(wù)層的調(diào)用(其實(shí)病沒有跨過ApiController,只是將ApiController公共化击罪,對(duì)于這一點(diǎn)的處理類似于MVC沿量,對(duì)服務(wù)端的 調(diào)用沒有跨過HttpHandler一樣)辩块,這樣不僅減少了ApiController的開發(fā)趴久,也更能體現(xiàn)驅(qū)動(dòng)領(lǐng)域設(shè)計(jì)的層結(jié)構(gòu)融蹂。
WEBAPI同樣集成自AbpController, 我們看到AbpController封裝了很多方法,集成了很多東西,繼承自System.Web.Mvc.Controller,abp包含一下功能
1. 本地化
2. 異常處理
3. 對(duì)返回的JsonResult進(jìn)行包裝
4. 審計(jì)日志
5. 權(quán)限認(rèn)證([AbpMvcAuthorize]特性)
6. 工作單元(默認(rèn)未開啟,通過添加[UnitOfWork]開啟)
7. 錯(cuò)誤機(jī)制的響應(yīng).

這里就不細(xì)講,會(huì)在第五章詳講.但是又多了結(jié)果緩存,ABP為WebAPI請(qǐng)求的響應(yīng)添加緩存控制header(no-cache,no-store)廓潜。因此阱佛,它阻止瀏覽器緩存相應(yīng),甚至GET請(qǐng)求也會(huì)被阻止緩存耍缴±危可以在配置里禁用此功能。
HTTP特性:一般包含HttpGet防嗡、HttepPost,HttpDelete,HttpPut等特性.一般HttpGet变汪、HttepPost,HttpDelete比較常用.
api路由:可以重定義路由,在外部調(diào)用的時(shí)候.如[Route("api/friends/{friendId}")]

二, SwaggerUI

前面已經(jīng)建好服務(wù)層的接口了,可以通過接口直接去請(qǐng)求數(shù)據(jù)庫.平時(shí)我們請(qǐng)求數(shù)據(jù)都是在控制器里面去注入接口,然后調(diào)用方法,其實(shí)還有一種更好的實(shí)現(xiàn),我們可以不通過控制器,直接在頁面js請(qǐng)求的時(shí)候直接請(qǐng)求服務(wù)接口,ABP集成了Swagger Api.而且這個(gè)界面非常的nice.

  1. 選擇WEBAPI添加nuget包,輸入"Swagger ",會(huì)出來智能的出來Swashbuckle和Swagger.Net.UI兩個(gè)包,引用其中一個(gè)就可以了,但是不能同時(shí)引用,否則生成的時(shí)候回會(huì)報(bào)錯(cuò)路由錯(cuò)誤.
  1. 引用到項(xiàng)目中之后,打開WebApiModule.cs,創(chuàng)建ConfigureSwaggerUi方法
  2. 然后在重寫的然Initialize()中對(duì)他進(jìn)行調(diào)用。

運(yùn)行項(xiàng)目,依然是WEB為啟動(dòng)項(xiàng)目,不需要設(shè)置WEBAPI啟動(dòng),就看到了以下界面.



至此,ABP的WEBAPI基本已經(jīng)事項(xiàng),但是還是存在以下問題.

  1. 界面的說明都是英文的需要進(jìn)行漢化,既然是接口文檔,前端人員在用的時(shí)候需要文字說明.
  2. 控制器沒有描述
  3. 接口過多每次生成速度比較慢
  4. 關(guān)于調(diào)用接口會(huì)不會(huì)暴露,
  5. 移動(dòng)端調(diào)用方式
  6. 調(diào)用如何確保安全
  7. 身份驗(yàn)證的方式

首先我們先在application生成xml文檔勾選上



然后修改一下ConfigureSwaggerUi方法



在修改一下DTO注釋驗(yàn)證,當(dāng)然我們已經(jīng)加上驗(yàn)證了

運(yùn)行一下,效果出來了.



修改提示語言為中文,一個(gè) 是注入JavaScript文件蚁趁,一個(gè)是輸入css文件裙盾。所以如果這里可以對(duì)我們的SwaggerUI界面進(jìn)行自定義修改的。
  1. 需要將js文件路徑注入到SwaggerUI中他嫡。

2. 寫入js文件


然而你在運(yùn)行的發(fā)現(xiàn)報(bào)錯(cuò),其實(shí)這里有兩個(gè)套路,自古深情留不起,唯有套路得人心,謹(jǐn)記啊,所以看以下三點(diǎn):
3. 我們來看WEBAPI路徑



4. js文件設(shè)置嵌入的資源


  1. 還有一種是在webapimodule中關(guān)閉 CSRF功能

或者js里面寫入以下帶代碼.



做完以上四步,我們?cè)賮砜闯晒?



以上就是Swagger ui 運(yùn)用及部分漢化.
其他終端在調(diào)用接口是的時(shí)候的身份驗(yàn)證方式,第一種就是cookie認(rèn)證方式番官;
第二種就是token認(rèn)證方式:在訪問webapi之前,先要向目標(biāo)系統(tǒng)申請(qǐng)令牌(token)钢属,申請(qǐng)到令牌后徘熔,再使用令牌訪問webapi。Abp默認(rèn)提供了這種方式淆党;

第三種是基于OAuth2.0的token認(rèn)證方式:OAuth2.0認(rèn)證方式彌補(bǔ)了Abp自帶token認(rèn)證的短板酷师,即無法進(jìn)行token刷新,這些東西其實(shí)我也還沒有完全搞懂,會(huì)在后續(xù)學(xué)習(xí)的過程中加上。

///    <summary>
/// 中文轉(zhuǎn)換
///    </summary>
var SwaggerTranslator = (function () {
    //定時(shí)執(zhí)行檢測(cè)是否轉(zhuǎn)換成中文,最多執(zhí)行500次  即500*50/1000=25s
    var iexcute = 0,
    //中文語言包
    _words = {
        "Warning: Deprecated": "警告:已過時(shí)",
        "Implementation Notes": "實(shí)現(xiàn)備注",
        "Response Class": "響應(yīng)類",
        "Status": "狀態(tài)",
        "Parameters": "參數(shù)",
        "Parameter": "參數(shù)",
        "Value": "值",
        "Description": "描述",
        "Parameter Type": "參數(shù)類型",
        "Data Type": "數(shù)據(jù)類型",
        "Response Messages": "響應(yīng)消息",
        "HTTP Status Code": "HTTP狀態(tài)碼",
        "Reason": "原因",
        "Response Model": "響應(yīng)模型",
        "Request URL": "請(qǐng)求URL",
        "Response Body": "響應(yīng)體",
        "Response Code": "響應(yīng)碼",
        "Response Headers": "響應(yīng)頭",
        "Hide Response": "隱藏響應(yīng)",
        "Headers": "頭",
        "Try it out!": "試一下染乌!",
        "Show/Hide": "顯示/隱藏",
        "List Operations": "顯示操作",
        "Expand Operations": "展開操作",
        "Raw": "原始",
        "can't parse JSON.  Raw result": "無法解析JSON. 原始結(jié)果",
        "Model Schema": "模型架構(gòu)",
        "Model": "模型",
        "apply": "應(yīng)用",
        "Username": "用戶名",
        "Password": "密碼",
        "Terms of service": "服務(wù)條款",
        "Created by": "創(chuàng)建者",
        "See more at": "查看更多:",
        "Contact the developer": "聯(lián)系開發(fā)者",
        "api version": "api版本",
        "Response Content Type": "響應(yīng)Content Type",
        "fetching resource": "正在獲取資源",
        "fetching resource list": "正在獲取資源列表",
        "Explore": "瀏覽",
        "Show Swagger Petstore Example Apis": "顯示 Swagger Petstore 示例 Apis",
        "Can't read from server.  It may not have the appropriate access-control-origin settings.": "無法從服務(wù)器讀取山孔。可能沒有正確設(shè)置access-control-origin慕匠。",
        "Please specify the protocol for": "請(qǐng)指定協(xié)議:",
        "Can't read swagger JSON from": "無法讀取swagger JSON于",
        "Finished Loading Resource Information. Rendering Swagger UI": "已加載資源信息饱须。正在渲染Swagger UI",
        "Unable to read api": "無法讀取api",
        "from path": "從路徑",
        "Click to set as parameter value": "點(diǎn)擊設(shè)置參數(shù)",
        "server returned": "服務(wù)器返回"
    },

    //定時(shí)執(zhí)行轉(zhuǎn)換
     _translator2Cn = function () {
         if ($("#resources_container .resource").length > 0) {
             _tryTranslate();
         }

         if ($("#explore").text() == "Explore" && iexcute < 500) {
             iexcute++;
             setTimeout(_translator2Cn, 50);
         }
     },

     //設(shè)置控制器注釋
     _setControllerSummary = function () {
         $.ajax({
             type: "get",
             async: true,
             url: $("#input_baseUrl").val(),
             dataType: "json",
             success: function (data) {
                 var summaryDict = data.ControllerDesc;
                 var id, controllerName, strSummary;
                 $("#resources_container .resource").each(function (i, item) {
                     id = $(item).attr("id");
                     if (id) {
                         controllerName = id.substring(9);
                         strSummary = summaryDict[controllerName];
                         if (strSummary) {                            
                             $(item).children(".heading").children(".options").prepend('<li class="controller-summary" title="' + strSummary + '">' + strSummary + '</li>');
                         }
                     }
                 });
             }
         });
     },

     //嘗試將英文轉(zhuǎn)換成中文
    _tryTranslate = function () {
        $('[data-sw-translate]').each(function () {
            $(this).html(_getLangDesc($(this).html()));
            $(this).val(_getLangDesc($(this).val()));
            $(this).attr('title', _getLangDesc($(this).attr('title')));
        });
    },
    _getLangDesc = function (word) {
        return _words[$.trim(word)] !== undefined ? _words[$.trim(word)] : word;
    };

    return {
        Translator: function () {
            document.title = "API描述文檔";
            $('body').append('<style type="text/css">.controller-summary{color:#10a54a !important;word-break:keep-all;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:250px;text-align:right;cursor:default;} </style>');
            $("#logo").html("接口描述").attr("href", "/Home/Index");
            //設(shè)置控制器描述
            _setControllerSummary();
            _translator2Cn();         
        }
    }
})();
//執(zhí)行轉(zhuǎn)換
SwaggerTranslator.Translator();

控制器描述和接口文檔緩存

public class CachingSwaggerProvider : ISwaggerProvider
    {
        private static ConcurrentDictionary<string, SwaggerDocument> _cache =
            new ConcurrentDictionary<string, SwaggerDocument>();

        private readonly ISwaggerProvider _swaggerProvider;

        public CachingSwaggerProvider(ISwaggerProvider swaggerProvider)
        {
            _swaggerProvider = swaggerProvider;
        }

        public SwaggerDocument GetSwagger(string rootUrl, string apiVersion)
        {
            var cacheKey = string.Format("{0}_{1}", rootUrl, apiVersion);
            SwaggerDocument srcDoc = null;
            //只讀取一次
            if (!_cache.TryGetValue(cacheKey, out srcDoc))
            {
                srcDoc = _swaggerProvider.GetSwagger(rootUrl, apiVersion);

                srcDoc.vendorExtensions = new Dictionary<string, object> { { "ControllerDesc", GetControllerDesc() } };
                _cache.TryAdd(cacheKey, srcDoc);
            }
            return srcDoc;
        }

        /// <summary>
        /// 從API文檔中讀取控制器描述
        /// </summary>
        /// <returns>所有控制器描述</returns>
        public static ConcurrentDictionary<string, string> GetControllerDesc()
        {
            string xmlpath = string.Format("{0}/bin/SwaggerDemo.XML", System.AppDomain.CurrentDomain.BaseDirectory);
            ConcurrentDictionary<string, string> controllerDescDict = new ConcurrentDictionary<string, string>();
            if (File.Exists(xmlpath))
            {
                XmlDocument xmldoc = new XmlDocument();
                xmldoc.Load(xmlpath);
                string type = string.Empty, path = string.Empty, controllerName = string.Empty;

                string[] arrPath;
                int length = -1, cCount = "Controller".Length;
                XmlNode summaryNode = null;
                foreach (XmlNode node in xmldoc.SelectNodes("http://member"))
                {
                    type = node.Attributes["name"].Value;
                    if (type.StartsWith("T:"))
                    {
                        //控制器
                        arrPath = type.Split('.');
                        length = arrPath.Length;
                        controllerName = arrPath[length - 1];
                        if (controllerName.EndsWith("Controller"))
                        {
                            //獲取控制器注釋
                            summaryNode = node.SelectSingleNode("summary");
                            string key = controllerName.Remove(controllerName.Length - cCount, cCount);
                            if (summaryNode != null && !string.IsNullOrEmpty(summaryNode.InnerText) && !controllerDescDict.ContainsKey(key))
                            {
                                controllerDescDict.TryAdd(key, summaryNode.InnerText.Trim());
                            }
                        }
                    }
                }
            }
            return controllerDescDict;
        }
    }

漢化結(jié)果:



返回簡(jiǎn)書總目錄:ABP+AdminLTE+Bootstrap Table權(quán)限管理系統(tǒng)一期
前往博客園總目錄:ABP+AdminLTE+Bootstrap Table權(quán)限管理系統(tǒng)一期

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市台谊,隨后出現(xiàn)的幾起案子蓉媳,更是在濱河造成了極大的恐慌,老刑警劉巖锅铅,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酪呻,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡盐须,警方通過查閱死者的電腦和手機(jī)玩荠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人阶冈,你說我怎么就攤上這事闷尿。” “怎么了女坑?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵填具,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我匆骗,道長(zhǎng)劳景,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任碉就,我火速辦了婚禮盟广,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瓮钥。我一直安慰自己筋量,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布碉熄。 她就那樣靜靜地躺著毛甲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪具被。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天只损,我揣著相機(jī)與錄音一姿,去河邊找鬼。 笑死跃惫,一個(gè)胖子當(dāng)著我的面吹牛叮叹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播爆存,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蛉顽,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了先较?” 一聲冷哼從身側(cè)響起携冤,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎闲勺,沒想到半個(gè)月后曾棕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡菜循,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年翘地,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡衙耕,死狀恐怖昧穿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情橙喘,我是刑警寧澤时鸵,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站渴杆,受9級(jí)特大地震影響寥枝,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜磁奖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一囊拜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧比搭,春花似錦冠跷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至霉赡,卻和暖如春橄务,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背穴亏。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工蜂挪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嗓化。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓棠涮,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親刺覆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子严肪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353