选项预检导致POST请求挂起 [英] OPTIONS Preflight causing POST request to hang

查看:158
本文介绍了选项预检导致POST请求挂起的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的angular应用程序遇到了CORS OPTIONS飞行前请求的问题.简而言之,我正在尝试实现一个简单的登录名,该登录名将POST请求发送到我的服务器,该服务器具有使用PHP \ Slim构建的后端,并带有用户的凭据. CORS OPTIONS首先请求,然后我以苗条的方式处理它,并以200 OK响应,因此,当浏览器获得OK时,它最终将凭据发送给我,而后挂起...没有响应代码,什么都没有.

老实说,这让我感到困惑,我实际上是在猜测我的请求服务有问题,因为这是唯一发送OPTIONS请求的服务.

疯狂的是,在我向垃圾邮件发送了登录按钮后,它最终屈服并做出了响应.

I checked the network requests and this is the idea of what it looks like:

200 OPTIONS
    POST
200 OPTIONS
    POST

the posts are just blank

这是我的服务.

  signin(user: User): Observable<Result> {
    return this._http.post<Result>( this.base + this.signinURL, user )
      .pipe(catchError(this.error));
  }

  private error(error: HttpErrorResponse) {
    return throwError(error);
  }

这是与CORS相关的路线

$app->options('/{routes:.+}', function (Request $request, Response $response, $args) {
    return $response->withStatus(200);
});
$app->add(function (Request $req, Response $res, $next) {
    $response = $next($req, $res);
    return $response
        ->withHeader('Access-Control-Allow-Origin', 'http://localhost:4200')
        ->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
        ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
});
$app->map(['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], '/{routes:.+}', function(Request $req, Response $res) {
    $handler = $this->notFoundHandler;
    return $handler($req, $res);
});

值得一提的是,我在Angular CLI服务器上运行前端,在PHP Server v7.3上运行PHP.

如果有人可以向我解释发生了什么以及为什么我会感到高兴,请多谢...:)

解决方案

客户端(浏览器)发送OPTIONS请求;浏览器会在每次请求之前自动完成此操作.一个OPTIONS请求只是告诉客户它允许发出什么样的请求(POST,GET,DELETE等).请注意,由于您的代码未显示任何明确的OPTIONS请求,因此我假设您的应用程序是Web应用程序.

由于您在Chrome中看不到POST正文的原因与您的服务问题无关.这是铬特定的东西;它基本上与页面刷新/重定向有关-Chrome不会保留帖子正文.如果您保留chrome日志,并在进行任何重新加载/刷新/重定向之前停止网络,则会看到正文.有一个特定的原因会导致这种情况发生,我无法回忆起我在哪里阅读它.我的建议是只在本地登录.

对于您的API,您的OPTIONS处理看起来有些有趣. $app->options是您捕获所有OPTIONS请求的位置,并且您正在做的所有操作都返回200;您没有定义标题.未经测试,我认为这是您的问题.

我会说掉所有这些,而只是做这样的事情:

 $app = new \Slim\App();

$app->add(function (Request $request, Response $response, $next) {
    if($request->getMethod() !== 'OPTIONS') {
        return $next($request, $response);
    }

    $response = $response->withHeader('Access-Control-Allow-Origin', '*');
    $response = $response->withHeader('Access-Control-Allow-Methods', $request->getHeaderLine('Access-Control-Request-Method'));
    $response = $response->withHeader('Access-Control-Allow-Headers', $request->getHeaderLine('Access-Control-Request-Headers'));

    return $next($request, $response);
});

# ... all your other routes ...

$app->run();
 

或者,为什么不让您的http服务器自己处理OPTIONS请求呢?

就我而言,我会注意到我也在PHP 7.3中运行基于Slim的API,我不必理会OPTIONS请求,只需在虚拟主机配置(apache2)中设置标头即可.像这样:

 <VirtualHost *:443>

    ...

    <IfModule mod_headers.c>
            # allowable request origins
            Header set Access-Control-Allow-Origin "*"

            # allowable request types
            Header set Access-Control-Allow-Methods "POST, GET, DELETE, OPTIONS"

            # how long to cache results of preflight requests
            Header set Access-Control-Max-Age "1800"

            # allowable headers
            Header set Access-Control-Allow-Headers "x-requested-with, Content-Type, origin, authorization, accept"

            # permit preflight response requests using credentials
            Header set Access-Control-Allow-Credentials "true"
    </IfModule>

    ...

