Sponsored

Wednesday, February 3, 2021

.NET Web API: Confusing 404 Response to OPTIONS Request for CORS pre-flight

Problem: when a Angular applications sends an OPTIONS request to its own backend to satisfy CORS preflight policy it unexpectedly receives a 404 response from a .NET Core Web API application that effectively blocks an actual data data request from executing correctly. If you have ever experienced this problem, continue reading to understand what the problem is and how to fix it. 

First, let's understand why this situation even occurs.

Modern web application extensively utilize frontend/backend communication and often need to send requests to external (residing on a different domain) endpoints. Such type of communication is known as Cross Origin Resource Sharing (CORS) and is handled by a namesake protocol that is an extension of a standard HTTP protocol. To help facilitate this scenario, modern browsers and web frameworks, like Angular, provide with built-in CORS support to make developers' lives easier without compromising application security.

In a CORS scenario any frontend to backend data request is accompanied with a preliminary OPTIONS request which is also known as a pre-flight request. Remember, that scenario only occurs when a client app and a backend app are deployed on different domains (origins). If it's not the case an explanation below does not apply.

Now, when an OPTION request is sent (silently, developers don't need to write any code for that), the client side app expects a successful response back that includes related CORS information and can decide whether to continue with an actual data request. In order for this communication to work correctly, the backend application has to be configured to support CORS OPTIONS requests as well.

When the backend is implemented using .NET Web API framework CORS support can be added easily and should work correctly and automatically:

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
      {
         options.AddPolicy(CorsPolicy, builder => builder
      	    	.AllowAnyOrigin()
                .AllowAnyMethod()
                .AllowAnyHeader()
         );
      });
      ...
}

Important to note, that the OPTIONS pre-flight request is expected to receive a successful response otherwise the next actual data request will not be sent.

Now that we understand what happens on the wire, here is where the puzzle is: the OPTIONS request is sent by the client app but the backend responds with 404 which indicates incorrect request URL.

But that makes no sense: the URL is exactly the same as for the follow-up data request and it does exist in the code. What is happening?

After fiddling for a while with varies developer's tools it became apparent that the returned code 404 is wrong: instead, 400 (Bad Request) should actually be returned because in this specific scenario the query string of the request URL was too long and exceeded allowed maximum length.

The challenge of diagnosing this problem was that client application was not helpful as it would always send an OPTIONS request first that would fail with 404 and the Web API actual response (400) was never returned. So instead, a non-browser HTTP client was used to send the request without the OPTIONS that returned 400 with the correct explanation.

After increasing the maximum length of the query string in the backend's web.config the problem was solved:

<requestLimits maxQueryString="2048" maxUrl="4096" />

Conclusion

Errors always happen in software development and need to be diagnosed and fixed. When the base framework that developers rely on give incorrect diagnostics it makes it more difficult to analyze and fix the problem. It is important to understand the expected behavior and use the right tools to diagnose the problem.

No comments:

Post a Comment