diff --git a/Server/application/common/controller/Api.php b/Server/application/common/controller/Api.php index ba350669..5a554eff 100644 --- a/Server/application/common/controller/Api.php +++ b/Server/application/common/controller/Api.php @@ -63,17 +63,11 @@ class Api extends Controller /** * 跨域检测 + * @deprecated 已由全局中间件 AllowCrossDomain 处理,此方法保留用于兼容 */ protected function _checkCors() { - // 允许跨域 - header('Access-Control-Allow-Origin: *'); - header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'); - header('Access-Control-Allow-Headers: Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-Requested-With, X-Token, X-Api-Token'); - header('Access-Control-Max-Age: 1728000'); - header('Access-Control-Allow-Credentials: true'); - - // 对OPTIONS请求直接返回 + // 由全局中间件处理跨域,此处不再处理 if ($this->requestType === 'OPTIONS') { Response::create()->send(); exit; diff --git a/Server/application/common/controller/Auth.php b/Server/application/common/controller/Auth.php index 4b1099db..73f7d847 100644 --- a/Server/application/common/controller/Auth.php +++ b/Server/application/common/controller/Auth.php @@ -26,22 +26,13 @@ class Auth extends Controller /** * 初始化 - * 设置跨域相关响应头 */ public function initialize() { parent::initialize(); - // 允许跨域访问 - header('Access-Control-Allow-Origin: ' . $this->allowOrigin); - header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization'); - header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'); + // 由全局中间件处理跨域,此处不再处理 - // 预检请求直接返回200 - if (Request::method(true) == 'OPTIONS') { - exit(); - } - // 初始化认证服务 $this->authService = new AuthService(); } diff --git a/Server/application/common/middleware/AllowCrossDomain.php b/Server/application/common/middleware/AllowCrossDomain.php new file mode 100644 index 00000000..6046fe3f --- /dev/null +++ b/Server/application/common/middleware/AllowCrossDomain.php @@ -0,0 +1,59 @@ +header('origin'); + + // 当请求使用 credentials 模式时,不能使用通配符 + // 必须指定具体的域名或提取请求中的 Origin + $allowOrigin = '*'; + if ($origin) { + // 如果需要限制特定域名,可以在这里判断 + // 以下是允许的域名列表,如果请求来自这些域名之一,则允许跨域 + $allowDomains = [ /* */ ]; + + // 如果请求来源在允许列表中,直接使用该源 + if (in_array($origin, $allowDomains)) { + $allowOrigin = $origin; + } + } + + // 设置允许的请求头信息 + $allowHeaders = [ + 'Authorization', 'Content-Type', 'If-Match', 'If-Modified-Since', + 'If-None-Match', 'If-Unmodified-Since', 'X-Requested-With', + 'X-Token', 'X-Api-Token', 'Accept', 'Origin' + ]; + + $response = $next($request); + + // 添加跨域响应头 + $response->header([ + 'Access-Control-Allow-Origin' => $allowOrigin, + 'Access-Control-Allow-Headers' => implode(', ', $allowHeaders), + 'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS', + 'Access-Control-Allow-Credentials' => 'true', + 'Access-Control-Max-Age' => '86400', + ]); + + // 对于预检请求,直接返回成功响应 + if ($request->method(true) == 'OPTIONS') { + return response()->code(200); + } + + return $response; + } +} \ No newline at end of file diff --git a/Server/application/superadmin/middleware/AdminAuth.php b/Server/application/superadmin/middleware/AdminAuth.php index 8bc0d70c..dc61d930 100644 --- a/Server/application/superadmin/middleware/AdminAuth.php +++ b/Server/application/superadmin/middleware/AdminAuth.php @@ -1,6 +1,8 @@ method(true) == 'OPTIONS') { + return $next($request); + } + // 获取Cookie中的管理员信息 $adminId = cookie('admin_id'); $adminToken = cookie('admin_token'); @@ -28,7 +35,7 @@ class AdminAuth } // 获取管理员信息 - $admin = \app\common\model\Administrator::where([ + $admin = Administrator::where([ ['id', '=', $adminId], ['status', '=', 1] ])->find(); @@ -62,7 +69,8 @@ class AdminAuth /** * 创建登录令牌 - * @param \app\common\model\Administrator $admin + * + * @param Administrator $admin * @return string */ private function createToken($admin) diff --git a/Server/config/middleware.php b/Server/config/middleware.php index 31f6a822..132ab645 100644 --- a/Server/config/middleware.php +++ b/Server/config/middleware.php @@ -17,5 +17,17 @@ return [ 'default_namespace' => 'app\\common\\middleware\\', // 优先级设置,此数组中的中间件会按照数组中的顺序优先执行 - 'priority' => [], + 'priority' => [ + 'app\\common\\middleware\\AllowCrossDomain' + ], + + // 全局中间件 + 'alias' => [ + 'cors' => 'app\\common\\middleware\\AllowCrossDomain' + ], + + // 应用中间件 + 'app' => [ + 'cors' + ], ]; diff --git a/SuperAdmin/app/dashboard/projects/[id]/edit/page.tsx b/SuperAdmin/app/dashboard/projects/[id]/edit/page.tsx index 86b4066e..09d2b3a4 100644 --- a/SuperAdmin/app/dashboard/projects/[id]/edit/page.tsx +++ b/SuperAdmin/app/dashboard/projects/[id]/edit/page.tsx @@ -71,6 +71,22 @@ export default function EditProjectPage({ params }: { params: { id: string } }) const fetchProject = async () => { try { const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/company/detail/${id}`) + + // 检查响应状态 + if (!response.ok) { + toast.error(`获取失败: ${response.status} ${response.statusText}`); + setIsLoading(false); + return; + } + + // 检查内容类型是否为JSON + const contentType = response.headers.get("content-type"); + if (!contentType || !contentType.includes("application/json")) { + toast.error("服务器返回了非JSON格式的数据"); + setIsLoading(false); + return; + } + const data = await response.json() if (data.code === 200) { @@ -115,6 +131,21 @@ export default function EditProjectPage({ params }: { params: { id: string } }) ...(password && { password }) }), }) + + // 检查响应状态 + if (!response.ok) { + toast.error(`更新失败: ${response.status} ${response.statusText}`); + setIsSubmitting(false); + return; + } + + // 检查内容类型是否为JSON + const contentType = response.headers.get("content-type"); + if (!contentType || !contentType.includes("application/json")) { + toast.error("服务器返回了非JSON格式的数据"); + setIsSubmitting(false); + return; + } const data = await response.json() @@ -155,6 +186,19 @@ export default function EditProjectPage({ params }: { params: { id: string } }) accountId: project.s2_accountId }), }) + + // 检查响应状态 + if (!response.ok) { + toast.error(`请求失败: ${response.status} ${response.statusText}`); + return; + } + + // 检查内容类型是否为JSON + const contentType = response.headers.get("content-type"); + if (!contentType || !contentType.includes("application/json")) { + toast.error("服务器返回了非JSON格式的数据"); + return; + } const data = await response.json() @@ -213,6 +257,19 @@ export default function EditProjectPage({ params }: { params: { id: string } }) } }); + // 检查响应状态和内容类型 + if (!response.ok) { + console.error("轮询请求失败:", response.status, response.statusText); + return; + } + + // 检查内容类型是否为JSON + const contentType = response.headers.get("content-type"); + if (!contentType || !contentType.includes("application/json")) { + console.error("轮询请求返回的不是JSON格式:", contentType); + return; + } + const data = await response.json(); if (data.code === 200) { @@ -240,6 +297,7 @@ export default function EditProjectPage({ params }: { params: { id: string } }) } } catch (error) { console.error("轮询请求出错:", error); + // 不要因为单次错误停止轮询 } } @@ -247,6 +305,20 @@ export default function EditProjectPage({ params }: { params: { id: string } }) const refreshProjectData = async () => { try { const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/company/detail/${id}`) + + // 检查响应状态 + if (!response.ok) { + toast.error(`刷新失败: ${response.status} ${response.statusText}`); + return; + } + + // 检查内容类型是否为JSON + const contentType = response.headers.get("content-type"); + if (!contentType || !contentType.includes("application/json")) { + toast.error("服务器返回了非JSON格式的数据"); + return; + } + const data = await response.json() if (data.code === 200) { diff --git a/SuperAdmin/lib/api-utils.ts b/SuperAdmin/lib/api-utils.ts index 087707d0..4a7cba94 100644 --- a/SuperAdmin/lib/api-utils.ts +++ b/SuperAdmin/lib/api-utils.ts @@ -43,7 +43,7 @@ export async function apiRequest( const options: RequestInit = { method, headers, - credentials: 'include', // 包含跨域请求的Cookie + credentials: 'same-origin', // 改为same-origin,避免跨域请求发送Cookie }; // 添加请求体(针对POST、PUT请求)