How to set up web API routing for proxy controller?

Part of my application needs to act as a proxy for a third party RESTful web service. Is there a way to set up web API routing so that all requests of the same type will go to the same method?

For example, if a client sends in any of these GET requests, I want them to go into one GET action method, which then sends the request to the downstream server.

api/Proxy/Customers/10045
api/Proxy/Customers/10045/orders
api/Proxy/Customers?lastname=smith

      

A single action method for GET will pick up any of these three requests and send them to the appropriate service (I know how to work with the HttpClient to make this happen efficiently):

http://otherwebservice.com/Customers/10045
http://otherwebservice.com/Customers/10045/orders
http://otherwebservice.com/Customers?lastname=smith

      

I don't want to tightly couple my web service with a third party web service and replicate their entire API as method calls inside mine.

One workaround I was thinking about is to simply encode the target url in JavaScript on the client and pass it to the web API, which will only see one parameter. It will work, but I would rather use the routing capabilities in the web API if possible.

+3


source to share


1 answer


This is how I got it to work. First, create a controller with a method for each verb you want to support:

public class ProxyController : ApiController
{
    private Uri _baseUri = new Uri("http://otherwebservice.com");

    public async Task<HttpResponseMessage> Get(string url)
    {
    }

    public async Task<HttpResponseMessage> Post(string url)
    {
    }

    public async Task<HttpResponseMessage> Put(string url)
    {
    }

    public async Task<HttpResponseMessage> Delete(string url)
    {
    }
}

      

The methods are asynchronous because they are going to use the HttpClient. Match your route like this:

config.Routes.MapHttpRoute(
  name: "Proxy",
  routeTemplate: "api/Proxy/{*url}",
  defaults: new { controller = "Proxy" });

      



Now let's go back to the Get method in the controller. Create an HttpClient object, create a new HttpRequestMessage object with the appropriate Url, copy all (or almost all) from the original request message and call SendAsync ():

public async Task<HttpResponseMessage> Get(string url)
{
    using (var httpClient = new HttpClient())         
    {
        string absoluteUrl = _baseUri.ToString() + "/" + url + Request.RequestUri.Query;
        var proxyRequest = new HttpRequestMessage(Request.Method, absoluteUrl);
        foreach (var header in Request.Headers)
        {
            proxyRequest.Headers.Add(header.Key, header.Value);
        }

        return await httpClient.SendAsync(proxyRequest, HttpCompletionOption.ResponseContentRead);
    }
}

      

Combining URLs can be more difficult , but that's the basic idea. For Post and Put methods, you also need to copy the request body

Also note the parameter HttpCompletionOption.ResponseContentRead

passed in the call SendAsync

, because without it ASP.NET will spend time reading the content if the content is large (in my case it changed the request to 500KB 100ms into a 60s request).

+12


source







All Articles