我需要访问请求上下文,特别是访问自定义类中的Items,并且我不想让它从ServiceStack Service继承或在我的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; }
}


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

但是,当从真正的HTTP请求或redis消息请求中调用SessionIdGetter时,请求始终为null。难道我做错了什么?目的是解耦并使用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提及


  注意:ServiceStack的Service基类已经实现了IRequiresRequestContext,它允许您使用IRequestContext访问base.RequestContext以及使用base.Requestbase.Response的HTTP请求和响应。


我相信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";
        }
    }

最佳答案

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


您确实有机会检查和修改您的Service实例,然后在它执行您的Service之前,通过覆盖AppHost中的OnPreExecuteServiceFilter()来检查并修改每个实现IRequest的Services依赖项中的IRequiresRequest,并使用:

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


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

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


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

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

08-26 19:00