asp.netjson.netrestwebapi

ASP.NET "pass through" WEB API


I am currently working with an architecture which consists of two connected APIs: an External API exposed to clients and an Internal API which connects to internal services and is only exposed to the External API.

The External API should be passing through the response data from the Internal API to the client, without processing, but I couldn't figure out to do this. I'm currently deserializing and serializing the objects again.

This is only an issue because for big JSON payloads (bigger than 15MB) the External API starts adding some considerable processing time, which is from the JSON deserialize method alone.

Is there a way to return a JSON result based on the request, without processing it?

Current code simplified for brevity:

External API Controller

[HttpGet]
public async Task<ActionResult> GetSomeData(...)
{
    var serviceResponse = await _serviceInvoker.GetAsync<SomeType>(...)
        .ConfigureAwait(false);
   
    Response.StatusCode = (int)serviceResponse.StatusCode;
   
    return new JsonResult(serviceResponse.Content);
}

Internal service invoker response parser

using System.Text.Json;

private async Task<ServiceInvokerResult> ParseServiceResponse<T>(HttpResponseMessage response)
{
    object deserializedContent = null;
    var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

    switch (response.StatusCode)
    {
        case HttpStatusCode.OK:
                deserializedContent = JsonSerializer.Deserialize<T>(content);
            break;
        (...)
    }

    return new ServiceInvokerResult
    {
        (...)
        StatusCode = response.StatusCode,
        Content = deserializedContent ?? content
    };
}

///

Solution update based on responses:

The ServiceInvoker now always returns a HttpResponseMessage without processing it:

External API Controller

[HttpGet]
public async Task<ActionResult> GetSomeData(...)
{
    var serviceResponse = await _serviceInvoker.GetAsync(...)
        .ConfigureAwait(false);
   
    Response.StatusCode = (int)serviceResponse.StatusCode;
   
    return new FileStreamResult(await response.GetContentAsStreamAsync(), response.GetContentType());
}

Extension methods

public static async Task<Stream> GetContentAsStreamAsync(this HttpResponseMessage response)
{
   return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
}

public static string GetContentType(this HttpResponseMessage response)
{
   return response.Content?.Headers?.ContentType?.MediaType ?? Constants.Http.JsonContentType;
}

Solution

  • Haven't tried that personally, but you could try returning a stream of data from your Internal API back to your client.

    Make your Internal API return a stream:

    private async Task<ServiceInvokerResult> ParseServiceResponse<T>(HttpResponseMessage response)
    {
        var contentStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
        // ...
    
        return new ServiceInvokerResult
        {
            (...)
            StatusCode = response.StatusCode,
            ContentStream = contentStream,
            ContentType = "application/json"
        };
    }
    

    Then, in your External API:

    [HttpGet]
    public async Task<ActionResult> GetSomeData(...)
    {
        var response = await _serviceInvoker.GetContentStreamAsync(...)
            .ConfigureAwait(false);
       
        // ...
       
        return File(response.ContentStream, response.ContentType);
    }