我用Claude帮我搞了个号称更简单的一个域名指向一个应用的方案,大家看看是否可行。

wocall

他说:
webman 多应用的路由解析逻辑,核心在 webman-framework 的 App.php 里,它解析 URL 的第一段作为应用名。所以所有问题都来自同一个根:框架用 URL 路径前缀识别应用,而不是 Host。

整条对话链揭示了一个本质:webman 多应用是用 URL 第一段识别应用,不是用 Host。所有方案都是在绕这个设计,不是真正解决它。

绕法有两种:

1、在请求进 webman 之前改路径(Nginx rewrite)
2、在 webman 内部改路径(PHP 层)

真正最简单、最稳固的方案:全局中间件改 URI

读完文档和帖子后,发现一个被忽视的细节:webman 的路由匹配用的是 $request->path(),而 path() 的来源是 $_SERVER['REQUEST_URI'],这是一个普通的 PHP 超全局变量,在中间件里完全可以修改。

关键验证点

$_SERVER['REQUEST_URI'] 修改是否在路由匹配前生效?是的——webman 的中间件执行顺序是全局中间件 → 路由匹配 → 控制器,@ 超全局中间件是最早执行的,此时路由还没匹配,$_SERVER['REQUEST_URI'] 改了之后,后续 $request->path() 返回的就是新路径,路由匹配拿到的也是新路径。

这才是真正从根上解决问题,不依赖 Nginx,不依赖任何插件,不会有同名控制器冲突。

下面是具体代码:
第一步:创建配置文件

// config/domain.php
return [
    'shop.example.com'  => 'shop',
    'admin.example.com' => 'admin',
    'blog.example.com'  => 'blog',
];

第二步:创建中间件

<?php
// app/middleware/DomainToApp.php

namespace app\middleware;

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

class DomainToApp implements MiddlewareInterface
{
    public function process(Request $request, callable $handler): Response
    {
        $map  = config('domain', []);
        $host = $request->host(true); // true = 去掉端口号

        // 域名不在白名单,直接 404
        if (!isset($map[$host])) {
            return response('Not Found', 404);
        }

        $app  = $map[$host];
        $path = $request->path(); // e.g. "/"  或  "/user/list"

        // 有对应应用,且路径还没带前缀,则插入
        if ($app !== '' && !str_starts_with($path, '/' . $app)) {
            $newPath = '/' . $app . ($path === '/' ? '' : $path);
            $qs      = $request->queryString();
            $_SERVER['REQUEST_URI'] = $newPath . ($qs ? '?' . $qs : '');
        }

        return $handler($request);
    }
}

第三步:注册为超全局中间件

// config/middleware.php
return [
    '@' => [
        app\middleware\DomainToApp::class,
    ],
    '' => [
        // 其他全局中间件...
    ],
];

@ 是超全局,会在所有路由匹配和其他中间件之前执行,且对插件也生效。

第四步:目录结构确认一下

你的 app/ 目录需要是这样:

app/
├── shop/
│ ├── controller/
│ │ └── IndexController.php
│ └── model/
├── admin/
│ ├── controller/
│ │ └── IndexController.php ← 同名没问题,命名空间不同
│ └── model/
└── blog/
├── controller/
│ └── IndexController.php
└── model/

控制器命名空间分别是 app\shop\controller、app\admin\controller、app\blog\controller,互不干扰。

验证

启动 webman 后:

访问 实际路由到
shop.example.com/user/list app\shop\controller\UserController::list
admin.example.com/user/list app\admin\controller\UserController::list
unknown.example.com/anything 404

本地测试时没有真实域名,可以改 hosts 文件:

127.0.0.1 shop.example.com
127.0.0.1 admin.example.com
127.0.0.1 blog.example.com

然后访问 http://shop.example.com:8787/user/list 即可验证。

新增域名只需改一处

以后加一个应用 api:

config/domain.php 加一行 'api.example.com' => 'api'
创建 app/api/controller/ 目录开始写代码

Nginx 不用动,domain 插件不用装,重启 webman 生效。

278 1 0
1个评论

shiroi

额,这个实现方案,很久以前就有了啊

  • 暂无评论

wocall

754
积分
0
获赞数
0
粉丝数
2021-11-14 加入
🔝