Skip to content

Instantly share code, notes, and snippets.

@moosetraveller
Last active August 12, 2021 23:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save moosetraveller/8b67080a57d3fe068582b33e55c8e6c6 to your computer and use it in GitHub Desktop.
Save moosetraveller/8b67080a57d3fe068582b33e55c8e6c6 to your computer and use it in GitHub Desktop.
Simple ASP.NET Core HTTP Proxy

Simple ASP.NET Core HTTP Proxy

Why needed?

There is no Esri Proxy (https://github.com/Esri/resource-proxy) for ASP.NET Core. However, it's very easy to implement a simple ASP.NET HTTP proxy. The solution below is not a full replacement of proxy.ashx but elaborates an idea how to handle CORS issues and login credential in a webmap application.

Note: If you have authorization and access to configure the webserver and/or configure ArcGIS Online services, there may be other alternatives (https://github.com/Esri/resource-proxy#alternatives). In addition, it may be worth to consider a dedicated proxy server (depending on the traffic).

Requirements

The NuGet Package AspNetCore.Proxy (https://github.com/twitchax/AspNetCore.Proxy) must be installed.

appsettings.json

Extend appsettings.json with a key "Proxy":

{
  "Proxy": {
    "AcceptedUrls": [
      "https://www.geomo.ch",
      "https://www.moosetraveller.ch"
    ],
    "MailService": {
      "Url": "https://www.geomo.ch/api/tracking",
      "ApiKey": "12345"
    }
  }
}

Configuration Class ProxyConfig

namespace Geomo {
  public class ProxyConfig {
    public string[] AcceptedUrls { get; set; }
    public ParcelServiceConfig ParcelService { get; set; }
  }
  public class MailServiceConfig {
    public string ApiKey { get; set; } 
    public string Url { get; set; }
  }
}

ConfigureServices(IServiceCollection) in Startup.cs

// ...
public void ConfigureServices(IServiceCollection services)
{
  // ...
  var proxyConfig = new ProxyConfig();
  Configuration.GetSection("Proxy").Bind(proxyConfig);
  services.AddSingleton(proxyConfig);
  // ...
  services.AddProxies();
  // ...
}
// ...

ProxyController.cs

public class ProxyController : ControllerBase
{
  private readonly ProxyConfig _configuration;
  private readonly ILogger<ProxyController> _logger;
  private readonly HttpProxyOptions _httpOptions;

  public ProxyController(ProxyConfiguration configuration, ILogger<ProxyController> logger)
  {
      _configuration = configuration;
      _logger = logger;

      _httpOptions = HttpProxyOptionsBuilder.Instance
          .WithHandleFailure((httpContext, exception) =>
          {
              _logger.LogError(exception, $"Proxy Error: {nameof(exception)}");
              return Task.CompletedTask;
          })
          .Build();
  }
  
  // proxy?https://www.moosetraveller.ch
  // => https://www.moosetraveller.ch
  [Route("proxy")]
  public Task HandleProxyRequest()
  {
    var query = HttpUtility.ParseQueryString(Request.QueryString.Value).ToString();
    if (_configuration.AcceptedUrls.Any(url => query.StartsWith(url))) 
    {
      return this.HttpProxyAsync(query, _httpOptions);
    }
    HttpContext.Response.StatusCode = 500;
    return HttpContext.Response.WriteAsync($"URL {query} is not accepted.");
  }
  
  // example with route parameter: 
  // api/external/parcels/57896 
  // => https://www.geomo.ch/api/tracking?tracking=57896&type=letter&apiKey=12345
  [Route("api/external/parcels/{trackingNumber}")]
  public Task GetParcel(string trackingNumber) {
    var query = HttpUtility.ParseQueryString("?");
    query["tracking"] = trackingNumber;
    query["type"] = "parcel";
    query["apiKey"] = _configuration.ParcelService.ApiKey;
    return this.HttpProxyAsync($"{_configuration.ParcelService.Url}?{query}", _httpOptions);
  }
  
  // example with query string: 
  // api/external/letters?trackingNumber=56874 
  // => https://www.geomo.ch/api/tracking?tracking=57896&type=letter&apiKey=12345
  [Route("api/external/letters")]
  public Task GetLetter() {
    var query = HttpUtility.ParseQueryString(Request.QueryString.Value);
    query["type"] = "letter";
    query["apiKey"] = _configuration.ParcelService.ApiKey;
    return this.HttpProxyAsync($"{_configuration.ParcelService.Url}?{query}", _httpOptions);
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment