Using Laravel's resource routes, I've set up an API to serve as the back-end of a React JS application. I'm attempting to access the 'update' method currently. I'm using Javascript's fetch()
to accomplish this, so its making one OPTIONS
request first, then making the POST
request (the form has a method spoof in it, setting _method
to PATCH
instead - this obviously doesn't affect the initial OPTIONS
call). This same page is also making a GET
request to the same endpoint via the same method, which works fine.
The fetch()
call is below. Of course, this being React, it's called through a Redux Saga process, but the actual fetch is there.
function postApi(values, endpoint, token) { // <-- values and endpoint are sent by the component, token is sent by a previous Saga function
return fetch(apiUrl + endpoint, { // <-- apiUrl is defined as a constant earlier in the file
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
},
body: JSON.stringify(
values
)
}).then(handleApiErrors)
.then(response => response.json())
.catch((error) => {throw error})
}
And the Laravel routes:
Route::group(['middleware' => 'auth:api'], function() {
Route::resource('users', 'UserController');
}
I was encountering an error where the initial OPTIONS
request to the URL was returning a 404
error, which right away is strange, since the endpoint obviously exists, the exact same endpoint having just been queried seconds ago, but I assumed maybe Laravel was returning the wrong error, and I had used the wrong method. I did some digging and debugging trying to get the request to be correct before giving up and making the request in Postman. The thing is: it works fine in Postman.
Here are the response headers from the server (note that any access origin is permitted):
Access-Control-Allow-Origin:*
Cache-Control:no-cache, private
Connection:close
Content-Length:10
Content-Type:text/html; charset=UTF-8
Date:Thu, 21 Sep 2017 13:29:08 GMT
Server:Apache/2.4.27 (Unix) OpenSSL/1.0.2l PHP/7.0.22 mod_perl/2.0.8-dev Perl/v5.16.3
X-Powered-By:PHP/7.0.22
Here's the request headers for the request as made from the React JS app (the one that receives a 404 error):
Accept:*/*
Accept-Encoding:gzip, deflate, br
Accept-Language:en-US,en;q=0.8,fr;q=0.6,ga;q=0.4
Access-Control-Request-Headers:authorization,content-type
Access-Control-Request-Method:POST
Cache-Control:no-cache
Connection:keep-alive
Host:localhost
Origin:http://localhost:3000
Pragma:no-cache
Referer:http://localhost:3000/employees/edit/13
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36
In Postman, I set up those exact same request headers and made the exact same OPTIONS
request to the server. And it worked fine! I received an empty 200 response.
Just to be sure, I double-checked the Apache access log. And sure enough:
...
::1 - - [20/Sep/2017:15:33:24 -0400] "OPTIONS /the/path/to/api/users/13 HTTP/1.1" 200 -
::1 - - [20/Sep/2017:15:40:26 -0400] "OPTIONS /the/path/to/api/users/13 HTTP/1.1" 404 10
...
Request method the exact same, request URL the exact same, except one returned 200, the other returned 404, for no discernable reason.
Additionally, I should add that another POST
request, to the create
method, works just fine.
What could be causing this?
ATTEMPTED SOLUTIONS
1. I saw this question (React Native + fetch + API : DELETE request fails in App, works in Postman), and even though I'm running Apache, not Nginx, I thought I'd try adding a trailing slash to the request URL. The OPTIONS
request now returns a 301 error (moved permanently).
2. I removed the trailing slash and continued trying to fix. Per comment suggestion, I removed the automatic route generation and created my own:
Route::get('/users', 'UserController@index');
Route::post('/users', 'UserController@create');
Route::put('/users/{user}', 'UserController@update');
Route::patch('/users/{user}', 'UserController@update');
Route::get('/users/{user}', 'UserController@show');
The Postman request still returns 200 OK, and the React request still returns 404 Not Found.
3. Eureka! Kind of. Per another comment suggestion, I exported the request from Chrome as cURL and imported it into Postman directly - maybe I missed something when copying the headers over. It seems I did, because now the Postman request also returns 404!
After playing around with disabling and/or enabling the imported headers, I've determined that the issue is the combination of the Origin
and Access-Control-Request-Method
headers. If only one is present the request returns 200, but if both are present I receive a 404.
This does still leave me with the question of how to fix the problem, however. At this point I wonder if the question might become more of a Laravel question - IE, why an OPTIONS
request to a perfectly valid Resource route would return 404. I assume because those resources routes are listening for PUT
or PATCH
but not OPTIONS
.