I am facing a CORS issue while attempting to make a POST request from my React frontend to a PHP backend. The error message in the browser console is as follows:
Access to fetch at 'http://localhost/mcha-express/website/api/v1/book/:csrfToken' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
book.js:33
POST http://localhost/mcha-express/website/api/v1/book/:csrfToken net::ERR_FAILED
book.js:62 Error submitting the form: TypeError: Failed to fetch
at handleSubmit (book.js:33:1)
at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1)
at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:1)
at invokeGuardedCallback (react-dom.development.js:4277:1)
at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:4291:1)
at executeDispatch (react-dom.development.js:9041:1)
at processDispatchQueueItemsInOrder (react-dom.development.js:9073:1)
at processDispatchQueue (react-dom.development.js:9086:1)
at dispatchEventsForPlugins (react-dom.development.js:9097:1)
at react-dom.development.js:9288:1
Here are the relevant parts of my frontend and backend code:
// Frontend (book.js):
import React, { useState, useEffect } from 'react';
import Navbar from './navbar';
import Footer from './footer';
function Book(){
useEffect(() => {
document.title = '';
}, []);
const [data, setData] = useState({
full_name: '',
email_address: '',
contact_number: '',
complete_address: '',
details: ''
});
const [submitting, setSubmitting] = useState(false);
const handleInputChange = (e) => {
const { name, value } = e.target;
setData((prevData) => ({
...prevData,
[name]: value
}));
};
const handleSubmit = async (e) => {
e.preventDefault();
setSubmitting(true);
try {
const response = await fetch('http://localhost/mcha-express/website/api/v1/book/:csrfToken', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
csrfToken: 'a73f54b65f76467e9413a5c5fde2e32d1f3b4b82c7c0b0634a12bd6ca0b87dc2',
...data,
}),
});
const result = await response.json();
// Handle the response accordingly
if (response.ok) {
console.log(result.message); // Successful response
// Reset the form after successful submission if needed
setData({
full_name: '',
email_address: '',
contact_number: '',
complete_address: '',
details: '',
});
} else {
console.error(result.error); // Error response
// Handle error, display message, etc.
}
} catch (error) {
console.error('Error submitting the form:', error);
} finally {
setSubmitting(false);
}
};
return (
<>
<Navbar/>
{/* Book Section Start */}
<section className='book'>
<div className='book-bg'></div>
<div className='container'>
<form onSubmit={handleSubmit}>
<h1>BOOK</h1>
<p>Book now for effortless parcel delivery with us! Simply fill out the form, secure your slot, and leave the rest to us.</p>
<div className='form-labels'>
<div className={'response-message error'}>
<span>Submitting</span>
</div>
<div className='labels'>
<input
type='text'
placeholder='Full Name'
name='full_name'
value={data.full_name}
onChange={handleInputChange}
/>
<input
type='email'
placeholder='Email Address'
name='email_address'
value={data.email_address}
onChange={handleInputChange}
/>
</div>
<div className='labels'>
<input
type='number'
placeholder='Contact Number'
name='contact_number'
value={data.contact_number}
onChange={handleInputChange}
/>
<input
type='text'
placeholder='Complete Address'
name='complete_address'
value={data.complete_address}
onChange={handleInputChange}
/>
</div>
<div className='label'>
<textarea
rows='5'
placeholder='Please provide the details of your requirements...'
name='details'
value={data.details}
onChange={handleInputChange}
></textarea>
</div>
<div className='label'>
<button type='submit'>
<span>Submit</span>
</button>
</div>
</div>
</form>
</div>
</section>
{/* Book Section End */}
{/* Footer Section Start */}
<Footer/>
{/* Footer Section End */}
</>
);
}
export default Book;
Backend (index.php):
<?php
$request_uri = $_SERVER['REQUEST_URI'];
$uri_parts = explode('/', $request_uri);
$endpoint = implode('/', array_slice($uri_parts, 2));
include 'api.php';
// POST book
if (strpos($endpoint, 'website/api/v1/book') !== false) {
header('Content-Type: application/json');
$csrfToken = end($uri_parts);
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
post_book($csrfToken);
} else {
http_response_code(405);
echo json_encode(['error' => 'Invalid request method for this endpoint']);
}
}
?>
Backend (api.php):
<?php
// Security Headers
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: POST, GET, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, *");
include 'config.php';
session_start();
// Ito yung magkicreate ng csrf token
function generateCsrfToken() {
if (!isset($_SESSION['csrf_token']) || !isset($_SESSION['csrf_token_expire']) || $_SESSION['csrf_token_expire'] < time()) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
$_SESSION['csrf_token_expire'] = time() + 300;
}
echo json_encode([
'csrfToken' => $_SESSION['csrf_token'],
'expiryDate' => $_SESSION['csrf_token_expire'],
]);
}
// Ito yung magbavalidate ng token
function validateCsrfToken($sentToken) {
return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $sentToken);
}
// Ichicheck kung yung csrf token ba is existing
function checkCsrfToken($sentCsrfToken) {
if (!$sentCsrfToken || !validateCsrfToken($sentCsrfToken)) {
http_response_code(403);
echo json_encode(['error' => 'CSRF Token Validation Failed']);
exit;
}
}
// POST method ng pag iinsert ng book
function post_book($csrfToken) {
global $pdo;
// Defined variable
$booking_id = 'BI-' . mt_rand(1000000000, 9999999999);
$full_name = filter_input(INPUT_POST, 'full_name', FILTER_SANITIZE_STRING);
$email_address = filter_input(INPUT_POST, 'email_address', FILTER_SANITIZE_EMAIL);
$contact_number = filter_input(INPUT_POST, 'contact_number', FILTER_SANITIZE_STRING);
$complete_address = filter_input(INPUT_POST, 'complete_address', FILTER_SANITIZE_STRING);
$details = filter_input(INPUT_POST, 'details', FILTER_SANITIZE_STRING);
// Ichicheck kung invalid ba yung csrf token
if ($csrfToken !== $_SESSION['csrf_token']) {
http_response_code(403);
echo json_encode(['error' => 'CSRF Token Validation Failed']);
exit;
}
// Kapag merong isang field yung missing
if (!$csrfToken || !$full_name || !$email_address || !$contact_number || !$complete_address || !$details) {
http_response_code(400);
echo json_encode(['error' => 'Invalid input data']);
exit;
}
// Taga check kung yung booking_id is nageexist naba
$existingBookingQuery = $pdo->prepare("SELECT COUNT(*) FROM book WHERE booking_id = ?");
$existingBookingQuery->execute([$booking_id]);
$existingBookingCount = $existingBookingQuery->fetchColumn();
if ($existingBookingCount > 0) {
http_response_code(400);
echo json_encode(['error' => 'Booking ID already exists']);
exit;
}
$sql = "INSERT INTO book (booking_id, full_name, email_address, contact_number, complete_address, details)
VALUES (?, ?, ?, ?, ?, ?)";
$stmt = $pdo->prepare($sql);
$stmt->bindParam(1, $booking_id);
$stmt->bindParam(2, $full_name);
$stmt->bindParam(3, $email_address);
$stmt->bindParam(4, $contact_number);
$stmt->bindParam(5, $complete_address);
$stmt->bindParam(6, $details);
if ($stmt->execute()) {
echo json_encode(['message' => 'Booking successfully submitted!']);
} else {
echo json_encode(['error' => '[Error] Failed to submit your booking.']);
}
$stmt->closeCursor();
}
?>
Question Details:
- I have already set the necessary CORS headers in my PHP backend.
- The error specifically mentions an issue with the preflight OPTIONS request not having an HTTP ok status.
What I’ve Tried:
- I have checked and confirmed that my CORS headers are correctly set.
- I have reviewed similar questions on Stack Overflow, but none of the solutions seem to resolve my issue.
Request for Assistance:
I would appreciate any insights or suggestions on how to resolve this CORS issue. Is there something I might be missing in my setup? Any help would be greatly appreciated!