我需要访问请求上下文,特别是访问自定义类中的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; }
}
但是,当从真正的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 implementsIRequiresRequestContext
which allows you to access theIRequestContext
withbase.RequestContext
and the HTTP Request and Response withbase.Request
andbase.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实例本身,这也将使其更易于测试.