8,RESTful风格
8.1,什么是RESTful?
REST全称是Representational State Transfer,中文意思是表述性状态转移。
REST本身并没有创造新的技术、组件或服务
REST指的是一组架构约束条件和原则,如果一个架构符合REST的约束条件和原则,我们就称他为RESTfull架构。
理论上REST架构风格并不是绑定在Http上,只不过目前Http是唯一与REST相关的实例。所以我们这里描述的REST也是通过Http实现REST.
8.2,REST主要规则是什么?
8.2.1,资源与URL,所有的资源都有一个资源标志符
Core webApi 访问一块资源
https://loaclhost:8000/Company ---代表Company的一块资源
https://localhost:8000/User ---代表User的一块资源
8.2.2,同一资源接口
html
冥等性:对同一个Rest接口的多次访问,得到的资源状态时相同的。
安全性:对该REST接口访问,不会是服务器端资源的状态发生改变。
8.2.3,资源的表述
服务端可以通过Content-Type告诉客户端资源的表述形式。
资源的表述形式有xml,html,json等格式,图片可以使用PNG,Jpeg展现出来
8.2.4,资源的链接
在设计RESTfull架构时,使用很多时间来寻找漂亮的URI,而忽略了超媒体。所以,应给多花一些时间来给资源的表述提供链接,而不是专注于资源的CURD。
8.2.5,状态的转移
原则上要求无状态通信原则
9,Core WebApi返回结果中文转义问题
设置json序列化中支持的编码器解决返回结果中文转义问题
C#
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//设置序列化的编码器,解决编码转义问题
services.AddControllers().AddJsonOptions(jsonOption=> {
jsonOption.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(System.Text.Unicode.UnicodeRanges.All);
})
;
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebAPICore应用", Version = "v1" });
});
}
}
10,Swagger配置
作用: 根据Controller与Action自动生成OpenAPI风格的文档,该文档支持多版本文档的切换查阅。

10.1 安装Swagger

一般情况在创建WebApiCore项目时选择OpenApi支持选项,选择该选项后创建项目时自动安装该Nuget。

10.2 配置Swagger
配置均是在StartUp.cs文件进行
10.2.1,解决编码转义问题
C#
public void ConfigureServices(IServiceCollection services)
{
//解决编码转义问题
services.AddControllers().AddJsonOptions(jsonOption=> {
jsonOption.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(System.Text.Unicode.UnicodeRanges.All);
})
}
10.2.2,版本控制(忽略这个,直接查阅第12节版本控制)
C#
public void ConfigureServices(IServiceCollection services)
{
//解决编码转义问题
services.AddControllers().AddJsonOptions(jsonOption=> {
jsonOption.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(System.Text.Unicode.UnicodeRanges.All);
})
;
//若要启用Swagger的版本控制需要在api控制器或者方法上添加特性[ApiExplorerSettings(GroupName="版本号"]
//1,生成不同版本的json文档
#region 分版本Swagger配置1
Enum.GetNames(typeof(Utility.Swagger.ApiVersions)).ToList().ForEach(version =>
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc(version, new OpenApiInfo
{
Title = $"枫林OpenAPI {version}",
Version = "v1",
Description = $"通用版本的CoreApi版本{version}"
});
});
});
}
C#
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
//2,SwaggerUI使用不同版本的Swagger json文件
#region 分版本的Swagger配置2
app.UseSwaggerUI(c =>
{
Enum.GetNames(typeof(Utility.Swagger.ApiVersions)).ToList().ForEach(version =>
c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"WebAPICore应用 {version}"));
});
#endregion
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
添加Swagger版本控制特性([ApiExplorerSettings])的控制器
C#
[ApiController]
[Route("[controller]")]
//在Swagger版本1中呈现
[ApiExplorerSettings(GroupName =nameof(Utility.Swagger.ApiVersions.V1),IgnoreApi =false)]
public class WeatherForecastController : ControllerBase
{
}
10.2.3,配置Swagger显示注释
1,在项目属性中设置输出xml文档文件

2,配置展示注释
C#
#region 配置展示注释
services.AddSwaggerGen(option =>
{
//注释路径位置
var xmlfile = System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.FullyQualifiedName;
string commentfile = xmlfile.Substring(0, xmlfile.LastIndexOf('.')) + ".xml";
option.IncludeXmlComments(commentfile, true);
//对action的名称进行排序,如果有多个,就可以看见效果
option.OrderActionsBy(apiDesciotion => apiDesciotion.RelativePath);
});
#endregion

