Updated: Be sure to check my follow up post
Due to infrastructure limitations my current team was in need of a reverse proxy that could point to our ASP.NET Web API endpoint. After failing to get IT to setup a reverse proxy in the load balancer I ended up experimenting with a reverse proxy based on a simple implementation using Web API.
The first requirement is to intercept all requests made to the reverse proxy endpoint. Fortunately the Web API pipeline allows this via the DelegatingHandler:
public class ProxyHandler : DelegatingHandler{} public class WebApiConfig { public static void Configure(HttpConfiguration config) { config.MessageHandlers.Add(new ProxyHandler()); config.Routes.MapHttpRoute("abe", "{*path}"); } }
The configuration above adds the ProxyHandler to the general pipeline thus allowing it to intercept all requests which are processed by the Web API pipeline. Then a single catch-all route is added to make sure all requests are processed by the pipeline.
In the proxy delegating handler all requests must now be forwarded to the desired location:
public class ProxyHandler : DelegatingHandler { private async Task<HttpResponseMessage> RedirectRequest(HttpRequestMessage request, CancellationToken cancellationToken) { var redirectLocation = "http://localhost:61948"; var localPath = request.RequestUri.LocalPath; var client = new HttpClient(); var clonedRequest = await HttpRequestMessageExtensions.CloneHttpRequestMessageAsync(request); clonedRequest.RequestUri = new Uri(redirectLocation + localPath); return await client.SendAsync(clonedRequest, HttpCompletionOption.ResponseHeadersRead, cancellationToken); } protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { return RedirectRequest(request, cancellationToken); } }
I experienced some problems forwarding GET requests which is why the above code clone the entire HttpRequestMessage via the below snippet found on stack overflow:
public static class HttpRequestMessageExtensions { public static async Task<HttpRequestMessage> CloneHttpRequestMessageAsync(HttpRequestMessage req) { var clone = new HttpRequestMessage(req.Method, req.RequestUri); var ms = new MemoryStream(); if (req.Content != null) { await req.Content.CopyToAsync(ms).ConfigureAwait(false); ms.Position = 0; if ((ms.Length > 0 || req.Content.Headers.Any()) && clone.Method != HttpMethod.Get) { clone.Content = new StreamContent(ms); if (req.Content.Headers != null) foreach (var h in req.Content.Headers) clone.Content.Headers.Add(h.Key, h.Value); } } clone.Version = req.Version; foreach (var prop in req.Properties) clone.Properties.Add(prop); foreach (var header in req.Headers) clone.Headers.TryAddWithoutValidation(header.Key, header.Value); return clone; } }
I’m very impressed by the elegance of both Web API but more so the way the HttpRequestMessage/HttpResponseMessage is reused between Web API and HttpClient.
I tested the reverse proxy solutions against our current API and all our GET/POST requests went through. Furthermore all exception message was passed through the proxy as well.
hi, this is very helpful but i like to know how can i resolve connecting through a secured endpoint?
LikeLike