3.校验通过的话,接口里需要能访问到对应的 Client 对象
var client = HttpContext.Items["client"] as Client;
public enum ClientIdSource { Query, Body, Route, Header }ValidateClientAttribute 实现
public class ValidateClientAttribute( ClientIdSource source = ClientIdSource.Query) : ActionFilterAttribute { /// <summary> /// 堆代码 duidaima.com /// 客户端ID的参数名称,注意是 DTO 里的属性名称,不是请求体JSON的字段名 /// </summary> public string ParameterName { get; set; } = "client_id"; /// <summary> /// 设置验证成功之后,存储在 `HttpContext.Items` 对象中的 `Client` 对象的 key /// </summary> public string ClientItemKey { get; set; } = "client"; public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { var clientId = ""; switch (source) { case ClientIdSource.Query: clientId = context.HttpContext.Request.Query[ParameterName]; break; case ClientIdSource.Body: // 使用反射从请求体中读取 client_id // 这里读取到的 body 是 Controller 下 Action 方法的第一个参数,通常是请求体中的 JSON 数据模型绑定转换为对应 DTO 实例 var body = context.ActionArguments.Values.FirstOrDefault(); if (body != null) { var clientProp = body.GetType().GetProperty(ParameterName); if (clientProp != null) { clientId = clientProp.GetValue(body) as string; } } break; case ClientIdSource.Route: clientId = context.RouteData.Values[ParameterName] as string; break; case ClientIdSource.Header: clientId = context.HttpContext.Request.Headers[ParameterName]; break; } if (string.IsNullOrWhiteSpace(clientId)) { throw new ArgumentNullException(ParameterName); } var clientRepo = context.HttpContext.RequestServices.GetRequiredService<IBaseRepository<Client>>(); var client = await clientRepo.Select.Where(a => a.ClientId == clientId).FirstAsync(); if (client != null) { context.HttpContext.Items["client"] = client; await next(); } else { context.Result = new NotFoundObjectResult( new ApiResponse { Message = $"client with id {clientId} not found" }); } } }有几点需要注意的,下面介绍一下:
其他几个参数位置还好,获取都比较容易,如果是 POST 或者 PUT 方法,一般都是把数据以 JSON 的形式放在 Request Body 里,这个时候,我们可以去读取这个 Body 的值,但读取完之后得自己解析 JSON,还得把 Stream 写回去,有点麻烦。而且如果 Body 是 XML 形式,还要用其他的解析方式。这里我使用了反射的方式,让 AspNetCore 框架去处理这个 Request Body ,然后我直接用反射,根据参数名去读取 Client ID。
使用
这是几个使用例子public class PwdLoginDto : LoginDto { [Required] [JsonPropertyName("username")] public string Username { get; set; } [Required] [JsonPropertyName("password")] public string Password { get; set; } }在接口中使用
[HttpPost("login/password")] [ValidateClient(ClientIdSource.Body, ParameterName = "ClientId")] public async Task<IActionResult> LoginByPassword(PwdLoginDto dto) { }参数在 Query Params 里
[HttpGet("authorize/url")] [ValidateClient(ClientIdSource.Query, ParameterName = "client_id")] public ApiResponse<string> GetAuthorizeUrl([FromQuery] AuthorizeInput input) { return new ApiResponse<string>(GenerateAuthorizeUrl(input)); }