Access Servicstack.net session in validator

2019-02-19 07:07发布

How can I access a ServiceStack.net session in my validation code?

public class UserSettingsValidator : AbstractValidator<UserSettingsRequest>
{
    public UserSettingsValidator()
    {
        RuleFor(x => x.UserId)
            .SetValidator(new PositiveIntegerValidator())
            .SetValidator(new UserAccessValidator(session.UserId)); //<-- I need to pass the UserID from the session here
    }
}

In the Service Implementation I just do:

var session = base.SessionAs<UserSession>();

but this does not work for my abstract validator.

Thanks!

Edit: this is version 3.9.71.0

2条回答
对你真心纯属浪费
2楼-- · 2019-02-19 07:32

I assume you are just using the ValidationFeature plugin, as most do. If that's the case, then I don't think it is possible. Ultimately the ValidationFeature is a plugin which uses a RequestFilter.

I wanted to do something similar before too, then realised it wasn't possible.

The RequestFilter is run before the ServiceRunner. See the order of operations guide here.

What this means to you is your populated request DTO reaches your service, and the validation feature's request filter will try validate your request, before it has even created the ServiceRunner.

The ServiceRunner is where an instance of your service class becomes active. It is your service class instance that will be injected with your UserSession object.

So effectively you can't do any validation that relies on the session at this point.

Overcomplicated ?:

It is possible to do validation in your service method, and you could create a custom object that would allow you pass the session along with the object you want to validate. (See next section). But I would ask yourself, are you overcomplicating your validation?

For a simple check of the request UserId matching the session's UserId, presumably you are doing this so the user can only make changes to their own records; Why not check in the service's action method and throw an Exception? I am guessing people shouldn't be changing this Id, so it's not so much a validation issue, but more a security exception. But like I say, maybe your scenario is different.

public class SomeService : Service
{
    public object Post(UserSettingsRequest request) // Match to your own request
    {
        if(request.UserId != Session.UserId)
            throw new Exception("Invalid UserId");
    }
}

Validation in the Service Action:

You should read up on using Fluent Validators. You can call the custom validator yourself in your service method.

// This class allows you to add pass in your session and your object
public class WithSession<T>
{
    public UserSession Session { get; set; }
    public T Object { get; set; }
}

public interface IUserAccessValidator
{
    bool ValidUser(UserSession session);
}

public class UserAccessValidator : IUserAccessValidator
{
    public bool ValidUser(UserSession session)
    {
        // Your validation logic here
        // session.UserId
        return true;
    }
}

public class UserSettingsValidator : AbstractValidator<WithSession<UserSettingsRequest>>
{
    public IUserAccessValidator UserAccessValidator { get; set; }

    public UserSettingsValidator()
    {
        // Notice check now uses .Object to access the object within
        RuleFor(x => x.Object.UserId)
            .SetValidator(new PositiveIntegerValidator());

        // Custom User Access Validator check, passing the session
        RuleFor(x => x.Session).Must(x => UserAccessValidator.ValidUser(x)); 
    }
}

Then to actually use the validator in your service:

public class SomeService : Service
{
    // Validator with be injected, you need to registered it in the IoC container.
    public IValidator<WithSession<UserSettingsRequest>> { get; set; }

    public object Post(UserSettingsRequest request) // Match to your own request
    {
        // Combine the request with the current session instance
        var requestWithSession = new WithSession<UserSettingsRequest> {
            Session = this.Session,
            Object = request
        };

        // Validate the request
        ValidationResult result = this.Validator.Validate(requestWithSession);
        if(!result.IsValid)
        {
            throw result.ToException();
        }

        // Request is valid
        // ... more logic here
        return result;
    }
}

I hope this helps. Note: code is untested

查看更多
Bombasti
3楼-- · 2019-02-19 07:34

It appears that after reading from a bunch of people experiencing similar problems, then many hours of playing with several solutions based on the SS4 Cookbook etc, this is a problem that is already solved:

https://forums.servicestack.net/t/blaz-miheljak-355-feb-3-2015/176/2

Implement the IRequiresRequest interface on your validator, and voila.

查看更多
登录 后发表回答