10.2.4,Token传递
添加访问认证Token要求
C#
#region 扩展传入token
//添加安全定义
option.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "请输入token,格式为 Bearer **** (注意中间必须是空格)",
Name = "Authorization",
In = ParameterLocation.Header,
BearerFormat = "JWT",
Scheme = "Bearer"
});
//添加安全要求
option.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference=new OpenApiReference
{
Type= ReferenceType.SecurityScheme,
Id="Bearer"
}
},new List<string>() }
});
#endregion


10.2.5,扩展文件上传按钮
C#
#region 扩展文件上传,展示文件上传按钮
option.OperationFilter<Utility.Swagger.FileUploadFilter>();
#endregion
C#
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;
}
//只有Action的形参类型为IFormCollection才展示按钮
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"} }
}
}
}
}
}
};
}
}
}

11,全局路由扩展
添加全局路由前缀
C#
public void ConfigureServices(IServiceCollection services)
{
//配置路由前缀
services.AddControllers(option =>
{
option.Conventions.Insert(0, new Utility.Route.RouteConvention(new RouteAttribute("api/")));
});
//配置Swagger
services.AddSwaggerGenExt();
}
C#
/// <summary>
/// 全局路由前缀配置
/// </summary>
public class RouteConvention : IApplicationModelConvention
{
/// <summary>
/// 定义一个路由前缀变量
/// </summary>
readonly AttributeRouteModel _centralPrefix;
/// <summary>
/// 调用时传入指定的路由前缀
/// </summary>
/// <param name="routeTemplateProvider"></param>
public RouteConvention(IRouteTemplateProvider routeTemplateProvider)
{
_centralPrefix = new AttributeRouteModel(routeTemplateProvider);
}
public void Apply(ApplicationModel application)
{
foreach (var controller in application.Controllers)
{
//1,已经标记了RouteAttribute的Controller
foreach (var selectorModel in controller.Selectors.Where(item=>item.AttributeRouteModel!=null))
{
//在当前路由上再添加一个路由前缀
selectorModel.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(_centralPrefix, selectorModel.AttributeRouteModel);
}
//2,没有标记RouteAttribute的Controller
foreach (var unSelectorModel in controller.Selectors.Where(item => item.AttributeRouteModel != null))
{
//在当前路由上再添加一个路由前缀
unSelectorModel.AttributeRouteModel = _centralPrefix;
}
}
}
}
12,版本控制
Nuget:
Microsoft.AspNetCore.Mvc.Versioning
Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer

C#
public void ConfigureServices(IServiceCollection services)
{
//配置路由前缀
//services.AddControllers(option =>
//{
// option.Conventions.Insert(0, new Utility.Route.RouteConvention(new RouteAttribute("api/")));
//});
//配置Swagger
services.AddSwaggerGenExt();
//配置Swagger多版本控制
services.AddSwaggerGenWithVersioning(Enum.GetNames(typeof(Utility.Swagger.ApiVersions)));
}
C#
/// <summary>
/// 控制swagger多版本生成
/// </summary>
/// <param name="services"></param>
/// <param name="apiVersions"></param>
public static void AddSwaggerGenWithVersioning(this IServiceCollection services, string[] apiVersions)
{
services.AddSwaggerGen(m =>
{
// 注册每个版本
foreach (var version in apiVersions)
{
m.SwaggerDoc(version, new OpenApiInfo
{
Title = $"枫林 API {version.ToUpper()}",
Version = version
});
}
// 冲突处理
m.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
// 根据控制器上的 ApiVersionAttribute 筛选文档
m.DocInclusionPredicate((docName, apiDesc) =>
{
if (!apiDesc.TryGetMethodInfo(out var methodInfo)) return false;
var versions = methodInfo.DeclaringType?
.GetCustomAttributes(true)
.OfType<ApiVersionAttribute>()
.SelectMany(attr => attr.Versions)
.Select(v => $"v{v.MajorVersion}")
.ToList();
return versions?.Contains(docName) ?? false;
});
});
// 配置 API 版本控制
services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0); // 默认版本为 1.0
options.AssumeDefaultVersionWhenUnspecified = true; // 未指定版本时使用默认版本
options.ReportApiVersions = true; // 返回支持的版本信息
options.ApiVersionReader = ApiVersionReader.Combine(
new QueryStringApiVersionReader("version"), // 从查询字符串读取版本号
new UrlSegmentApiVersionReader(), // 从 URL 路径读取版本号
new HeaderApiVersionReader("X-API-Version"), // 从请求头读取版本号
new MediaTypeApiVersionReader("version") // 从媒体类型读取版本号
);
});
}
C#
namespace WebAPICore应用.Controllers.v1
{
//[Route("api/[controller]")]
[ApiController]
[ApiVersion("1.0")]//指明版本
[Route("api/v{version:apiVersion}/[controller]")]
//控制器上添加swagger的
// [ApiExplorerSettings(IgnoreApi =false,GroupName =nameof(Utility.Swagger.ApiVersions.V1))]
public class UserController : ControllerBase
{
// GET: api/<UserController>
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "V1版本", "V1版本" };
}
}
}
C#
namespace WebAPICore应用.Controllers.v2
{
//[Route("api/[controller]")]
[ApiController]
// [ApiExplorerSettings(IgnoreApi = false, GroupName = nameof(Utility.Swagger.ApiVersions.V2))]
[ApiVersion("2.0")]//指明版本
[Route("api/v{version:apiVersion}/[controller]")]
public class UserController : ControllerBase
{
// GET: api/<UserController>
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "V2版本", "V2版本" };
}
}
}


