Asp.netCore WebApi初体验之搭建Swagger并在IIS上部署
1、新建.netCore webapi项目
2、安装swagger ,通过 Package Manager 控制台:Install-Package
3、Startup.cs中注册服务并添加至管道
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Threading.Tasks; 5 using Microsoft.AspNetCore.Builder; 6 using Microsoft.AspNetCore.Hosting; 7 using Microsoft.AspNetCore.HttpsPolicy; 8 using Microsoft.AspNetCore.Mvc; 9 using Microsoft.Extensions.Configuration; 10 using Microsoft.Extensions.DependencyInjection; 11 using Microsoft.Extensions.Logging; 12 using Microsoft.Extensions.Options; 13 14 namespace CoreTest 15 { 16 public class Startup 17 { 18 public Startup(IConfiguration configuration) 19 { 20 Configuration = configuration; 21 } 22 public IConfiguration Configuration { get; } 23 // This method gets called by the runtime. Use this method to add services to the container. 24 public void ConfigureServices(IServiceCollection services) 25 { 26 services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 27 28 //1、注册服务Swagger 29 services.AddSwaggerGen(options => 30 { 31 options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info 32 { 33 Version = "v1", 34 Title = "My API", 35 Description = "by JiaJia" 36 }); 37 }); 38 } 39 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 40 public void Configure(IApplicationBuilder app, IHostingEnvironment env) 41 { 42 if (env.IsDevelopment()) 43 { 44 app.UseDeveloperExceptionPage(); 45 } 46 else 47 { 48 app.UseHsts(); 49 } 50 app.UseHttpsRedirection(); 51 app.UseMvc(); 52 53 //2、添加到管道 54 #if DEBUG 55 app.UseSwagger(); 56 app.UseSwaggerUI(c => 57 { 58 c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); 59 // c.DocExpansion(DocExpansion.None); 60 }); 61 #endif 62 } 63 } 64 }
4、访问 Swagger UI 地址
http://localhost:60238/swagger/index.html
5、添加备注
1)在项目属性的 生成 => 输出 中勾选 XML文档文件。
2)在 Start.cs => ConfigureServices
方法中的 AddSwaggerGen
处增加 IncludeXmlComments
处理。
1 //1、注册服务Swagger 2 services.AddSwaggerGen(options => 3 { 4 options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info 5 { 6 Version = "v1", 7 Title = "My API", 8 Description = "by JiaJia" 9 }); 10 //在 Start.cs => ConfigureServices 方法中的 AddSwaggerGen 处增加 IncludeXmlComments 处理。 11 options.IncludeXmlComments(string.Format("{0}/CoreTest.xml", 12 AppDomain.CurrentDomain.BaseDirectory)); 13 14 /*或者这种添加方式//为 Swagger JSON and UI设置xml文档注释路径 15 var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);//获取应用程序所在目录(绝对,不受工作目录影响,建议采用此方法获取路径) 16 var xmlPath = Path.Combine(basePath, "CoreTest.xml");//和项目名对应 17 options.IncludeXmlComments(xmlPath);*/ 18 });
3)控制器对Action添加注释信息
1 /// <summary> 2 /// 根据ID获取用户信息 3 /// </summary> 4 /// <param name="id">用户ID</param> 5 /// <returns>用户信息</returns> 6 [HttpGet("{id}")] 7 public ActionResult<string> Get(int id) 8 { 9 return "user info"; 10 }
4)(指定输出路径默认空白,需要填写:bin\Debug\),而且默认的(CoreTest.xml)xml文件,与Startup的文件必须对应(否则会报错)
5)不想每一个方法都这么加注释,可以这么配置(对当前项目进行配置,可以忽略警告,记得在后边加上分号 ;1591)
最终效果:
二、接口执行时间分析——MiniProfiler
搭建MiniProfiler
安装引入nuget包:
Install-Package MiniProfiler.AspNetCore.Mvc
然后,在startup.cs 中配置服务ConfigureServices:
1 //注册服务MiniProfiler 2 services.AddMiniProfiler(options => 3 { 4 options.RouteBasePath = "/profiler";//注意这个路径要和下边 index.html 脚本配置中的一致, 5 (options.Storage as MemoryCacheStorage).CacheDuration = TimeSpan.FromMinutes(10); 6 });
3、最后,调用下中间件即可:
1 //调用MiniProfiler中间件 2 app.UseMiniProfiler();
在 Swagger 中配置 MiniProfiler
上边我们在配置中已经启动了服务,接下来就需要设置如何在 swagger 中展示了,这个时候我们就需要自定义我们的swagger主页了,以前我们是用的默认的index.html,现在咱们需要自定义一个:
从官网 Github 上,下载最新的 index.html:https://github.com/swagger-api/swagger-ui/blob/master/dist/index.html
1 <!-- HTML for static distribution bundle build --> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <meta charset="UTF-8"> 6 <title>%(DocumentTitle)</title> 7 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 8 <link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet"> 9 <link rel="stylesheet" type="text/css" href="./swagger-ui.css"> 10 <link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" /> 11 <link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" /> 12 <style> 13 14 15 html { 16 box-sizing: border-box; 17 overflow: -moz-scrollbars-vertical; 18 overflow-y: scroll; 19 } 20 21 *, 22 *:before, 23 *:after { 24 box-sizing: inherit; 25 } 26 27 body { 28 margin: 0; 29 background: #fafafa; 30 } 31 </style> 32 %(HeadContent) 33 </head> 34 <body> 35 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0"> 36 <defs> 37 <symbol viewBox="0 0 20 20" id="unlocked"> 38 <path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path> 39 </symbol> 40 <symbol viewBox="0 0 20 20" id="locked"> 41 <path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z" /> 42 </symbol> 43 <symbol viewBox="0 0 20 20" id="close"> 44 <path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z" /> 45 </symbol> 46 <symbol viewBox="0 0 20 20" id="large-arrow"> 47 <path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z" /> 48 </symbol> 49 <symbol viewBox="0 0 20 20" id="large-arrow-down"> 50 <path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z" /> 51 </symbol> 52 53 <symbol viewBox="0 0 24 24" id="jump-to"> 54 <path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z" /> 55 </symbol> 56 <symbol viewBox="0 0 24 24" id="expand"> 57 <path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z" /> 58 </symbol> 59 </defs> 60 </svg> 61 <div id="swagger-ui"></div> 62 <!-- Workaround for https://github.com/swagger-api/swagger-editor/issues/1371 --> 63 <script> 64 if (window.navigator.userAgent.indexOf("Edge") > -1) { 65 console.log("Removing native Edge fetch in favor of swagger-ui's polyfill") 66 window.fetch = undefined; 67 } 68 </script> 69 <script src="./swagger-ui-bundle.js"></script> 70 <script src="./swagger-ui-standalone-preset.js"></script> 71 <script> 72 window.onload = function () { 73 var configObject = JSON.parse('%(ConfigObject)'); 74 var oauthConfigObject = JSON.parse('%(OAuthConfigObject)'); 75 76 // Apply mandatory parameters 77 configObject.dom_id = "#swagger-ui"; 78 configObject.presets = [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset]; 79 configObject.layout = "StandaloneLayout"; 80 81 // If oauth2RedirectUrl isn't specified, use the built-in default 82 if (!configObject.hasOwnProperty("oauth2RedirectUrl")) 83 configObject.oauth2RedirectUrl = window.location.href.replace("index.html", "oauth2-redirect.html"); 84 85 // Build a system 86 const ui = SwaggerUIBundle(configObject); 87 88 // Apply OAuth config 89 ui.initOAuth(oauthConfigObject); 90 } 91 </script> 92 </body> 93 </html>
将该文件天机到项目中,并设置成嵌入资源的类型:
接下来,在 Index.html 文件中,增加配置脚本(我是在顶部写的,Head应该也可以):
<script async="async" id="mini-profiler" src="/profiler/includes.min.js?v=4.0.138+gcc91adf599" data-version="4.0.138+gcc91adf599" data-path="/profiler/" data-current-id="4ec7c742-49d4-4eaf-8281-3c1e0efa748a" data-ids="" data-position="Left" data-authorized="true" data-max-traces="15" data-toggle-shortcut="Alt+P" data-trivial-milliseconds="2.0" data-ignored-duplicate-execute-types="Open,OpenAsync,Close,CloseAsync"> </script>
包括参数比如版本,/profiler的路径,position的位置显示,authorized的是否权限,max-traces最多显示多少条(15)等等。
修改后的网页代码如下
<script async="async" id="mini-profiler" src="/profiler/includes.min.js?v=4.0.138+gcc91adf599" data-version="4.0.138+gcc91adf599" data-path="/profiler/" data-current-id="4ec7c742-49d4-4eaf-8281-3c1e0efa748a" data-ids="" data-position="Left" data-authorized="true" data-max-traces="15" data-toggle-shortcut="Alt+P" data-trivial-milliseconds="2.0" data-ignored-duplicate-execute-types="Open,OpenAsync,Close,CloseAsync"> </script> <!-- HTML for static distribution bundle build --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>%(DocumentTitle)</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet"> <link rel="stylesheet" type="text/css" href="./swagger-ui.css"> <link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" /> <link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" /> <style> html { box-sizing: border-box; overflow: -moz-scrollbars-vertical; overflow-y: scroll; } *, *:before, *:after { box-sizing: inherit; } body { margin: 0; background: #fafafa; } </style> %(HeadContent) </head> <body> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0"> <defs> <symbol viewBox="0 0 20 20" id="unlocked"> <path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path> </symbol> <symbol viewBox="0 0 20 20" id="locked"> <path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z" /> </symbol> <symbol viewBox="0 0 20 20" id="close"> <path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z" /> </symbol> <symbol viewBox="0 0 20 20" id="large-arrow"> <path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z" /> </symbol> <symbol viewBox="0 0 20 20" id="large-arrow-down"> <path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z" /> </symbol> <symbol viewBox="0 0 24 24" id="jump-to"> <path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z" /> </symbol> <symbol viewBox="0 0 24 24" id="expand"> <path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z" /> </symbol> </defs> </svg> <div id="swagger-ui"></div> <!-- Workaround for https://github.com/swagger-api/swagger-editor/issues/1371 --> <script> if (window.navigator.userAgent.indexOf("Edge") > -1) { console.log("Removing native Edge fetch in favor of swagger-ui's polyfill") window.fetch = undefined; } </script> <script src="./swagger-ui-bundle.js"></script> <script src="./swagger-ui-standalone-preset.js"></script> <script> window.onload = function () { var configObject = JSON.parse('%(ConfigObject)'); var oauthConfigObject = JSON.parse('%(OAuthConfigObject)'); // Apply mandatory parameters configObject.dom_id = "#swagger-ui"; configObject.presets = [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset]; configObject.layout = "StandaloneLayout"; // If oauth2RedirectUrl isn't specified, use the built-in default if (!configObject.hasOwnProperty("oauth2RedirectUrl")) configObject.oauth2RedirectUrl = window.location.href.replace("index.html", "oauth2-redirect.html"); // Build a system const ui = SwaggerUIBundle(configObject); // Apply OAuth config ui.initOAuth(oauthConfigObject); } </script> </body> </html>
然后我们修改下中间件去调用我们这个 index.html 页面:
1 app.UseSwaggerUI(c => 2 { 3 c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); 4 // 将swagger首页,设置成我们自定义的页面,记得这个字符串的写法:解决方案名.index.html 5 c.IndexStream = () => GetType().GetTypeInfo().Assembly.GetManifestResourceStream("CoreTest.index.html"); 6 });
配置使用静态资源文件的中间件
app.UseStaticFiles();
完整的Startup.cs文件
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Reflection; 5 using System.Threading.Tasks; 6 using Microsoft.AspNetCore.Builder; 7 using Microsoft.AspNetCore.Hosting; 8 using Microsoft.AspNetCore.Mvc; 9 using Microsoft.Extensions.Configuration; 10 using Microsoft.Extensions.DependencyInjection; 11 using Microsoft.Extensions.Logging; 12 using Microsoft.Extensions.Options; 13 using StackExchange.Profiling.Storage; 14 15 namespace CoreTest 16 { 17 public class Startup 18 { 19 public Startup(IConfiguration configuration) 20 { 21 Configuration = configuration; 22 } 23 public IConfiguration Configuration { get; } 24 // This method gets called by the runtime. Use this method to add services to the container. 25 public void ConfigureServices(IServiceCollection services) 26 { 27 services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 28 //1、注册服务Swagger 29 services.AddSwaggerGen(options => 30 { 31 options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info 32 { 33 Version = "v1", 34 Title = "My API", 35 Description = "by JiaJia" 36 }); 37 options.IncludeXmlComments(string.Format("{0}/CoreTest.xml", AppDomain.CurrentDomain.BaseDirectory)); 38 }); 39 //注册服务MiniProfiler 40 services.AddMiniProfiler(options => 41 { 42 options.RouteBasePath = "/profiler";//注意这个路径要和下边 index.html 脚本配置中的一致, 43 (options.Storage as MemoryCacheStorage).CacheDuration = TimeSpan.FromMinutes(10); 44 }); 45 } 46 47 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 48 public void Configure(IApplicationBuilder app, IHostingEnvironment env) 49 { 50 if (env.IsDevelopment()) 51 { 52 app.UseDeveloperExceptionPage(); 53 } 54 //2、Swagger添加到管道 55 #if DEBUG 56 app.UseSwagger(); 57 app.UseSwaggerUI(c => 58 { 59 c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); 60 // 将swagger首页,设置成我们自定义的页面,记得这个字符串的写法:解决方案名.index.html 61 c.IndexStream = () => GetType().GetTypeInfo().Assembly.GetManifestResourceStream("CoreTest.index.html"); 62 }); 63 #endif 64 //2、MiniProfiler添加到管道 65 app.UseMiniProfiler(); 66 67 app.UseMvc(); 68 } 69 } 70 }
控制器代码
1 /// <summary> 2 /// 根据ID获取用户信息 3 /// </summary> 4 /// <param name="id">用户ID</param> 5 /// <returns>用户信息</returns> 6 [HttpGet("{id}")] 7 public ActionResult<IEnumerable<string>> Get() 8 { 9 string url1 = string.Empty; 10 string url2 = string.Empty; 11 using (MiniProfiler.Current.Step("Get方法")) 12 { 13 using (MiniProfiler.Current.Step("准备数据")) 14 { 15 using (MiniProfiler.Current.CustomTiming("SQL", "SELECT * FROM Config")) 16 { 17 // 模拟一个SQL查询 18 Thread.Sleep(500); 19 20 url1 = "https://www.baidu.com"; 21 url2 = "https://www.sina.com.cn/"; 22 } 23 } 24 25 26 using (MiniProfiler.Current.Step("使用从数据库中查询的数据,进行Http请求")) 27 { 28 using (MiniProfiler.Current.CustomTiming("HTTP", "GET " + url1)) 29 { 30 var client = new WebClient(); 31 var reply = client.DownloadString(url1); 32 } 33 34 using (MiniProfiler.Current.CustomTiming("HTTP", "GET " + url2)) 35 { 36 var client = new WebClient(); 37 var reply = client.DownloadString(url2); 38 } 39 } 40 } 41 return new string[] { "value1", "value2" }; 42 }
现在已经配置好了 MiniProfiler 和 Swagger了,运行页面左上角