19

I have a few web services running on different servers, and I want to have one web service running "in front" of the rest to decide which web service (server) the request should be forwarded to based on header values.

The idea is that a client will send a request, say:

http://api.mysite.com/cars

The API at mysite.com will inspect the request, extract information from the API key (which is supplied in the headers) and redirect to the appropriate server, e.g.

http://server4.mysite.com/api/cars

Is this going to work? I'm concerned about how I will return the response (w/data) from "server4" to the client. Will the response only be returned back to the first server or will the client achieve that response?

msk
  • 1,052
  • 3
  • 15
  • 31
  • Both scenarios are viable. Which one you want and what doesn't work with that? – Adriano Repetti Jun 24 '15 at 11:46
  • I want clients to only have to talk to http://api.mysite.com independently of which server they're really contacting. I was just concerned whether this would be possible. I thought maybe the client would automatically(?) receive a response from the first server and thereby miss the "second" response. Maybe it's just a silly thought... I just wanted to know if I will be able to return the response from the 2nd server directly to the client. – msk Jun 24 '15 at 12:03
  • 1
    You can redirect them (they'll communicate directly with that server). Assuming you site is sessionless then it's good because of load balancing (and because it doesn't impact on your server resources). Instead if you want to "filter" their requests (for example because of authentication is made on your server) then you have to first download response from 2nd server and then send it back to client. Benefit: they see only _you_, you can have sessions (without effort) but drawbacks: you will need much more resources in your server (and load balancing will be harder). – Adriano Repetti Jun 25 '15 at 12:47
  • I exactly have the same setup, client calls the external api endpoint and then based on the input i redirect them to the internal api endpoint (on different internal servers) and everything works and as far as response is concerned if you want to massage your response before sending you can do that otherwise you can just send the response as is from the internal API call. So conclusion , what you are talking about is a valid scenario and it should work. – Prashant Jul 01 '15 at 16:42

2 Answers2

13

Just run into the same task and have to add some more lines in addition to Yazan Ati answer.

    [HttpPost]
    [HttpGet]
    [Route("api/TestBot/{*remaining}")]
    public Task<HttpResponseMessage> SendMessage()
    {
        const string host = "facebook.botframework.com";
        string forwardUri = $"https://{host}/api/v1/bots/billycom{Request.RequestUri.Query}";

        Request.Headers.Remove("Host");
        Request.RequestUri = new Uri(forwardUri);
        if (Request.Method == HttpMethod.Get)
        {
            Request.Content = null;
        }

        var client = new HttpClient();
        return client.SendAsync(Request, HttpCompletionOption.ResponseHeadersRead);
    }
Veikedo
  • 1,453
  • 1
  • 18
  • 25
10

All you need to do is build a Web API DelegatingHandler like this:

public class ProxyHandler : DelegatingHandler
{
  protected override async System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
  {
    UriBuilder forwardUri = new UriBuilder(request.RequestUri);
    //strip off the proxy port and replace with an Http port
    forwardUri.Port = 80;
    //send it on to the requested URL
    request.RequestUri = forwardUri.Uri;
    HttpClient client = new HttpClient();
    var response = await  client.SendAsync(request,HttpCompletionOption.ResponseHeadersRead);
    return response;
  }
} 
Yazan Ati
  • 126
  • 1
  • 3
  • For more detail on this answer see https://blog.kloud.com.au/2013/11/24/do-it-yourself-web-api-proxy/ –  Oct 06 '16 at 15:27
  • 1
    This solution does not works with GET and DELETE, because the implementation of SendAsync itself try to read the request stream which leads to error "Cannot send a content-body with this verb-type." (see this http://stackoverflow.com/questions/3981564/cannot-send-a-content-body-with-this-verb-type ).To make it works, instead of calling client.SendAsync, I have to call client.GetAsync or client.PostAsync or client.PutAsync or client.DeleteAsync depend on the request's Http Method. As a consequence, I have to manually copy all the Header from the request object to the HttpClient object. – Hung Nguyen Apr 07 '17 at 17:13
  • @HungNguyen Shouldn't you just override the other methods as well? SendAsync is just for sending data. – Thanasis Ioannidis Oct 13 '17 at 10:08
  • @HungNguyen try adding the following to get it to work with GETs if (request.Method == HttpMethod.Get) { request.Content = null; } – alex Jan 27 '18 at 19:57
  • it is not working over Https, please help if anyone has some idea – shanky Feb 27 '20 at 16:06