webman按照手册来,在中间件中配置了跨域,但是请求的时候还是出现了404问题

bang

以下是中间件代码:

<?php
namespace support\middleware;

use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;

/**
 * 跨域测试
 * Class AccessControl
 * @package support\middleware
 */
class AccessControl implements MiddlewareInterface
{
    public function process(Request $request, callable $next) : Response
    {
        /** @var Response $response */
        $response = $next($request);
        $response->withHeaders([
            'Access-Control-Allow-Origin' => '*',
            'Access-Control-Allow-Methods' => 'GET,POST,PUT,DELETE,OPTIONS',
            'Access-Control-Allow-Headers' => 'Content-Type,Authorization,X-Requested-With,Accept,Origin',
        ]);
        return $response;
    }
}

config目录下的配置

<?php
return [
    '' => [
        support\middleware\AccessControl::class
    ]
];

前端请求代码(使用了Jquery):

$.ajax({
            url : 请求地址,
            type : "post",
            dataType : 'JSON',
            data : data.field,
            success : function(response){

            },
            error : function (err) {

            }
        });

但是浏览器的ajax请求出错

跨域的问题没办法解决,是不是哪一步操作错误了呢?

5200 7 1
7个回答

walkor

路由配置贴下。

  • bang 2020-08-22

    <?php
    namespace app\route;
    use Webman\Route;
    Route::post('/login' , 'app\admin\controller\Auth\Admin@login');
    路由代码,其他的没有动过

bang
<?php
namespace app\route;

use Webman\Route;

Route::post('/login' , 'app\admin\controller\Auth\Admin@login');

这个是我路由代码

  • bang 2020-08-22

    用postman请求是没有问题的,意味着路由配置应该是没有问题

  • walkor 2020-08-22

    用 Route::any(),截图里浏览器发起的是OPTIONS请求,不是post

  • bang 2020-08-24

    @1:any方法中并不包括OPTIONS,我手动在Route.php文件加上去之后,请求会直接转发到控制器逻辑,这样不符合CORS逻辑,OPTIONS是浏览器发起一种“试探”吧,不应该直接转发到控制器。由于回复发不了图,我在下面再发图吧。

bang

修改前的any方法


    public static function any($path, $callback)
    {
        static::addRoute(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD' ], $path, $callback);
    }

修改后的any方法

    public static function any($path, $callback)
    {
        static::addRoute(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD' , 'OPTIONS'], $path,  $callback);
    }

请求修改前的any,结果如提问一样是404

截图

请求修改后的any,结果如下图,请求直接就访问到了业务逻辑,由于OPTIONS是不带任何的业务参数的,所有很大可能会报错。

截图

  • 暂无评论
walkor

升级下webman-frameworkwebman-framework从1.0.1开始路由增加了options支持。

所以你的路由应该设置为:

Route::options('/login' , function(){
    return response('');
});
Route::post('/login' , 'app\admin\controller\Auth\Admin@login');

这样将options路由和post路由分开,options路由就不用处理业务逻辑了。

  • bang 2020-08-24

    是不是项目中每个跨域api接口(几乎全部都是)都得做同样OPTIONS的路由?

walkor

是不是项目中每个跨域api接口(几乎全部都是)都得做同样OPTIONS的路由?

你可以直接在中间件里判断下当前是否是options请求,如果是则直接返回跨域header,不继续执行正常逻辑。这样就不用每个接口都设置options路由。中间件代码类似如下:

class AccessControl implements MiddlewareInterface
{
    public function process(Request $request, callable $next) : Response
    {
        /** @var Response $response */
        if ($request->method() == 'OPTIONS') {
            response = response('');
        } else {
            $response = $next($request);
        }
        $response->withHeaders([
            'Access-Control-Allow-Origin' => '*',
            'Access-Control-Allow-Methods' => 'GET,POST,PUT,DELETE,OPTIONS',
            'Access-Control-Allow-Headers' => 'Content-Type,Authorization,X-Requested-With,Accept,Origin',
        ]);
        return $response;
    }
}

再说说options请求
浏览器在特定情况下才会发起opitons请求。

1. 使用了下面任一HTTP 方法:  
PUT/DELETE/CONNECT/OPTIONS/TRACE/PATCH
2. 人为设置了以下集合之外首部字段: 
Accept/Accept-Language/Content-Language/Content-Type/DPR/Downlink/Save-Data/Viewport-Width/Width
3. Content-Type 的值不属于下列之一:  
application/x-www-form-urlencoded、multipart/form-data、text/plain

一般情况下ajax请求不会触发options请求。我这里经过测试,在ajax里参数设置contentType:'application/json'时,会触发options请求。

$.ajax({
            url : '地址',
            type : "post",
            contentType:'application/json', // 这里
            success : function(response){},
            error : function (err) {}
        });

如果你的项目发起了options请求,可以看下是符合哪个条件触发的,是否可以避免。

  • bang 2020-08-24

    感谢,升级和配置中间件之后,是可以了。我看fast-route是支持group的,但是webman没把group这个方法封装进去,慢慢来吧。祝webman发展越来越好!!

bang

反复测试过之后,由于文档中没说明框架的生命周期。我测过之后,发现请求后,先是路由再到中间件的。也就是说,其实在中间件中判断请求方式是否为OPTIONS不重要了。因为当ajax触发OPTIONS请求时,如果在路由文件没有写OPTIONS的对应路由时,就直接会报404,不会到中间件里面去了。代码如下:

<?php
Route::options('/login' , function(){
    return response('');
});
Route::post('/login' , 'app\admin\controller\Auth\Admin@login');

这样又回到了所有的路由都写一遍OPTIONS的路由的情况了,这样很累赘

问题如何重现?

请求时,代码如下:

$.ajax({
            url : routeUrl.login,
            type : "post",
            dataType : 'JSON',
            data : data.field,
            header:{token:'12312321'},
            success : function(response){
            },
            error : function (err) {
                console.log(err)
            }
        });
  • 暂无评论
bang

以下为个人的解决方法:
找到以下文件:vendor/workerman/webman-framework/src/App.php
在onMessage方法中添加一下代码:

public function onMessage(TcpConnection $connection, $request)
{
        //添加以下代码
        if($request->method() == 'OPTIONS'){
            $response = response('');
            //规则自己定
            $response->withHeaders([
                'Access-Control-Allow-Origin' => '*',
                'Access-Control-Allow-Methods' => 'GET,POST,PUT,DELETE,OPTIONS',
                'Access-Control-Allow-Headers' => 'Content-Type, Authorization, X-Requested-With, Accept, Origin, Authorization, Headers, token',
                'Access-Control-Request-Headers' => 'Headers, token',
            ]);
            static::send($connection, $response, $request);
            return null;
        }

    .....以下为官方代码,省略
}

然后重新开启workerman,完成!!!

  • walkor 2020-08-25

    用 Route::any(),然后中间件里判断OPTIONS就好了

  • bang 2020-08-25

    @1:使用any()也是可以,这个得看看业务逻辑需要吧

  • elibool 2021-10-11

    楼主的方法有效,但需要和 walkor 大佬的 app/middleware/AccessControlTest.php 中间件一起配合使用; view 前后端分离环境 ( jquery ajax , postman 无上述跨域问题)

年代过于久远,无法发表回答
🔝