简体   繁体   中英

Calling one ServiceStack 4 service from another with a file upload thrown in for fun

I have a working service in 4.0.31 that looks like this:

public object Post(MyDTO request)
{
   foreach (var uploadedFile in base.Request.Files)
   {
      ... do something ...
   }
   return new MyDTOResponse();
}

Everything works great, and I'm happy!

But now, I want to call the same service method from within another service, the way to do this apparently is:

public object Post(MyOtherDTO request)
{
  var myService = base.ResolveService<MyService>();
  // now I call some new method I wrote to bypass the file upload part, since
  // myService.Post() doesn't know about the file upload part
  var myResponse = myService.NewMethodThatLetsMePassAStreamToTheOtherService(streamData);
  ... do other stuff...
  return new MyOtherDTOResponse();
}

While I'm not unhappy with this, it does create a hard dependency between the two services, so I'm not thrilled like I usually am with ServiceStack!

Is there a more elegant way of putting this together? I'm probably just missing something really, really obvious...

I'm not 100% clear on what the issue is, if it's how to share logic between services? then you could pull common logic out of each service class and reference the shared code in both Services.

If no dependencies are required I'll refactor the shared code behind re-usable extension methods .

If dependencies are required I will refactor it behind a shared logic class that's a dependency in both Services, see the IGreeter example in the sharing logic between MVC and ServiceStack answer :

public class MyService1 : Service
{
    public ISharedDep SharedDep { get; set] 

    public object Any(Request1 request)
    {
        //...
    }
}

public class MyService2 : Service
{
    public ISharedDep SharedDep { get; set] 

    public object Any(Request2 request)
    {
        //...
    }
}

Shared logic using Request Context using base class

If it's common code used by many Services that requires the base.Request context than you could move it to a common Service base class:

public class MyServiceBase : Service
{
    public ISharedDep SharedDep { get; set] 

    public object SharedMethod(object request)
    {
        //...
    }
}

public class MyServices1 : MyServiceBase { ... }
public class MyServices2 : MyServiceBase { ... }

Shared logic using Request Context using Extension method

If you prefer not to use a base class, this can be re-factored behind an extension method as well:

public static void MyServiceExtensions
{
    public static object SharedMethod(this IServicBase service, object request)
    {
        var sharedDep = service.TryResolve<ISharedDep>();
        return sharedDep.SharedMethodWithRequestCtx(request, service.Request);
    }
}

Loose Coupling by executing a Request DTO

If the issue is about a loose-coupled way to call Services without a reference to the implementation itself you can execute the Request DTO with the ServiceController:

public class MyService : Service
{
    public object Any(Request requestDto)
    {
        var altDto = new AltRequest { Id = requestDto.Id };
        var response = HostContext.ServiceController.Execute(altDto, base.Request);
        //...
    }
}

Note: this API is available as base.ExecuteRequest(requestDto) in v4.0.32+.

Uploading Files to a HTTP Service In Memory

If the issue is instead how to execute a Service that handles file uploads, there's an example in the embedded version of HttpBenchmarks showing how to call a Service that processes HTTP File uploads with a custom Request Context that uses local FileSystem files instead:

using (var admin = Resolve<AdminServices>())
{
    //...
    var dir = new FileSystemVirtualPathProvider(this, Config.WebHostPhysicalPath);
    var files = dir.GetAllMatchingFiles("*.txt")
        .Concat(dir.GetAllMatchingFiles("*.zip"));

    admin.Request = new BasicRequest
    {
        Files = files.Map(x => new HttpFile {
            ContentLength = x.Length,
            ContentType = MimeTypes.GetMimeType(x.Name),
            FileName = x.Name,
            InputStream = x.OpenRead(),
        } as IHttpFile).ToArray()
    };

    if (admin.Request.Files.Length > 0)
    {
        admin.Post(new UploadTestResults
        {
            TestPlanId = 1,
            TestRunId = testRun.Id,
            CreateNewTestRuns = true,
        });
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM