【问题标题】:Exact Same Request Returns 200 to Postman but 404 to React App完全相同的请求返回 200 给 Postman,但返回 404 给 React App
【发布时间】:2018-03-02 00:47:00
【问题描述】:

使用 Laravel 的资源路由,我设置了一个 API 作为 React JS 应用程序的后端。我目前正在尝试访问“更新”方法。我正在使用 Javascript 的 fetch() 来完成此操作,因此它首先发出一个 OPTIONS 请求,然后发出 POST 请求(表单中有一个欺骗方法,将 _method 设置为 PATCH 而不是 - 这个显然不会影响最初的OPTIONS 调用)。同一页面通过相同的方法向同一端点发出GET 请求,效果很好。

fetch() 电话如下。当然,这是 React,它是通过 Redux Saga 进程调用的,但实际的 fetch 在那里。

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})
}

还有 Laravel 路线:

Route::group(['middleware' => 'auth:api'], function() {
    Route::resource('users', 'UserController');
}

我遇到了一个错误,其中对 URL 的初始 OPTIONS 请求返回了一个 404 错误,这立刻很奇怪,因为端点显然存在,完全相同的端点在几秒钟前刚刚被查询过,但是我假设 Laravel 可能返回了错误的错误,并且我使用了错误的方法。在放弃并在 Postman 中提出请求之前,我进行了一些挖掘和调试,试图让请求正确。问题是:它在 Postman 中工作正常

以下是来自服务器的响应标头(请注意,允许任何访问来源):

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

这是来自 React JS 应用程序(收到 404 错误的应用程序)发出的请求的请求标头:

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

在 Postman 中,我设置了完全相同的请求标头并向服务器发出完全相同的 OPTIONS 请求。而且效果很好!我收到了一个空的 200 响应。

为了确定,我仔细检查了 Apache 访问日志。果然:

...
::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
...

请求方法一模一样,请求URL一模一样,除了一个返回200,另一个返回404,无从得知。

此外,我应该将 另一个 POST 请求添加到 create 方法中,效果很好。

这可能是什么原因造成的?


尝试的解决方案

1. 我看到了这个问题 (React Native + fetch + API : DELETE request fails in App, works in Postman),尽管我运行的是 Apache,而不是 Nginx,但我想我会尝试在请求 URL 中添加一个斜杠。 OPTIONS 请求现在返回 301 错误(永久移动)。

2. 我删除了尾部斜线并继续尝试修复。根据评论建议,我删除了自动生成路线并创建了自己的路线:

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');

Postman 请求仍然返回 200 OK,React 请求仍然返回 404 Not Found。

3. 尤里卡! 有点。根据另一个评论建议,我将来自 Chrome 的请求导出为 cURL 并将其直接导入 Postman - 也许我在复制标题时错过了一些东西。看来我做到了,因为现在邮递员请求返回404!

在尝试禁用和/或启用导入的标头后,我确定问题是OriginAccess-Control-Request-Method 标头的组合。如果只有 one 存在,则请求返回 200,但如果 both 存在,我会收到 404。

但是,这仍然给我留下了如何解决问题的问题。在这一点上,我想知道这个问题是否可能更像是一个 Laravel 问题 - IE,为什么对完全有效的资源路由的 OPTIONS 请求会返回 404。我假设是因为这些资源路由正在监听 PUTPATCH但不是OPTIONS

【问题讨论】:

  • 很好的问题,但在问题中添加实际的fetch 调用可能有助于更好地理解。
  • 也发布你的 Laravel 路线。
  • 添加了 fetch() 示例和 Laravel 路由。
  • 服务器的 CORS 设置怎么样?你能告诉我们这些吗?另外,在浏览器的开发工具中,除了 404 之外,您是否在控制台中看到任何错误?
  • 遗憾的是,除了 404 本身之外,控制台中根本没有错误。添加了来自服务器的响应头。

标签: apache laravel reactjs


【解决方案1】:

既然您已经设置好 CORS,接下来您需要做的就是处理“预检”OPTIONS 请求。您可以使用中间件来做到这一点:

PreflightRequestMiddleware:

if ($request->getMethod() === $request::METHOD_OPTIONS) {
    return response()->json([],204);
}

return $next($request);

在新创建的中间件的handle()方法中添加上述代码。在全局中间件堆栈中添加中间件。

另外,不要忘记在 CORS 设置中将 OPTIONS 方法添加到 Access-Control-Allow-Methods

更多选项,请查看this

答案:

阅读this 文章。当浏览器向您的应用程序发送 OPTIONS 请求时,应用程序无法处理它,因为您只为给定端点定义了 GET/POST/DELETE/PUT/PATCH 路由。

所以,为了让这条路线能够处理预检请求:

Route::get('/users', 'UserController@index');

它需要一个相应的 OPTIONS 路由:

Route::options('/users', 'UserController@options');

注意:您将使用中间件在一个地方处理所有 OPTIONS 请求。但是,如果您将 OPTIONS 请求用于其他目的 - 请查看此答案中的第一个链接以获取更多选项。

【讨论】:

    猜你喜欢
    • 2022-11-02
    • 1970-01-01
    • 1970-01-01
    • 2014-08-04
    • 2019-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多