</VirtualHost>
 

I've been having issues with the CORS OPTIONS preflight request with my angular application. To be brief I'm trying to implement a simple login which sends a POST request to my server, which has a backend built using PHP\Slim, carrying the user's credentials. The CORS OPTIONS requests first and I handle it with slim to respond with a 200 OK, so when the browser gets the OK it finally sends my POST with the credentials, but then it just hangs...no response code, nothing.

Honestly, this is boggling my mind and I'm literally guessing there's something wrong with my request service because this is the only service sending an OPTIONS request, to begin with.

The crazy thing is that after I spam the login button, it eventually gives in and responds.

I checked the network requests and this is the idea of what it looks like:

200 OPTIONS
    POST
200 OPTIONS
    POST

the posts are just blank

This is my service.

  signin(user: User): Observable<Result> {
    return this._http.post<Result>( this.base + this.signinURL, user )
      .pipe(catchError(this.error));
  }

  private error(error: HttpErrorResponse) {
    return throwError(error);
  }

here's the CORS related routes

$app->options('/{routes:.+}', function (Request $request, Response $response, $args) {
    return $response->withStatus(200);
});
$app->add(function (Request $req, Response $res, $next) {
    $response = $next($req, $res);
    return $response
        ->withHeader('Access-Control-Allow-Origin', 'http://localhost:4200')
        ->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
        ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
});
$app->map(['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], '/{routes:.+}', function(Request $req, Response $res) {
    $handler = $this->notFoundHandler;
    return $handler($req, $res);
});

Also worth mentioning is that I'm running the front-end on the Angular CLI server and the PHP on the PHP Server v7.3.

If someone could just explain to me what is happening and why I'd be happy, thanks for the thoughts...:)

解决方案

The client (browser) is what sends the OPTIONS request; this is done automatically by the browser before every request. An OPTIONS request simply tells the client what kind of request it's permitted to make (POST, GET, DELETE, etc). Note that I'm assuming your application is a web-application since your code does not demonstrate any explicit OPTIONS request being made.

As for the reason why you aren't seeing the POST body in Chrome, is unrelated to your service issues. This is a chrome specific thing; it basically has to do with page refreshes/redirects --- Chrome won't persist the post body. If you preserve the chrome log, and stop the network before any reload/refresh/redirect can take place, you'll see the body. There's a specific reason this happens i just cant recall where I read it. My advice is just to log this locally instead.

As for your API, your OPTIONS handling looks a little funny. $app->options is where you're catching all OPTIONS requests, and all you are doing is returning 200; you aren't defining your headers. Without testing, I'm assuming this is your issue.

I'd say drop all of that, and instead just do something like this:

$app = new \Slim\App();

$app->add(function (Request $request, Response $response, $next) {
    if($request->getMethod() !== 'OPTIONS') {
        return $next($request, $response);
    }

    $response = $response->withHeader('Access-Control-Allow-Origin', '*');
    $response = $response->withHeader('Access-Control-Allow-Methods', $request->getHeaderLine('Access-Control-Request-Method'));
    $response = $response->withHeader('Access-Control-Allow-Headers', $request->getHeaderLine('Access-Control-Request-Headers'));

    return $next($request, $response);
});

# ... all your other routes ...

$app->run();

Alternatively, why not just let your http server handle the OPTIONS requests itself?

In my case, which I'll note i'm also running a Slim based API in PHP 7.3, I don't bother handling the OPTIONS request, I just set the headers in my virtual host configuration (apache2). Something like this:

<VirtualHost *:443>

    ...

    <IfModule mod_headers.c>
            # allowable request origins
            Header set Access-Control-Allow-Origin "*"

            # allowable request types
            Header set Access-Control-Allow-Methods "POST, GET, DELETE, OPTIONS"

            # how long to cache results of preflight requests
            Header set Access-Control-Max-Age "1800"

            # allowable headers
            Header set Access-Control-Allow-Headers "x-requested-with, Content-Type, origin, authorization, accept"

            # permit preflight response requests using credentials
            Header set Access-Control-Allow-Credentials "true"
    </IfModule>

    ...

</VirtualHost>

这篇关于选项预检导致POST请求挂起的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