如何使用 IRequiresRequest 在 ServiceStack 中注入 IRequest?

Jef*_*eff 4 c# httpcontext servicestack

我需要访问请求上下文,特别是我的自定义类中的 Items,我不想让它从ServiceStackService继承或在我的 Service 中设置它。

因此,如果我有一个类似下面的类,实现者类 ( ContextItemsGetter) 也实现了IRequiresRequest,我希望该Request属性被填充。

public interface IGetContextItems
{
  string Get(string key);
}

public class ContextItemsGetter : IGetContextItems, IRequiresRequest
{
  public string Get(string key)
  {
    //someway to access http context items
    //im RequestContext.Instance.Items[key] e.g. Prop1 Prop2
    //or Request.blah but Request is always null
  }
  public IRequest Request { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/Web/IRequiresRequest.cs

但是,当从真正的HTTP 请求redis 消息请求调用 SessionIdGetter 时,请求始终为空。难道我做错了什么?目的是解耦,使用Items在http请求和redis消息请求之间传递信息。

我还尝试使用 RequestContext.Instance.Items,这适用于 HTTP 请求,但在 redis 消息请求期间,项目不存在,我在调用 ExecuteMessage 之前填充的键不存在。

    var req = new BasicRequest { Verb = HttpMethods.Get };

    req.Items.Add("Prop1", m.GetBody().Prop1);
    req.Items.Add("Prop2", m.GetBody().Prop2);

    var result = HostContext.ServiceController.ExecuteMessage(m, req);
Run Code Online (Sandbox Code Playgroud)

我使用的是 4.0.50 版。

此外,此页面访问服务中提及的HTTP 特定功能

注:ServiceStack公司的服务基类已经实现了IRequiresRequestContext它允许您访问IRequestContextbase.RequestContext和HTTP请求和响应与base.Requestbase.Response

我相信IRequiresRequestContext现在被称为IRequiresRequest,所以我认为应该更新文档。

更新: 完整代码来演示我的初衷:

    [Route("/test", Verbs = "GET")]
    public class Dto : IReturnVoid
    { }

    public class DtoService : Service
    {
        //So that IGetContextItems is taken care of by IDependencyThatUsesIGetContextItems
        public IDependencyThatUsesIGetContextItems DependencyThatUsesIGetContextItems { get; set; }

        public void Get(Dto req)
        {
            DependencyThatUsesIGetContextItems.SomeMethod();
        }
    }

    public interface IGetContextItems
    {
        string Get(string key);
    }
    //since ContextItemsGetter implmeents IRequiresRequest
    //I can still easily test any service that uses IGetContextItems by mocking IGetContextItems
    public class ContextItemsGetter : IGetContextItems, IRequiresRequest
    {
        public IRequest Request { get; set; }

        public string Get(string key)
        {
            //either through injection
            //return Request.Items[key].ToString();

            //or some static class
            //return RequestContext.RequestItems.Items[key].ToString();
            return RequestContext.Instance.Items[key].ToString();
        }
    }

    public interface IDependencyThatUsesIGetContextItems
    {
        string SomeMethod();
    }
    public class DependencyThatUsesIGetContextItems : IDependencyThatUsesIGetContextItems
    {
        //this will be inejcted
        public IGetContextItems ContextItemsGetter { get; set; }

        public string SomeMethod()
        {
            var a = ContextItemsGetter.Get("SomeKey");
            return "blah";
        }
    }
Run Code Online (Sandbox Code Playgroud)

myt*_*thz 5

IRequiresRequest仅将当前注入IRequest到您的Service classes 和Validation Filters 中,它不会将 IRequest 注入到您的依赖项中,这些依赖项直接从 IOC 解析并且无法访问当前 IRequest 以进行注入。

ServiceStack 的便利 类ServiceAbstractValidator<T>基类也已经实现了,IRequiresRequest所以在大多数情况下,apply 的地方IRequiresRequest已经实现了,所以你不需要自己实现它。

将 传递IRequest到您的依赖项的推荐方法是将它们作为参数从您的服务中传递,例如:

public class MyServices : Service
{
    public IGetContextItems ContextItems { get; set; }

    public object Get(Request request)
    {
       return ContextItems.Get(base.Request, request.Id);
    }
}
Run Code Online (Sandbox Code Playgroud)

您确实有机会在您的服务实例执行您的服务之前检查和修改您的服务实例,方法是OnPreExecuteServiceFilter()在您的 AppHost 中进行覆盖,以通过并注入IRequest您实现的每个服务依赖项IRequiresRequest

public override object OnPreExecuteServiceFilter(IService service, 
    object request, IRequest req, IResponse res)
{
    service.InjectRequestIntoDependencies(req);
    return request;
}
Run Code Online (Sandbox Code Playgroud)

只要每个父级实现,调用以下扩展方法将递归填充您的服务依赖关系图IRequiresRequest

public static class ServiceExtensions
{
    public static void InjectRequestIntoDependencies(this object instance, IRequest req)
    {
        foreach (var pi in instance.GetType().GetPublicProperties())
        {
            var mi = pi.GetGetMethod();
            if (mi == null)
                continue;

            var dep = mi.Invoke(instance, new object[0]);
            var requiresRequest = dep as IRequiresRequest;
            if (requiresRequest != null)
            {
                requiresRequest.Request = req;
                requiresRequest.InjectRequestIntoDependencies(req);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但是您需要小心不要IRequiresRequest在您的任何 Singleton 依赖项(默认范围)上实现,因为它不是 ThreadSafe 而将 IRequest 作为参数传递。

同样为了避免将您的逻辑类耦合到 ServiceStack,我会考虑只传递您的依赖项需要的内容,IRequest而不是IRequest实例本身,这也将使测试更容易。