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了,运行页面左上角
切面 MiniFilter分析
1、只需要在 AOP 日志记录中,配置 MiniProfiler 即可:
来源:oschina
链接:https://my.oschina.net/u/4394481/blog/3321604