关于自定义应用名映射问题

renloong

问题描述

@walkor

邀请walkor大佬看看,这个路由方案能不能行

情况是这样:前端访问接口的方式是地址栏path+api
前端因为是打包好的,PHP开发者基本上不需要管前端。
这个时候PHP开发者在后端更改了admin入口之后,前端的接口地址会自动变更为PHP开发者定义的admin入口,那么在这种情况下admin的所有地址都需要变更为自定义的入口。
以下这个方案实现了控制器和中间件调用,对于webman框架来说,还有很多功能和问题。

需要的帮助:能不能实现一下自定义应用名映射或者帮忙指点一下问题

为此你搜索到了哪些方案及不适用的原因

webman-admin的后台路径如何更改?
webman-admin修改默认路由

以上这两种方案均只能更改Index/index,并不能全部替换

我的解决方案

<?php

/**
 * 自定义Admin入口
 * @authors 余心(RenLoong)
 * @home https://github.com/RenLoong/loong-admin
 */

use app\expose\utils\Str;
use support\Log;
use Webman\Route;

$admin_path = getenv('SERVER_ADMIN_PATH');
if ($admin_path && $admin_path != 'admin') {
    Route::any('/admin[{path:.+}]', function () {
        $request = request();
        $error = "ADMIN ROUTE:{$request->getRealIp()} {$request->method()} {$request->host(true)}{$request->uri()}";
        Log::error($error);
        return not_found();
    });
    Route::any('/' . $admin_path . '[{path:.+}]', function () {
        $args = func_get_args();
        $request = $args[0];
        $pathArr = explode('/', $request->path());
        // Str::title(),转为首字母大写的标题格式
        $controller = Str::title(empty($pathArr[2]) ? 'Index' : $pathArr[2]);
        $action = empty($pathArr[3]) ? 'index' : $pathArr[3];
        $controller = '\\app\\admin\\controller\\' . $controller . 'Controller';
        if (!class_exists($controller)) {
            $error = "ADMIN ROUTE:{$request->getRealIp()} {$request->method()} {$request->host(true)}{$request->uri()}";
            Log::error($error);
            return not_found();
        }
        $request->app = 'admin';
        $request->controller = $controller;
        $request->action = $action;
        $config_middlewares = config('middleware');
        $common_middlewares = isset($config_middlewares['']) ? $config_middlewares[''] : [];
        $admin_middlewares = isset($config_middlewares['admin']) ? $config_middlewares['admin'] : [];
        $middlewares = array_merge($common_middlewares, $admin_middlewares);

        // 最终的控制器处理逻辑(核心 handler)
        $coreHandler = function () use ($controller, $action, $args) {
            return call_user_func_array([new $controller, $action], $args);
        };

        // 构建中间件洋葱圈
        $dispatcher = array_reduce(
            array_reverse($middlewares),
            function ($next, $middleware) {
                return function ($request) use ($middleware, $next) {
                    $middleware = new $middleware;
                    return $middleware->process($request, $next);
                };
            },
            $coreHandler // 最终业务处理
        );

        // 执行并返回响应
        return $dispatcher($request);
    });
}
130 1 0
1个回答

也可以在 config/route.php 中扫描所有controller文件,找到所有控制器,每个控制器添加一条路由,改变控制器的路径。

  • renloong 2小时前

    大佬,经过你的指点改写成这样

    <?php
    
    /**
     * 自定义Admin入口
     * @authors 余心(RenLoong)
     * @home https://github.com/RenLoong/loong-admin
     */
    
    use Webman\Route;
    
    $admin_path = getenv('SERVER_ADMIN_PATH');
    if ($admin_path && $admin_path != 'admin') {
        Route::disableDefaultRoute('', 'admin');
        $controllersClass = glob(app_path('admin') . '/controller/*Controller.php');
        $len = count($controllersClass);
        $routes = [];
        for ($i = 0; $i < $len; $i++) {
            $value = $controllersClass[$i];
            $controllerName = str_replace('Controller', '', basename($value, '.php'));
            $classStr = 'app' . str_replace([app_path(), '.php', '/'], ['', '', '\\'], $value);
            $reflection = new \ReflectionClass('\\' . $classStr);
    
            // 忽略抽象类、接口
            if ($reflection->isAbstract() || $reflection->isInterface()) {
                continue;
            }
    
            $actions = $reflection->getMethods(ReflectionMethod::IS_PUBLIC);
            $construct = $reflection->getConstructor();
            $actionsLen = count($actions);
            for ($n = 0; $n < $actionsLen; $n++) {
                $class = $actions[$n]->class;
                $name = $actions[$n]->name;
                if ($class === $classStr && !str_starts_with($name, '__')) {
                    $routes["/{$controllerName}/{$name}"] = [$class, $name];
                }
            }
        }
        $controllersVersionClass = glob(app_path('admin') . '/controller/**/*Controller.php');
        if (!empty($controllersVersionClass)) {
            $len = count($controllersVersionClass);
            for ($i = 0; $i < $len; $i++) {
                $value = $controllersVersionClass[$i];
                $version=basename(dirname($value));
                $controllerName = str_replace('Controller', '', basename($value, '.php'));
                $classStr = 'app' . str_replace([app_path(), '.php', '/'], ['', '', '\\'], $value);
                $reflection = new \ReflectionClass('\\' . $classStr);
    
                // 忽略抽象类、接口
                if ($reflection->isAbstract() || $reflection->isInterface()) {
                    continue;
                }
    
                $actions = $reflection->getMethods(ReflectionMethod::IS_PUBLIC);
                $construct = $reflection->getConstructor();
                $actionsLen = count($actions);
                for ($n = 0; $n < $actionsLen; $n++) {
                    $class = $actions[$n]->class;
                    $name = $actions[$n]->name;
                    if ($class === $classStr && !str_starts_with($name, '__')) {
                        $routes["/{$version}/{$controllerName}/{$name}"] = [$class, $name];
                    }
                }
            }
        }
        Route::group('/' . $admin_path, function ()use($routes) {
            foreach ($routes as $key => $value) {
                Route::any($key,$value);
            }
        });
    }
    
  • renloong 1小时前

    反射这一步你觉得是否有必要?

🔝