我需要访问请求上下文,特别是访问自定义类中的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.Request
和base.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方便的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);
}
}
您确实有机会检查和修改您的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
实例本身,这也将使其更易于测试。