1、定义个Swagger版本(组)的枚举
namespace WebApp.Enums
{
/// <summary>
/// api版本枚举
/// </summary>
public enum ApiVersion
{
/// <summary>
/// v1版本
/// </summary>
v1 = 1,
/// <summary>
/// v2版本
/// </summary>
v2 = 2,
}
}
2、添加SwaggerExtentsion扩展类,配置注册Swagger
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Net.Http.Headers;
using System.Net;
using System.Reflection;
using System.Text;
using WebApp.Enums;
namespace WebApp.Common.Swagger
{
/// <summary>
/// SwaggerDoc配置
/// 用法:在Program.cs文件中进行注册:builder.AddSwaggerGenExt();
/// </summary>
public static class SwaggerExtension
{
/// <summary>
/// 扩展方法:Swagger文档
/// </summary>
/// <param name="services"></param>
public static void AddSwaggerGenExt(this IServiceCollection services)
{
#region 添加Swagger
//获取的是当前执行的方法所在的程序文件的名称:即项目名称,例如我的项目名称叫webapp
var AssemblyName = Assembly.GetExecutingAssembly().GetName().Name;
services.AddSwaggerGen(options =>
{
#region 配置版本
//options.SwaggerDoc("v1", new OpenApiInfo { Title = "售楼API", Version = "v1" });
typeof(ApiVersion).GetEnumNames().ToList().ForEach(version =>
{
options.SwaggerDoc(version, new OpenApiInfo()
{
Version = version,
Title = "凤凰网管理系统",
Description = $"凤凰网接口服务 版本: {version}",
Contact = new OpenApiContact
{
Name = "潇湘夜雨",
Email = "123@qq.com"
}
});
});
#endregion
#region 配置注释文档
// 获取当前项目的 XML文档文件路径:比如我的项目名称叫WebApp,那么它默认的 XML文档文件路径就是当前项目下的 WebApp.xml
var xmlFile = $"{AssemblyName}.xml";
var xmlFileFullPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
//var domainXmlPath = Path.Combine(AppContext.BaseDirectory, "Bgy.Domain.xml"); // 获取Bgy.Domain.xml文件路径
//var viewmodelXmlPath = Path.Combine(AppContext.BaseDirectory, "Bgy.ViewModel.xml");// 获取Bgy.ViewModel.xml文件路径
options.IncludeXmlComments(xmlFileFullPath, true); // 添加控制器层注释,true表示显示控制器注释
//options.IncludeXmlComments(domainXmlPath); // 添加Domain层注释
//options.IncludeXmlComments(viewmodelXmlPath); // 添加ViewModel层注释
//对action的名称进行排序。
options.OrderActionsBy(o => o.RelativePath);
#endregion
#region 配置授权认证信息
//添加一个必须的全局安全信息,
//第一个参数是方案唯一名称:和AddSecurityDefinition方法指定的方案名称标识一致即可:BearerAuth
//第二个参数是方案的描述:可以是BasicAuthScheme、ApiKeyScheme的实例或OAuth2Scheme
options.AddSecurityDefinition("BearerAuth", new OpenApiSecurityScheme()
{
Description = "在下框中输入请求头中需要添加Jwt授权Token:Bearer Token",
Name = "Authorization",
In = ParameterLocation.Header, //配置jwt默认加在Authorization信息的位置:这里配置的是将jwt信息放在请求头Header中
Type = SecuritySchemeType.Http,//使用Authorize头部
Scheme = "bearer", //内容为以 bearer开头
BearerFormat = "JWT",
//Reference= new OpenApiReference() { Type = ReferenceType.SecurityScheme, Id = "bearerAuth" }
});
//注册全局认证(所有的接口都可以使用认证)
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "BearerAuth" //方案名称标识
}
},
new string[] {} //不设权限
}
});
#endregion
#region 在Swagger中扩展文件上传按钮
options.OperationFilter<FileUploadFilter>();
#endregion
});
#endregion
}
/// <summary>
/// 扩展方法:配置SwaggerUI
/// 用法:在Program.cs文件中进行注册:app.UseSwaggerUIExt();
/// </summary>
/// <param name="app"></param>
public static void UseSwaggerUIExt(this WebApplication app)
{
//SwaggerBasicAuthMiddleware:是我自己扩展的一个中间件:目的是需要登陆才能到达Swagger的Index页面中,否则无法进入:可以根据需要去掉这个
//需要安装:Swashbuckle.AspNetCore包
//app.UseMiddleware<SwaggerBasicAuthMiddleware>();
if (app.Environment.IsDevelopment())
{
//app.UseSwagger();
//app.UseSwaggerUI();
}
var enviroment = app.Configuration["Swagger:environmentVariables"];
switch (enviroment)
{
case "development":
app.UseSwagger();//启用Swagger中间件
app.UseSwaggerUI(options => //配置版本
{
typeof(ApiVersion).GetEnumNames().ToList().ForEach(version =>
{
options.SwaggerEndpoint($"/swagger/{version}/swagger.json", version);
});
});
break;
case "testing":
app.UseSwagger();
app.UseSwaggerUI(options =>
{
typeof(ApiVersion).GetEnumNames().ToList().ForEach(version =>
{
options.SwaggerEndpoint($"/swagger/{version}/swagger.json", version);
});
});
break;
case "production":
break;
}
}
}
#region 扩展功能
/// <summary>
/// 文件上传的扩展:实现在Swagger中上传文件的功能
/// </summary>
public class FileUploadFilter : IOperationFilter
{
/// <summary>
/// 文件上传筛选:只有上传文件的方法才添加此功能
/// </summary>
/// <param name="operation"></param>
/// <param name="context"></param>
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
const string FileUploadContentType = "multipart/form-data";
if (operation.RequestBody == null || !operation.RequestBody.Content.Any(x => x.Key.Equals(FileUploadContentType, StringComparison.InvariantCultureIgnoreCase)))
{
return;
}
if (context.ApiDescription.ParameterDescriptions[0].Type == typeof(IFormCollection))
{
operation.RequestBody = new OpenApiRequestBody
{
Description = "文件上传",
Content = new Dictionary<string, OpenApiMediaType>
{
{
FileUploadContentType,new OpenApiMediaType
{
Schema=new OpenApiSchema
{
Type="object",
Required=new HashSet<string>{ "file"},
Properties=new Dictionary<string, OpenApiSchema>
{
{
"file",new OpenApiSchema
{
Type="string",
Format="binary"
}
}
}
}
}
}
}
};
}
}
}
/// <summary>
/// 如何在ASP.Net Core的生产环境中保护swagger ui,也就是index.html页面。其实swagger是自带禁用的功能的,只需要设置开关即可。
/// 但是有一些场景,是需要把这些接口进行开放或者导出成文档供第三方进行调用,这个时候却又不想让所有人访问。
/// 这里介绍一种权限控制访问的方式,用来指定用户使用;
/// </summary>
public class SwaggerBasicAuthMiddleware
{
private readonly RequestDelegate next;
/// <summary>
/// 增加对swagger ui的验证
/// </summary>
/// <param name="next"></param>
public SwaggerBasicAuthMiddleware(RequestDelegate next)
{
this.next = next;
}
/// <summary>
/// 登陆功能实现
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task InvokeAsync(HttpContext context)
{
if (context.Request.Path.StartsWithSegments("/swagger"))
{
string authHeader = context.Request.Headers["Authorization"];
if (authHeader != null && authHeader.StartsWith("Basic "))
{
// Get the credentials from request header
var header = AuthenticationHeaderValue.Parse(authHeader);
var inBytes = Convert.FromBase64String(header.Parameter);
var credentials = Encoding.UTF8.GetString(inBytes).Split(':');
var username = credentials[0];
var password = credentials[1];
//用户身份认证
if (username.Equals("admin") && password.Equals("123456"))
{
await next.Invoke(context).ConfigureAwait(false);
return;
}
}
context.Response.Headers["WWW-Authenticate"] = "Basic";
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
else
{
await next.Invoke(context).ConfigureAwait(false);
}
}
}
#endregion
}
3、appsettings.json配置文件
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Swagger": {
"environmentVariables": "development" //:development :testing :production
}
}
4、在Program.cs中注册SwaggerDoc及启用SwaggerUI
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using WebApp.Common.Swagger;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
#region JWT鉴权授权
var audience = "Audience";
var issuer = "Issuer";
var securityKey = "SIGfMA0FCSqGSIb3DFEBAQUAA4GNADCBiQKBgQDI2a2EJ7d872v0afyoSDJT2o1+SitIeJSWtLJU8/Wz2m7gStexajkeD+Lka6DSTy8gt9UwfgVQo6uKjVLG5Ex7PiGOODVqAEghBuS7JzIYU5RvI593nNDAPfnJsas96mSA9Q/mD8RTE2drj6hf3oZjJpMPZUQI/B1Qjb5H3K3PNwIDAQAB";
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) //默认授权机制名称;
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,//是否验证Issuer
ValidateAudience = true,//是否验证Audience
ValidateLifetime = true,//是否验证失效时间
ValidateIssuerSigningKey = true,//是否验证SecurityKey
ValidAudience = audience,//Audience
ValidIssuer = issuer,//Issuer,这两项和前面签发jwt的设置一致 表示谁签发的Token
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securityKey))//拿到SecurityKey
};
});
#endregion
builder.Services.AddSwaggerGenExt();//SwaggerGen
var app = builder.Build();
app.UseSwaggerUIExt(); //SwaggerUI
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
5、Api接口中使用
在接口控制器,或者方法上添加版本(组)标识:[ApiExplorerSettings(GroupName = "v1")]
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using WebApp.Enums;
namespace WebApp.Controllers
{
/// <summary>
/// 测试接口
/// </summary>
[ApiController]//[ApiController]能够推断参数的绑定源,就不需要[FromBody][FromForm][FromHeader][FromQuery][FromRoute]....来主动指定接收参数的形式
[Route("api/[controller]/[action]")]
public class HomeController : ControllerBase
{
private readonly ILogger<HomeController> _logger;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="logger"></param>
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
/// <summary>
/// 查询案列1
/// </summary>
/// <param name="id">编号</param>
/// <returns></returns>
[HttpGet("Abc")] //url地址是:api/WeatherForecast/Get/Abc
[ApiExplorerSettings(GroupName = "v1")]
[Authorize]
public IActionResult Get(int id)
{
return Ok(id);
//返回值:IActionResult
//return NotFound(); 404
//return Redirect("/Home/Index");
//var content = "Hello, World!";
//return Content(content, "text/plain");
//var data = new { Name = "John", Age = 30 };
//return Json(data);
//var filePath = "/path/to/file.pdf";
//return File(filePath, "application/pdf", "filename.pdf");
//byte[] videoBytes = System.IO.File.ReadAllBytes(containerPath);
//return File(videoBytes, "video/mp4");
}
/// <summary>
/// 查询案列2:路由的伪静态
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[HttpGet("Abc/{name}")] //url地址是:api/WeatherForecast/Get/Abc/lily :lily是name值, 同时name值是必填的,{name}必须要与action的参数名称一致。这就是路由的伪静态形式
[ApiExplorerSettings(GroupName = nameof(ApiVersion.v1))]
[Authorize]
public IActionResult Get(string name)
{
return Ok(name);
}
/// <summary>
/// 客户端登陆
/// </summary>
/// <param name="clientid">客户端名称</param>
/// <param name="password">客户端密码</param>
/// <returns>返回jwtToken</returns>
[HttpGet]
[ApiExplorerSettings(GroupName = nameof(ApiVersion.v2))]
[Route("api/login")]
public IActionResult Login(string clientid, string password)
{
//这里肯定是需要去连接数据库做数据校验
if (clientid == "admin" && password == "123456")//应该数据库
{
string token = GetJwtToken(clientid);
return Ok(new { token });
}
else
{
return Ok("");
}
}
/// <summary>
/// 获取Token
/// </summary>
/// <param name="UserName"></param>
/// <returns></returns>
[NonAction]
public string GetJwtToken(string UserName)
{
var issuer = "Issuer";
var audience = "Audience";
var securityKey = "SIGfMA0FCSqGSIb3DFEBAQUAA4GNADCBiQKBgQDI2a2EJ7d872v0afyoSDJT2o1+SitIeJSWtLJU8/Wz2m7gStexajkeD+Lka6DSTy8gt9UwfgVQo6uKjVLG5Ex7PiGOODVqAEghBuS7JzIYU5RvI593nNDAPfnJsas96mSA9Q/mD8RTE2drj6hf3oZjJpMPZUQI/B1Qjb5H3K3PNwIDAQAB";
Claim[] claims = new[]
{
new Claim(ClaimTypes.Name, UserName)
};
SymmetricSecurityKey key = new(Encoding.UTF8.GetBytes(securityKey));
SigningCredentials creds = new(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: issuer,
audience: audience,
claims: claims,
expires: DateTime.Now.AddMinutes(1),//5分钟有效期
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
}
/// <summary>
/// 文件上传
/// </summary>
/// <param name="from"></param>
/// <returns></returns>
[HttpPost]
public JsonResult UploadFile(IFormCollection from)
{
return new JsonResult(new
{
Success = true,
Message = "上传成功",
FileName = from.Files.FirstOrDefault()?.FileName
}) ;
}
/// <summary>
/// 标记了[NonAction]特性,则不被视为控制器的操作方法
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpPost(Name = "{id}")]
[NonAction]
public string PostTest(int id)
{
return id.ToString();
}
}
}
6、项目配置生成XML文件
7、效果图
7、注意点:
如果只是单纯只返回token的时候,记得在控制器右上角的Authorize里 先写Bearer+空格+你的token