c#-服务栈-操作,验证和请求过滤器的顺序

我在RequestFilter执行顺序中检测到问题.

ServiceStack中的ValidationFeature是一个仅注册全局请求筛选器的插件.操作顺序指出,全局请求过滤器在优先级< 0的过滤器属性之后和优先级> = 0的过滤器属性之后执行

我的BasicAuth过滤器具有-100优先级,实际上,如果在类级别对Service进行注释,则一切都会顺利进行,但是当注释在方法级别时,它将失败,并在之后执行身份验证过滤器.

我正在使用3.9.70
有什么快速解决办法吗?谢谢

解决方法:

在方法级别添加注释时,您将创建一个动作请求过滤器(因为您将注释添加到一个动作方法中),在其他过滤器运行之后,该请求在Order of Operations中为操作8.

5: Request Filter Attributes with Priority < 0 gets executed
6: Then any Global Request Filters get executed
7: Followed by Request Filter Attributes with Priority >= 0
8: Action Request Filters (New API only)

我建议的最佳解决方法是重新考虑您的服务结构.我想您会遇到这些困难,因为您将在安全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应用程序来演示这一点.

Full source code here.

本质上,我们没有添加标准的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)  };
    }
}

>因此,当我们调用没有MyRole属性的路线/ EinsteinsBirthday时,将正常调用验证,就像使用标准ValidationFeature一样.
>如果我们将路由称为/ HaveChristmas?role = Santa,则我们的验证插件将确定DTO具有我们的属性并且未运行.然后我们的属性过滤器触发,它将触发验证运行.因此顺序是正确的.

上一篇:CodeGo.net>如何使用IRequiresRequest注入ServiceStack中的IRequest?


下一篇:CodeGo.net>依赖注入-如何解决基于值,而不是类型的依赖关系?