我正在编写一个自定义 ActionFilterAttribute,以便在请求命中 .Net Core Web API 中的操作之前处理一些业务逻辑。 (核心v1.1) OnActionExecuting 已成功命中处理程序,但我无法提取来自请求正文的以 JSON 格式发送的数据。
我尝试了几种方法,例如读取 ActionExecutingContext 流主体(为空)、访问 Form 属性(但由于它是 json,因此不起作用)以及其他一些解决方案,但没有运气。
这是我的 ActionFilter 的代码,它是空的,因为这里的问题基本上是需要提取其数据,所以进一步的代码是无关紧要的。
public class AccountRestrictionAttribute : ActionFilterAttribute
{
public override async void OnActionExecuting(ActionExecutingContext context)
{
}
}
答案归功于rynowak,我在这里找到了它https://github.com/aspnet/Mvc/issues/5260
ModelBinding 在操作过滤器之前运行,因此如果您有表单数据或 [FromBody] 参数,我们已经读取了它。
是的,如果您位于操作过滤器中,则 context.ActionArguments 将包含我们创建的所有模型对象。所以如果你有:
public IActionResult Edit(int id, [FromBody] Widget widget) { }
然后 context.ActionArguments["widget"] 将返回 Widget 对象。如果您尝试以通用方式执行此操作,请查看 context.ActionDescriptor.Parameters - 这将包含所有参数定义和元数据。
如果您想读取操作过滤器内的请求正文,您需要使用
StreamReader
并启用缓冲,如下例所示...
public class AccountRestrictionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var body = string.Empty;
var request = context.HttpContext.Request;
request.EnableBuffering();
if(request.Body.CanSeek) request.Body.Position = 0;
using (var s = new StreamReader(request.Body, encoding: Encoding.UTF8,
detectEncodingFromByteOrderMarks: false, leaveOpen: true))
{
body = s.ReadToEnd();
}
var account = JsonConvert.DeserializeObject(body);
//account restriction logic below
}
}
我将流保持打开状态,以防您想在不同的中间件(例如日志记录)中进行进一步处理。以下是在请求中发送的以下 JSON 的示例...
{
"id": 123,
"email": "[email protected]",
"name": "John Smith",
"role": "Admin"
}
以及 VS 中带有断点的调试屏幕截图...
不要忘记用动作过滤器注释动作方法...
[HttpPost(""), AccountRestriction]
public IResult RestrictedArea()
{
...
}
您甚至可以将参数传递给操作过滤器...
[HttpPost(""), AccountRestriction(AllowedRoles = "Admin,Supervisor")]
public IResult RestrictedArea()
{
...
}
然后处理传入的参数...
public class AccountRestrictionAttribute : ActionFilterAttribute
{
public string AllowedRoles { get; set; } = string.Empty;
public override void OnActionExecuting(ActionExecutingContext context)
{
//code ommitted for better clarity...
var account = JsonConvert.DeserializeObject<AccountModel>(body);
var roles = AllowedRoles.Split(',', options: StringSplitOptions.RemoveEmptyEntries);
if (roles.Contains(account.Role.ToString()))
{
var allowedRoles = context.ActionArguments[AllowedRoles];
context.Result = new UnauthorizedResult();
}
}
}
请勿将其用于访问控制或授权目的。推荐的请求授权方式是通过访问令牌。