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

我需要访问请求上下文,特别是访问自定义类中的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提及

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注入到您的Service类和验证过滤器中,而不会将IRequest注入到您的依赖项中,这些依赖项是直接从IOC解析的,并且谁无权访问当前IRequest即可注入.

此外,ServiceStack的便捷Service和AbstractValidator< 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(),您确实可以检查和修改Service实例在执行Service之前对其进行检查和修改,以将IRequest注入并在实现IRequiresRequest的每个Services依赖项中注入:

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实例本身,这也将使其更易于测试.

上一篇:android-.net框架之外的Servicestack客户端,实现?


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