aspnet / AspNetWebStack

ASP.NET MVC 5.x, Web API 2.x, and Web Pages 3.x (not ASP.NET Core)
Other
858 stars 354 forks source link

PUT/DELETE operations to WebAPI produce 404 Not Found #198

Closed davidkeaveny closed 6 years ago

davidkeaveny commented 6 years ago

I am working on an ASP.NET WebForms/MVC application (in dev, IIS on Windows 10), which communicates with an API on another server; to avoid CORS issues, I have added a server-side proxy using MVC controllers, something like this:

[HttpDelete]
public ActionResult Delete(Timesheet timesheet)
{
    var result = ApiClient.Timesheets.Delete(timesheet);
    return Json(result);
}

These endpoints are then called using JQuery AJAX, and everything works nicely.

The problem comes when I decide that I don't want to have to implement a new proxy method every time I add an API method; my approach then is to write a transparent server-side proxy in WebAPI that just forwards the incoming HTTP request to the actual API.

public class TransparentProxyDelegatingHandler : DelegatingHandler
{
    private static readonly Uri ApiUri;
    private static readonly HttpClient Client;

    static TransparentProxyDelegatingHandler()
    {
        var apiServer = new Uri(ConfigurationManager.AppSettings["ApiUrl"]);
        ApiUri = new Uri(apiServer);
        Client = new HttpClient();
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.Headers.Add("X-Forwarded-For", request.GetClientIpAddress());
        request.RequestUri = new Uri(ApiUri, request.RequestUri.PathAndQuery.Replace("/Proxy", string.Empty));

        var response = await Client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);

        return response;
    }
}

When I register the API, I find that calls to e.g. GET /Proxy/Timesheets/1 or POST /Proxy/Timesheets get handled correctly; however, if I make a call to PUT /Proxy/Timesheets/1 or DELETE /Proxy/Timesheets/1, the delegating handler does not get invoked, and jQuery gets a 404 Not Found return from the ASP.NET WebForms/MVC application.

Please note that the existing server-side proxy is still functioning with PUT/DELETE methods, it's just the WebAPI code which will only handle the GET/POST methods, and not the PUT/DELETE methods. There are lots of Stack Overflow questions related to IIS configuration, particularly of the ExtensionlessUrlHandler and the verbs it accepts, but the MVC part of my application is still running and accepting those verbs, which is why I'm wondering if there is something in WebAPI that I should be aware of?

davidkeaveny commented 6 years ago

Turns out that the standard means of registering a delegating handler wasn't working:

public static void RegisterWebApi(HttpConfiguration configuration)
{
    configuration.MessageHandlers.Add(new TransparentProxyDelegatingHandler());
    configuration.Routes.MapHttpRoute("proxy", "proxy/{controller}/{action}/{id}", new { id = RouteParameter.Optional });
}

instead, I should have been using just:

public static void RegisterWebApi(HttpConfiguration configuration)
{
    configuration.Routes.MapHttpRoute(name: "proxy", routeTemplate: "proxy/{*path}",
                handler: HttpClientFactory.CreatePipeline(
                    innerHandler: new HttpClientHandler(),
                    handlers: new DelegatingHandler[]
                    {
                        new TransparentProxyDelegatingHandler(), 
                    }),
                defaults: new { path = RouteParameter.Optional }, 
                constraints: null);
}