Implementing Cross-Origin Resource Sharing (CORS) in a Python Web Application
Web applications often need to communicate with APIs hosted on different domains, protocols, or ports. By default, browsers enforce a security policy called the Same-Origin Policy (SOP), which prevents web pages from making requests to a different origin. Cross-Origin Resource Sharing (CORS) is a mechanism that allows servers to explicitly indicate which origins are permitted to access their resources. This challenge focuses on implementing robust CORS handling in a Python web application.
Problem Description
Your task is to build a simple Python web application using Flask that serves a JSON API endpoint. You need to correctly configure Cross-Origin Resource Sharing (CORS) for this endpoint so that it can be accessed by a frontend application hosted on a different origin (e.g., a different port or domain).
What needs to be achieved:
- Create a basic Flask web server.
- Implement a GET endpoint that returns a simple JSON response.
- Configure CORS headers for this endpoint to allow requests from a specified origin.
Key requirements:
- Flask Application: Use the Flask web framework.
- JSON API Endpoint: Create a
/api/dataendpoint that returns a JSON object like{"message": "Hello from your API!"}. - CORS Configuration:
- Allow requests from a specific origin. For the purpose of this challenge, assume the frontend will be served from
http://localhost:3000. - The endpoint should allow GET requests.
- You should also consider allowing credentials (though for this basic example, it's not strictly necessary but good practice to think about).
- Allow requests from a specific origin. For the purpose of this challenge, assume the frontend will be served from
- Response Headers: Ensure the server sends the appropriate CORS headers in its response.
Expected behavior:
- When a request comes from
http://localhost:3000(or any origin you've configured to be allowed), the/api/dataendpoint should respond with the JSON data and the correct CORS headers. - When a request comes from an unallowed origin, the browser should block the request, and the server might not even receive it if it's a preflight request that fails.
Edge cases to consider:
- Preflight Requests (OPTIONS method): Browsers often send an
OPTIONSrequest (a preflight request) before the actualGETrequest to check if the server permits the actual request. Your server must handle theseOPTIONSrequests correctly by returning the appropriate CORS headers. - Different HTTP Methods: While this challenge focuses on GET, in a real-world scenario, you'd need to consider other methods like POST, PUT, DELETE.
Examples
Example 1: Successful CORS Request
Scenario: A frontend application running on http://localhost:3000 makes a GET request to http://localhost:5000/api/data.
Input (Frontend Request - simplified):
GET /api/data HTTP/1.1
Host: localhost:5000
Origin: http://localhost:3000
Output (Server Response):
HTTP/1.0 200 OK
Content-Type: application/json
Access-Control-Allow-Origin: http://localhost:3000
Vary: Origin
Content-Length: 34
{"message": "Hello from your API!"}
Explanation: The server correctly identifies the Origin header from the incoming request and includes it in the Access-Control-Allow-Origin header of the response. The Vary: Origin header is also important for caching proxies.
Example 2: Preflight Request Handling
Scenario: A frontend application running on http://localhost:3000 makes a request that requires a preflight (e.g., a POST with a custom header). For this example, we'll simulate a preflight for our GET request as browsers might do this.
Input (Frontend Preflight Request):
OPTIONS /api/data HTTP/1.1
Host: localhost:5000
Origin: http://localhost:3000
Access-Control-Request-Method: GET
Access-Control-Request-Headers: content-type
Output (Server Response):
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Credentials: true
Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers
Explanation: The server responds to the OPTIONS request, confirming that GET requests from http://localhost:3000 are allowed. It also specifies Access-Control-Allow-Methods and potentially Access-Control-Allow-Credentials to inform the browser about the server's capabilities. The Vary header is crucial here to ensure proper caching of preflight responses.
Example 3: Denied CORS Request
Scenario: A frontend application running on an unallowed origin, say http://another-domain.com:8080, makes a GET request to http://localhost:5000/api/data.
Input (Frontend Request):
GET /api/data HTTP/1.1
Host: localhost:5000
Origin: http://another-domain.com:8080
Output (Server Response):
HTTP/1.0 200 OK
Content-Type: application/json
Access-Control-Allow-Origin: null # Or no Access-Control-Allow-Origin header at all
Content-Length: 34
{"message": "Hello from your API!"}
(Browser will likely block this response to the frontend JavaScript.)
Explanation: If the server does not explicitly allow http://another-domain.com:8080, it should not include Access-Control-Allow-Origin: http://another-domain.com:8080 in its response. The browser will then enforce the SOP and prevent the frontend JavaScript from accessing the response.
Constraints
- The Flask application should listen on
http://localhost:5000. - The allowed origin for frontend requests is strictly
http://localhost:3000. - The API endpoint is
/api/data. - The response format for the endpoint must be JSON.
- You should use a Python solution.
Notes
- You can use the
flaskandflask-corslibraries to simplify CORS implementation. - Consider the differences between simple requests and preflight requests. Simple requests (GET, HEAD, POST with specific Content-Types and no custom headers) might not trigger an
OPTIONSpreflight, but it's good practice to configure for them. - The
flask-corslibrary abstracts away much of the complexity of handlingOPTIONSrequests and setting headers. - For local development, you can simulate different origins by running your frontend on a different port (e.g.,
npm startfor React often runs on 3000, while the Flask server runs on 5000). - Success is defined by the ability of a client running on
http://localhost:3000to successfully fetch data fromhttp://localhost:5000/api/datawithout encountering CORS errors in the browser console.