ServiceStack中如何使用IRequiresRequest注入IRequest?

How to use IRequiresRequest to inject IRequest in ServiceStack?

我需要访问请求上下文,特别是我自定义 class 中的项目,我不想让它继承自 ServiceStack 服务或在我的服务。

所以如果我有一个像下面这样的 class,实现者 class (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; }
}

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);

我使用的是 4.0.50 版。

另外,此页面 Access HTTP specific features in services 其中提到

Note: ServiceStack's Service base class already implements IRequiresRequestContext which allows you to access the IRequestContext with base.RequestContext and the HTTP Request and Response with base.Request and base.Response.

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

Updated: full code to demo my original intention:

    [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";
        }
    }

IRequiresRequest 仅将当前 IRequest 注入您的 服务 类 和 验证过滤器 ,它不会将 IRequest 注入到您的依赖项中,这些依赖项直接从 IOC 解析并且无法访问当前 IRequest 以能够注入。

此外,ServiceStack 的便利 ServiceAbstractValidator<T> 基础 类 已经实现了 IRequiresRequest 所以在大多数情况下 IRequiresRequest 适用的地方已经实现了所以你不应该需要自己实施它。

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

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

    public object Get(Request request)
    {
       return ContextItems.Get(base.Request, request.Id);
    }
}

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

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

只要每个父级实现 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);
            }
        }
    }
}

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

另外,为了避免将您的逻辑 类 耦合到 ServiceStack,我会考虑仅从 IRequest 传递您的依赖项所需的内容,而不是 IRequest 实例本身,这也将使它成为可能更容易测试。