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; }
}
然而,当从真正的 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 的便利 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()
以通过并在您的每个服务依赖项中注入 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
实例本身,这也将使它成为可能更容易测试。
我需要访问请求上下文,特别是我自定义 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; }
}
然而,当从真正的 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 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
注入您的 服务 类 和 验证过滤器 ,它不会将 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()
以通过并在您的每个服务依赖项中注入 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
实例本身,这也将使它成为可能更容易测试。