13,API参数特性修饰
13.1,FromServices
- 表示来自于IOC容器创建。
- 必然需要在IOC容器先注册
- 如果没有标记
[FromServices],则默认这个参数需要通过调用方传递。
13.2,FromBody
搜集来自于客户端请求的参数中来自于Body的数据。
13.3,FromForm
搜集来自于客户端Form表单中的参数。
13.4,FromHeader
搜集来自于客户端请求的Header参数数据。
13.5,FromQuery
搜集来自客户端请求的查询字符串参数数据。
13.6,FromRoute
在来自客户端请求的路由中去收集这个参数的数据
14,日志记录
14.1,log4net日志记录
支持文本日志,数据库日志
-
Nuget引入程序包log4net+Microsoft.Extensions.Logging.Log4Net.AspNetCore -
准备配置文件【设置为始终复制】
-
植入
log4netC#builder.Logging.AddLog4Net("log4net.config"); -
注入
log对象,写日志。
15,AOP面向切面编程
15.1,什么是AOP?
AOP(Aspect orient Programming),面向切面编程,作为面向对象编程的一种补充,可以在不破坏之前的封装为基础动态增加一些功能,从而让系统更具可扩展性。
15.2,Core WebApi中的AOP支持有哪些?
- 授权------Authorize
- 资源------Resource
- 异常------Exception
- 方法前后------Action
- AlwayRunResult
- 结果前后------Result
15.3,如何使用带有注入参数的Fillter
**使用[ServiceFilter]或者[TypeFilter]**为方法或者类引入带有注入参数Fillter特性。实际上测试发现使用[ServiceFilter]抛异常提示需要注册,而[TypeFiler]正常。
15.3,IResourceFilter扩展定制
-
定义类,实现
IResourceFilter/IAsyncRessourceFilter接口,继承Attribute类 -
实现方法
-
标记在Api方法上
-
执行时机:API方法执行前与执行后
-
适用场景:为缓存而生。缓存:历史存储区域,以key-value格式保存数据。
C#
/// <summary>
/// 扩展缓存
/// </summary>
public class CustomCacheResourceFilterAttribute : Attribute, IResourceFilter
{
//定义一个缓存字典
static Dictionary<string, object> cacheDictionary = new Dictionary<string, object>();
//顺序:A
/// <summary>
/// api执行后执行
/// </summary>
/// <param name="context"></param>
public void OnResourceExecuted(ResourceExecutedContext context)
{
//api 执行完成后缓存请求url
string requestPath = context.HttpContext.Request.Path;
cacheDictionary[requestPath] = context.Result;
}
//顺序:B
/// <summary>
/// api执行前执行
/// </summary>
/// <param name="context"></param>
public void OnResourceExecuting(ResourceExecutingContext context)
{
//查询是否已经存储过该请求
string requestPath = context.HttpContext.Request.Path;//Url地址
if (cacheDictionary.ContainsKey(requestPath))
{
//如果包含则获取键值后直接返回不再执行后面的api方法,
//如果Result==null,则继续执行后面的api方法
context.Result = cacheDictionary[requestPath] as IActionResult;
}
}
}
C#
//顺序:C
[HttpGet("{id}")]
[Utility.Filters.CustomCacheResourceFilter]//扩展缓存功能
public IActionResult Get(int id)
{
return new JsonResult(new {
Id=id,
Method=nameof(Get),
DataTime=DateTime.Now
});
}
执行顺序为:B->构造方法->C->A
第一次请求

第二次请求

