如何从ServiceStack中的请求/响应过滤器调用自己的服务?

时间:2013-07-30 22:43:18

标签: servicestack

我的问题是......

......我有像这样的DTO

[Route("/route/to/dto/{Id}", "GET")]
public class Foo : IReturn<Bar>
{
  public string Id { get; set; }
}

并且需要使用此签名调用实现该方法的服务

public Bar Get(Foo)

来自请求和/或响应过滤器。我不知道哪个类实现它(不想知道)。我需要的是类似下面示例中的LocalServiceClient类:

var client = new LocalServiceClient();
Bar bar = client.Get(new Foo());

这个LocalServiceClient事物是否存在? JsonServiceClient有一个非常相似的接口,但使用它将是有用的(我需要调用我自己的服务,我不需要额外的往返,甚至到本地主机,只是为了这样做)。

我知道Service类中的ResolveService方法,但它要求我有一个服务实例并知道哪个类将处理请求。

我认为这个LocalServiceClient是可能的,因为我拥有远程客户端(例如JsonServiceClient)需要调用服务的所有数据 - 请求DTO,路由,动词 - 但无法找到如何执行此操作。实际上,它应该比JsonServiceClient更容易实现。

JsonServiceClient会这样做,但必须有更好的方法,使用相同的请求上下文。

我想做什么(如果你不好奇我为什么要这样做,请跳过这个)

实际上,我的DTO是这样的:

[EmbedRequestedLinks]
[Route("/route/to/dto/{Id}", "GET")]
public class MyResponseDto
{
  public string Id { get; set; }
  public EmbeddableLink<AResponseDto> RelatedResource { get; set; }
  public EmbeddableLink<AnotherResponteDto> AnotherRelatedResource { get; set; }
}

EmbedRequestedLinksAttribute是一个请求/响应过滤器。此过滤器检查请求中是否存在名为“embed”的查询参数。如果是,则过滤器需要将参数引用的逗号分隔的相关资源“嵌入”到对此请求的响应中。 EmbeddableLink&LT; T&GT;可以使用以下扩展方法获取实例:

1) public static EmbeddableLink<T> ToEmbeddableLink<T>(this IReturn<T> requestDto)
2) public static EmbeddableLink<T> ToEmbeddableLink<T>(this T resource)

假设客户发出此请求:

GET /route/to/dto/123456?embed=relatedResource HTTP/1.1

将处理此请求的服务将使用使用signature(1)创建的EmbeddableLinks返回MyResponseDto的实例。然后我的响应过滤器将看到嵌入查询参数,并将调用相应服务的Get方法,将RelatedResource替换为另一个EmbeddableLink实例,这次使用扩展方法(2)创建:

var client = new LocalServiceClient();
response.RelatedResource = client.Get(response.RelatedResource.RequestDto)
                                 .ToEmbeddableLink();

EmbeddableLink的序列化程序负责其余部分。

如果embeddable链接未包含在嵌入列表中,则序列化例程将调用扩展方法ToUrl(由ServiceStack提供),该方法接受动词并将请求DTO转换为URL。在此示例中,客户端将获得此响应:

{
  "id": "9asc09dcd80a98",
  "relatedResource": { "id": "ioijo0909801", ... },
  "anotherRelatedResource":
  {
    "$link": { "href": "/route/to/another/dto/1sdf89879s" }
  }
}

我知道ServiceStack的创建者认为多态请求/响应是坏事,但这种情况对我来说似乎没问题,因为我不是在创建服务,而是扩展框架以帮助我按照我的方式创建服务(和可能是ServiceStack的其他用户)。我还在为ServiceStack创建其他超媒体扩展。 (我希望我的老板允许我在github上发布这些扩展)

2 个答案:

答案 0 :(得分:4)

如果您真的想这样做,那么请查看ServiceStack的源代码。查看ServiceManager和ServiceController。这些类负责注册和解析服务。您甚至可以使用反射来动态创建服务,使用静态EndpointHost.Metadata,如下所示:

var operation = EndpointHost.Metadata.Operations
                       .FirstOrDefault(x => x.RequestType == typeof(Person));
if (operation != null)
{
    var svc = Activator.CreateInstance(operation.ServiceType);
    var method = operation.ServiceType.GetMethod("Get");
    var response = method.Invoke(svc, new[] { new Person() });
}

这种方法有效但如果还有其他代码调用

,您将获得NULL异常
var httpRequest = RequestContext.Get<IHttpRequest>();

但我不建议这样做。

相反,如果您创建自己的业务服务类来执行所有CRUD操作(POST / PUT / GET等)。然后使ServiceStack Services对它们进行精简包装。现在,您可以随时调用自己的服务,而无需担心HTTP请求和ServiceStack。仅在处理HTTP请求时使用ServiceStack服务

答案 1 :(得分:3)

您可以调用静态AppHostBase.Resolve()方法,如演示here,从MVC控制器调用SeviceStack服务:

var helloService = AppHostBase.Resolve<HelloService>();
helloService.RequestContext = System.Web.HttpContext.Current.ToRequestContext();
var response = (HelloResponse)helloService.Any(new HelloRequest { Name = User.Identity.Name });

但是,我会采用@ kampsj的方法,使您的ServiceStack服务成为应用程序服务类的一个薄包装器,并且只处理ServiceStack服务中的HTTP / Session特定内容。