ThreeMammals / Ocelot

.NET API Gateway
https://www.nuget.org/packages/Ocelot
MIT License
8.38k stars 1.64k forks source link

Question on Custom Request Aggregation #937

Closed crack-haddock closed 5 years ago

crack-haddock commented 5 years ago

Expected Behavior / New Feature

Referring to my config (see below). I have two aggregate routes - an "automatic" one, and one using a custom aggregator. The automatic/internal one calls both internal APIs and combines the two result sets into one as expected.

I wish to return a customised/filtered set of results given the individual results of each internal call, and expected that using a custom aggregator would allow me to do this. To use a lazy example, given the "tom and laura" example from the docs, I might wish to only return their ages i.e. anonymised data.

Actual Behavior / Motivation for New Feature

I have set up a custom aggregator class implementing IDefinedAggregator as described in the docs (see below). While I have implemented said interface, and it is called as expected, all it seems to receive is headers and routing info for each of the individual internal APIs called. At this stage I have no interest in adding/removing/transforming any of that. I guess retrieving the actual data is done in a further step(?), and it's this I'd like access to.

As an aside, most of my attempts to return from the Aggregator method silently fail and hang the client (the one time it doesn't if when one of the APIs requires authorisation and I try to access a header state that is not returned for this- this gives a 500 response). There is another issue #883 that reports this, but his solution is confusing as it no longer satisfies the interface definition.

Steps to Reproduce the Problem

  1. config { "ReRoutes": [ { "DownstreamPathTemplate": "/graph/{owt}/relations", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 50789 } ], "UpstreamPathTemplate": "/graphymcgraphface/{owt}/relations", "UpstreamHttpMethod": [ "GET" ], "Key": "getgraphrelations" }, { "DownstreamPathTemplate": "/graph/{owt}", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 50789 } ], "UpstreamPathTemplate": "/graphymcgraphface/{owt}", "UpstreamHttpMethod": [ "GET" ], "Key": "getgraph" , "AuthenticationOptions": { "AuthenticationProviderKey": "TestKey", "AllowedScopes": [] } } ], "Aggregates": [ { "ReRouteKeys": [ "getgraph", "getgraphrelations" ], "UpstreamPathTemplate": "/graphymcgraphfaceaggro/{owt}" }, { "ReRouteKeys": [ "getgraph", "getgraphrelations" ], "Aggregator": "MeFirstAggregator", "UpstreamPathTemplate": "/graphymcgraphfaceaggrocustom/{owt}" } ] }

  2. services.AddOcelot(Configuration) .AddSingletonDefinedAggregator();

  3. public class MeFirstAggregator : IDefinedAggregator { public Task Aggregate(List responses) { ??? } }

Specifications

jasongm86 commented 5 years ago

I am using something like this:

public async Task<DownstreamResponse> Aggregate(List<DownstreamContext> responses)
{
    var xResponseContent = await responses.FirstOrDefault(r => r.DownstreamReRoute.Key.Equals("<X_REROUTE_KEY>")).DownstreamResponse.Content.ReadAsStringAsync();

    var yResponseContent = await responses.FirstOrDefault(r => r.DownstreamReRoute.Key.Equals("<Y_REROUTE_KEY>")).DownstreamResponse.Content.ReadAsStringAsync();

    var contentBuilder = new StringBuilder();
    contentBuilder.Append(xResponseContent);
    contentBuilder.Append(yResponseContent);

    var stringContent = new StringContent(contentBuilder.ToString())
    {
        Headers = { ContentType = new MediaTypeHeaderValue("application/json") }
    };

    return new DownstreamResponse(stringContent, HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>(), "OK");
}

Make sure you check your status codes before accessing any the downstream content though!

crack-haddock commented 5 years ago

Thank you very much, that's exactly what I was looking for!