Hanezu Don't worry. Think, and do.

Deploy Django REST Framework project using API Gateway

  1. Set up API Gateway
  2. Sending GET request
    1. Front-end: 403 + CORS error
    2. Back-end: one-time 301 response
  3. Sending POST request: 500 error
  4. Solution: add a trailing slash to the endpoint URL
    1. Why the CORS error was thrown?

Set up API Gateway

It is easy to wrap an existing API with API Gateway to hide the host and support HTTPS. I followed the official tutorial and set up my API.

To be specific, assume my DRF (Django REST Framework) project is served at my.server.host:12345. I created an ANY method with Integration type of HTTP Proxy and Endpoint URL of http://my.server.host:12345/{proxy}. Then I save the setup and deploy the API (it is very important to deploy API everytime you do any edition!).

Sending GET request

Front-end: 403 + CORS error

Having API Gateway set up, I attempted to send a GET request to the API from my front-end website, but I received the following 403 response.

Failed to load resource: the server responded with a status of 403 ()

which is followed by a CORS error.

Access to XMLHttpRequest at 'https://[API_ID].execute-api.ap-northeast-1.amazonaws.com/[API_ROUTE]/' (redirected from 'https://[API_ID].execute-api.ap-northeast-1.amazonaws.com/[API_STAGE]/[API_ROUTE]') from origin 'https://my.front-end.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

However, I already set up CORS in my DRF project using django-cors-headers, and direct access to the API did not cause any CORS error.

Back-end: one-time 301 response

It is more confusing that I did not receive any request in my backend, except a single 301 response:

"GET /[API_ROUTE] HTTP/1.1" 301 0

it seems that the request was redirected once, but the redirected request did not ever reach my server. Therefore the response to the redirected request were instead made by API Gateway.

Sending POST request: 500 error

When I send a POST request this time, I got a 500 error. This time, the request reached my server, and I saw the following error message:

RuntimeError: You called this URL via POST, but the URL doesn't end in a slash and you have APPEND_SLASH set. Django can't redirect to the slash URL while maintaining POST data. Change your form to point to my.server.host:12345/[API_ROUTE]/ (note the trailing slash), or set APPEND_SLASH=False in your Django settings.

Solution: add a trailing slash to the endpoint URL

In short, it turned out that API Gateway will ignore the trailing slash of all incoming request (see this thread), so changing the Endpoint URL to http://my.server.host:12345/{proxy}/ solve the problem.

Why the CORS error was thrown?

The reason why the CORS error was thrown lies in the fact that a successful cross-site request is composed of a preflight request and an actual request.

a successful cross-site request This graph from MDN web docs illustrates what is happening under the hood for a successful cross-site request.

In our case, since the route missed the trailing slash, the response to our preflight request was a 301 made by API Gateway. However, API Gateway did not add [API_STAGE] to the redirect response, and pass the 301 response back to the front-end as-is. The front-end then send another preflight request, this time with the trailing slash, but missing the API_STAGE.

As a result, API Gateway response a 403 directly, which, of course, contained no Access-Control-Allow-Origin header. As a result, the actual request was blocked by the browser.

our case The preflight request cannot reach the server because API Gateway does not proxy for routes that do not exist.

Notice that if we shut down our DRF server, the preflight request will receive a 504 Network error communicating with endpoint error, which is of course also generated by API Gateway.

comments powered by Disqus