我在RequestFilter
执行顺序中检测到问题。
ServiceStack 中的ValidationFeature
插件是一个只注册一个全局请求过滤器的插件。操作顺序指出全局请求过滤器在优先级 <0 的过滤器属性之后和优先级 >=0 的过滤器属性之前执行
我的BasicAuth
过滤器具有 -100 优先级,事实上,如果Service
在类级别注释一切都会顺利,但是当注释在方法级别时它会失败,然后执行身份验证过滤器。
我正在使用 3.9.70 有什么快速解决方法吗?谢谢
我在RequestFilter
执行顺序中检测到问题。
ServiceStack 中的ValidationFeature
插件是一个只注册一个全局请求过滤器的插件。操作顺序指出全局请求过滤器在优先级 <0 的过滤器属性之后和优先级 >=0 的过滤器属性之前执行
我的BasicAuth
过滤器具有 -100 优先级,事实上,如果Service
在类级别注释一切都会顺利,但是当注释在方法级别时它会失败,然后执行身份验证过滤器。
我正在使用 3.9.70 有什么快速解决方法吗?谢谢
当您在方法级别添加注释时,您正在创建一个Action Request Filter
(因为您正在将注释添加到操作方法) ,在其他过滤器运行之后,它在操作顺序中是操作 8。
5:执行优先级 < 0 的请求过滤器属性
6:然后执行任何全局请求过滤器
7:随后是优先级 >= 0 的请求过滤器属性
8:操作请求过滤器(仅限新 API)
我可以建议的最佳解决方法是重新考虑您的服务结构。我想您会遇到这些困难,因为您在安全 api 方法旁边添加了未经身份验证的 api 方法,因此正在使用方法级别的属性来控制身份验证。所以你大概在做这样的事情你的类和属性会有所不同,这只是示例:
public class MyService : Service
{
// Unauthenticated API method
public object Get(GetPublicData request)
{
return {};
}
// Secure API method
[MyBasicAuth] // <- Checks user has permission to run this method
public object Get(GetSecureData request)
{
return {};
}
}
我会以不同的方式执行此操作,并将您的不安全和安全方法分成 2 个服务。所以我用这个:
// Wrap in an outer class, then you can still register AppHost with `typeof(MyService).Assembly`
public partial class MyService
{
public class MyPublicService : Service
{
public object Get(GetPublicData request)
{
return {};
}
}
[MyBasicAuth] // <- Check is now class level, can run as expected before Validation
public class MySecureService : Service
{
public object Get(GetSecureData request)
{
return {};
}
}
}
您可以通过创建自己的自定义验证功能来解决执行顺序问题,这将允许您推迟验证过程。我创建了一个功能齐全的自托管 ServiceStack v3 应用程序来演示这一点。
本质上,我们没有添加标准ValidationFeature
插件,而是实现了一个稍微修改的版本:
public class MyValidationFeature : IPlugin
{
static readonly ILog Log = LogManager.GetLogger(typeof(MyValidationFeature));
public void Register(IAppHost appHost)
{
// Registers to use your custom validation filter instead of the standard one.
if(!appHost.RequestFilters.Contains(MyValidationFilters.RequestFilter))
appHost.RequestFilters.Add(MyValidationFilters.RequestFilter);
}
}
public static class MyValidationFilters
{
public static void RequestFilter(IHttpRequest req, IHttpResponse res, object requestDto)
{
// Determine if the Request DTO type has a MyRoleAttribute.
// If it does not, run the validation normally. Otherwise defer doing that, it will happen after MyRoleAttribute.
if(!requestDto.GetType().HasAttribute<MyRoleAttribute>()){
Console.WriteLine("Running Validation");
ValidationFilters.RequestFilter(req, res, requestDto);
return;
}
Console.WriteLine("Deferring Validation until Roles are checked");
}
}
配置使用我们的插件:
// Configure to use our custom Validation Feature (MyValidationFeature)
Plugins.Add(new MyValidationFeature());
然后我们需要创建我们的自定义属性。您的属性当然会有所不同。ValidationFilters.RequestFilter(req, res, requestDto);
如果您对用户具有所需角色并满足您的条件感到满意,您需要做的关键是打电话。
public class MyRoleAttribute : RequestFilterAttribute
{
readonly string[] _roles;
public MyRoleAttribute(params string[] roles)
{
_roles = roles;
}
#region implemented abstract members of RequestFilterAttribute
public override void Execute(IHttpRequest req, IHttpResponse res, object requestDto)
{
Console.WriteLine("Checking for required role");
// Replace with your actual role checking code
var role = req.GetParam("role");
if(role == null || !_roles.Contains(role))
throw HttpError.Unauthorized("You don't have the correct role");
Console.WriteLine("Has required role");
// Perform the deferred validation
Console.WriteLine("Running Validation");
ValidationFilters.RequestFilter(req, res, requestDto);
}
#endregion
}
为此,我们需要在 DTO 路由而不是操作方法上应用我们的自定义属性。因此,这与您现在的操作方式略有不同,但仍应灵活。
[Route("/HaveChristmas", "GET")]
[MyRole("Santa","Rudolph","MrsClaus")] // Notice our custom MyRole attribute.
public class HaveChristmasRequest {}
[Route("/EasterEgg", "GET")]
[MyRole("Easterbunny")]
public class GetEasterEggRequest {}
[Route("/EinsteinsBirthday", "GET")]
public class EinsteinsBirthdayRequest {}
然后您的服务将如下所示:
public class TestController : Service
{
// Roles: Santa, Rudolph, MrsClaus
public object Get(HaveChristmasRequest request)
{
return new { Presents = "Toy Car, Teddy Bear, Xbox" };
}
// Roles: Easterbunny
public object Get(GetEasterEggRequest request)
{
return new { EasterEgg = "Chocolate" };
}
// No roles required
public object Get(EinsteinsBirthdayRequest request)
{
return new { Birthdate = new DateTime(1879, 3, 14) };
}
}
/EinsteinsBirthday
因此,当我们调用没有属性的路由时,将正常MyRole
调用验证,就像使用标准一样ValidationFeature
。
如果我们调用路由/HaveChristmas?role=Santa
,那么我们的验证插件将确定 DTO 具有我们的属性并且不运行。然后我们的属性过滤器触发,它将触发验证运行。因此顺序是正确的。