-
Notifications
You must be signed in to change notification settings - Fork 176
Description
Expected Behavior
Based on the original discussion in issue #4446, simply enabling the CORS middleware should be sufficient to support a complete browser request cycle, including handling preflight OPTIONS requests automatically without requiring explicit route definitions.
Current Behavior
When using the CORS middleware with the experimental REST event handler, preflight OPTIONS requests result in a 404 error because the middleware doesn't appear to short-circuit the normal routing process. This requires manually defining OPTIONS routes for each endpoint that needs CORS support, which defeats the purpose of having automatic CORS handling.
Browser error: Failed to load resource: Preflight response is not successful. Status code: 404
Code snippet
import { Router } from '@aws-lambda-powertools/event-handler/experimental-rest';
import {
compress,
cors,
} from '@aws-lambda-powertools/event-handler/experimental-rest/middleware';
import { Logger } from '@aws-lambda-powertools/logger';
import type { Context } from 'aws-lambda';
const logger = new Logger({ serviceName: 'pong-service' });
const app = new Router();
app.use(async (_, reqCtx, next) => {
logger.info('Request received', {
headers: reqCtx.event.headers,
});
await next();
});
app.use(compress());
app.use(cors());
app.get('/ping', async () => {
return { message: 'pong' };
});
app.post('/submit', async (_, reqCtx) => {
const body = await reqCtx.request.json();
logger.info('Form submitted', { body });
return { message: 'submitted' };
});
export const handler = async (event: unknown, context: Context) =>
app.resolve(event, context);
Steps to Reproduce
- Set up a Lambda function using the experimental REST event handler with CORS middleware as shown above
- Create an HTML form that submits a POST request to the /submit endpoint from a different origin (see below for HTML code)
- Submit the form from a browser
- Observe that the preflight OPTIONS request fails with a 404 error
Click here for webpage w/ form code
```html <title>Comment Form</title> <style> body { font-family: Arial, sans-serif; max-width: 500px; margin: 50px auto; padding: 20px; } .form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input[type="text"] {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
button {
background-color: #007bff;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.message {
margin-top: 15px;
padding: 10px;
border-radius: 4px;
}
.success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
</style>
Submit Comment
<form id="commentForm">
<div class="form-group">
<label for="comment">Comment:</label>
<input type="text" id="comment" name="comment" required>
</div>
<button type="submit" id="submitBtn">Submit Comment</button>
</form>
<div id="message"></div>
<script>
document.getElementById('commentForm').addEventListener('submit', async function(e) {
e.preventDefault();
const submitBtn = document.getElementById('submitBtn');
const messageDiv = document.getElementById('message');
const commentInput = document.getElementById('comment');
const comment = commentInput.value.trim();
if (!comment) {
showMessage('Please enter a comment', 'error');
return;
}
submitBtn.disabled = true;
submitBtn.textContent = 'Submitting...';
messageDiv.innerHTML = '';
try {
const response = await fetch('your API/prod/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ comment: comment })
});
if (response.ok) {
const result = await response.json();
showMessage('Comment submitted successfully!', 'success');
commentInput.value = '';
} else {
showMessage('Failed to submit comment. Please try again.', 'error');
}
} catch (error) {
showMessage('Error submitting comment: ' + error.message, 'error');
} finally {
submitBtn.disabled = false;
submitBtn.textContent = 'Submit Comment';
}
});
function showMessage(text, type) {
const messageDiv = document.getElementById('message');
messageDiv.textContent = text;
messageDiv.className = 'message ' + type;
}
</script>
Possible Solution
The CORS middleware should intercept and handle OPTIONS requests before they reach the normal routing logic. Currently, it appears that preflight requests are being processed through the standard route matching, which results in a 404 when no explicit OPTIONS route is defined.
As a workaround, manually adding OPTIONS routes works:
app.options('/submit', async () => {
return new Response(null, { status: 204 });
});
However, this shouldn't be necessary if the CORS middleware is functioning as intended.
Powertools for AWS Lambda (TypeScript) version
latest
AWS Lambda function runtime
22.x
Packaging format used
npm
Execution logs
Browser console error: Failed to load resource: Preflight response is not successful. Status code: 404
Note: I'm uncertain whether this is a bug in the middleware logic or a misunderstanding of the expected behavior. The issue might be related to the middleware not properly short-circuiting the request processing for preflight OPTIONS requests, but further investigation would be needed to confirm the root cause.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status