因为获取的是上次的缓存所以结果一模一样。
15.4,IActionFilter扩展定制
-
定义类,实现
IActionFilter/IAsyncActionFilter接口,继承Attribute类 -
实现方法
-
标记在Api方法上
-
执行时机:API方法执行前与执行后
-
特点
- 与ResourceFilter相比
- ResourceFilter使用连个方法包裹了控制器的构造方法+Api
- ActionFilter使用连个办法只包裹了
API逻辑部分。
- 与ResourceFilter相比
-
ActionFilter场景使用:
- 可以扩展为缓存,但是相对而言
ResourceFilter性能更高些,请求处理的环节更少 ActionFillter更靠近api所以一般用于扩展日志功能
- 可以扩展为缓存,但是相对而言
ResourceFilter流程图 (以上文自定义的CustomCacheResourceFilterAttribute为例)
客户端缓存存在
缓存不存在
客户端请求
OnResourceExecuting
获取缓存值
取缓存值推送至客户端
执行控制器构造方法
执行API方法
结果推送至客户端
执行OnResourceExecuted保存此次结果用于缓存
ActionFilter流程图
缓存值存在
缓存不存在
客户端请求
执行控制器构造方法
OnActionExecuting
取缓存值返回至客户端
执行API方法
结果推送至客户端
执行OnResourceExecuted保存此次结果用于缓存
ResourceFilter在缓存存在时不用调用控制器构造方法,而ActionFilter不管缓存存不存在每次都需要调用构造方法。所以在缓存方面ResourceFilter性能优于ActionFilter。
实现IActionFilter的自定义类
C#
public class CustomLogActionFilterAttribute : Attribute, IActionFilter
{
log4net.ILog logger;
public CustomLogActionFilterAttribute(log4net.ILog logger)
{
this.logger = logger;
}
public void OnActionExecuted(ActionExecutedContext context)
{
string controllerName = context.HttpContext.Request.RouteValues["controller"]?.ToString();
string methodName = context.ActionDescriptor.RouteValues["action"];
logger.Info($"访问{controllerName}.{methodName}执行OnActionExecuted");
}
public void OnActionExecuting(ActionExecutingContext context)
{
string controllerName = context.HttpContext.Request.RouteValues["controller"]?.ToString();
string methodName = context.HttpContext.Request.RouteValues["action"]?.ToString();
logger.Info($"访问{controllerName}.{methodName}执行OnActionExecuting");
}
}
C#
[HttpGet]
//[Utility.Filters.CustomCacheResourceFilter]//扩展缓存功能
[TypeFilter(typeof(Utility.Filters.CustomLogActionFilterAttribute))]
//使用ServicceFilter需要注册否则抛异常
// [ServiceFilter(typeof(Utility.Filters.CustomLogActionFilterAttribute))]
public IEnumerable<string> Get()
{
logger.Info($"访问{nameof(FiltersController)}.{nameof(Get)}");
return new string[] { "value1", "value2" };
}
text
2025-12-29 21:16:51,025 [4] INFO loggerAppender [29] - 访问Filters.Get执行OnActionExecuting
2025-12-29 21:16:53,130 [4] INFO loggerAppender [30] - 访问FiltersController.Get
2025-12-29 21:17:14,546 [4] INFO loggerAppender [20] - 访问Filters.Get执行OnActionExecuted
15.5,IExceptionFilter扩展定制
C#
public class CustomExceptionFilterAttribute : Attribute, IExceptionFilter
{
public void OnException(ExceptionContext context)
{
if (!context.ExceptionHandled)
{
context.Result = new JsonResult(new
{
success=false,
msg=context.Exception.Message,
});
context.ExceptionHandled = true;
}
}
}
C#
[HttpGet("Calculate/{val}")]
[Utility.Filters.CustomExceptionFilter]
public double Calculate(int val)
{
return 12 / val;
}
正常时:

异常时:以自定义的形式向客户端推送信息。

15.6,IAsyncAuthorizationFilter鉴权实现整个过程
引入NuGet包:Microsoft.IdentityModel.JsonWebTokens

C#
public class AppCredential
{
public string ClientId { get; set; }
public string Secret { get; set; }
}
public class Application
{
public int ApplicationId { get; set; }
public string ApplicationName { get; set; }
public string ClientId { get; set; }
public string Secret { get; set; }
public string Scopes { get; set; }
}
public class AppRepository
{
static List<Application> _applications = new List<Application>
{
new Application
{
ApplicationId=1,
ApplicationName="webapi",
ClientId="aa-bb-cc",
Secret="893F4720-F3E5-46A4-BBE9-B3B2BB6E0F2E",
Scopes="read,write"
}
};
public static bool Authenticate(string ClientId, string Secret)
{
return _applications.Exists(application =>
{
return application.ClientId.Equals(ClientId, StringComparison.OrdinalIgnoreCase) && application.Secret.Equals(Secret, StringComparison.OrdinalIgnoreCase);
});
}
public static Application GetApplicationByClientId(string clientId)
{
return _applications.FirstOrDefault(app => app.ClientId.Equals(clientId, StringComparison.OrdinalIgnoreCase));
}
}
C#
public class Authenticator
{
public static string CreateToken(string clientId, DateTime expiresAt, string strSecurityKey)
{
//包含三部分 header,payload,Signature
//Algorithm:算法。秘钥+算法
//Sha256的SecurityKey的长度需要是'256'个 bit,即32个byte
//所以为兼容所有秘钥长度这里转换为md5
string securityKey = string.Empty;
using (var md5 = MD5.Create())
{
//md5计算结果为16个byte
byte[] keyBytes = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(strSecurityKey));
securityKey = keyBytes.Select(b => b.ToString("X2")).Aggregate((a, b) => a + b);
}
var signingCredentials = new SigningCredentials(
new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(securityKey)),
SecurityAlgorithms.HmacSha256Signature
);
//Payload(Claims)
Application app = AppRepository.GetApplicationByClientId(clientId);
var claimsDictionary = new Dictionary<string, object>
{
{"AppName",app.ApplicationName },
{"Read",app.Scopes?.Contains("read",StringComparison.OrdinalIgnoreCase) },
{"Write",app.Scopes?.Contains("write",StringComparison.OrdinalIgnoreCase) }
};
var tokenDescriptior = new SecurityTokenDescriptor
{
SigningCredentials = signingCredentials,
Claims = claimsDictionary,//负载
Expires = expiresAt,//过期时间
NotBefore = DateTime.UtcNow//不能早于这个时间
};
var tokenHandler = new JsonWebTokenHandler();
return tokenHandler.CreateToken(tokenDescriptior);
}
public static async Task<bool> VerifyTokenAsync(string tokenString, string securityKey)
{
if (string.IsNullOrEmpty(tokenString) || string.IsNullOrEmpty(securityKey))
{
return false;
}
string strSecurityKey = string.Empty;
using (var md5 = MD5.Create())
{
//md5计算结果为16个byte
byte[] keyBytes = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(securityKey));
strSecurityKey = keyBytes.Select(b => b.ToString("X2")).Aggregate((a, b) => a + b);
}
var keyByetes = System.Text.Encoding.UTF8.GetBytes(strSecurityKey);
var tokenHander = new JsonWebTokenHandler();
var validationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(keyByetes),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,//验证有效期
ClockSkew = TimeSpan.Zero//时钟偏差量
};
try
{
var result = await tokenHander.ValidateTokenAsync(tokenString, validationParameters);
return result.IsValid;
}
catch (SecurityTokenMalformedException)
{
return false;
}
catch (SecurityTokenExpiredException)
{
return false;
}
catch (SecurityTokenInvalidSignatureException)
{
return false;
}
catch (Exception ex)
{
throw ex;
}
}
}
C#
public class JwtTokenAuthFilterAttribute : Attribute, IAsyncAuthorizationFilter
{
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
//1,Get Authorization header from the request
if(!context.HttpContext.Request.Headers.TryGetValue("Authorization",out var authorizationHeader))
{
context.Result = new UnauthorizedResult();
return;
}
//2,Get rid of the Bearer prefix
string tokenString = authorizationHeader;
if (tokenString.StartsWith("Bearer", StringComparison.OrdinalIgnoreCase))
{
tokenString = tokenString.Substring("Bearer".Length).Trim();
}
else
{
context.Result = new UnauthorizedResult();
return;
}
//3,Get Configuration and the Secret Key
var configuration = context.HttpContext.RequestServices.GetService(typeof(IConfiguration)) as IConfiguration;
var securityKey = configuration?.GetValue<string>("SecurityKey");
//4,Verify the token
if(!await Authenticator.VerifyTokenAsync(tokenString, securityKey))
{
context.Result = new UnauthorizedResult();
}
}
}
使用特性
C#
[HttpGet]
//添加鉴权要求
[JwtTokenAuthFilter]
public IEnumerable<string> Get()
{
return new string[] { "V1版本", "V1版本" };
}
未添加Authorization Bearer时:

添加 Bearer Token后返回所需值

15.7,Filter的生效范围和依赖注入
- 方法注册------当前方法有效
- 控制器注册------当前控制器有效
- 全局注册------全局有效
全局注册示例:
在StartUp.cs中进行配置:
C#
//配置全局ExceptionFilter
services.AddControllers(option =>
{
option.Filters.Add(typeof(Utility.Filters.CustomExceptionFilterAttribute));
});
Demo链接
https://download.csdn.net/download/lingxiao16888/92544537?spm=1001.2014.3001.5501