nuxt3+php跨域问题解决
简单介绍技术环境及遇到什么难题
本次开发用nuxt搭了个单页面看板,通过iframe嵌套进原系统(php+jQuery),但是在接口调用时出现了跨域的问题,因为原接口需要在request head带cookie,但是我使用fetch时默认不会带cookie,只好寻求解决方案。
相关报错
Access to fetch at 'https://xxx.com/xxx' from origin 'https://xxx.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
使用到什么技术栈
前端:nuxt3+ts+vue
后端:php
服务器:pm2
开发流程
前端fetch请求封装时在request head对象中加参数
credentials: "include",
mode: "cors",
Cookie:`tms_session=${Cookies.get("tms_session")}`
整体的request.ts文件:
import { ElMessage } from "element-plus";
import { UseFetchOptions } from "nuxt/app";
import Cookies from "js-cookie";
type Methods = "GET" | "POST" | "DELETE" | "PUT";
export interface IResultData<T> {
code: number;
data: T;
msg: string;
status: number;
}
class HttpRequest {
request<T = any>(url: string, method: Methods, data: any, options?: UseFetchOptions<T>) {
return new Promise((resolve, reject) => {
const runtimeConfig = useRuntimeConfig();
const NAME = runtimeConfig.public.VITE_PROJECT_CODE;
// 有部分接口是authorization校验
const authorization = `bearer ${Cookies.get("token")}`;
const newOptions: UseFetchOptions<T> = {
baseURL: BASE_URL,
method: method,
...options,
headers: {
Authorization: authorization,
Accept: "application/json",
'Content-Type': "application/json",
credentials: "include",
mode: "cors",
Cookie:`tms_session=${Cookies.get("tms_session")}`
},
server: true,
};
if (method === "GET" || method === "DELETE") {
newOptions.params = data;
}
if (method === "POST" || method === "PUT") {
newOptions.body = data;
}
useFetch(url, {
...newOptions,
onRequest({ request }) {
},
onRequestError({ request, options, error }) {
reject(request);
return Promise.reject(request);
},
onResponse({ response }) {
if (response.status === 200) {
const data = response._data;
resolve(data);
return Promise.resolve(data);
} else {
reject(response);
return Promise.reject(response);
}
},
onResponseError({ response }) {
reject(response);
return Promise.reject(response);
},
}).catch((error) => {
return Promise.reject(error);
});
}).catch((error) => {
return Promise.reject(error);
});
}
// 封装常用方法
get<T = any>(url: string, params?: any, options?: UseFetchOptions<T>) {
return this.request(url, "GET", params, options);
}
post<T = any>(url: string, data: any, options?: UseFetchOptions<T>) {
//console.log("data", data);
return this.request(url, "POST", data, options);
}
Put<T = any>(url: string, data: any, options?: UseFetchOptions<T>) {
return this.request(url, "PUT", data, options);
}
Delete<T = any>(url: string, params: any, options?: UseFetchOptions<T>) {
return this.request(url, "DELETE", params, options);
}
}
const request = new HttpRequest();
export default request;
这时前端的跨域配置结束,下面是服务器nginx配置,重点:add_header
相关的几个必须要配置!!!
location ~ [^/]\.php(/|$) {
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /home/wwwroot/multi_projects/public$fastcgi_script_name;
include fastcgi_params;
add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Credentials 'true';
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'Origin,Mode, Credentials,Content-Type, Cookie, X-CSRF-TOKEN, Accept, Authorization, X-XSRF-TOKEN';
add_header Access-Control-Expose-Headers 'Content-Length,Content-Range';
if ($request_method = 'OPTIONS') {
return 204;
}
}
接下来是后端接口测:
重点:$allowedOrigins
的配置非常重要,要写全前端环境的ip地址,*
放在最后。
<?php
namespace App\Http\Middleware;
use Closure;
class Cors
{
public function handle($request, Closure $next)
{
// 设置允许的源
// 重点:这里的IP必须要写全,测试环境/生产环境都要写,写在*前面
$allowedOrigins = ['IP1','IP2','*'];
$origin = $request->server('HTTP_ORIGIN');
if (!in_array($origin, $allowedOrigins)) {
header("Access-Control-Allow-Origin: *");
}
// 设置允许的请求方法
header("Access-Control-Allow-Headers: Origin,Mode, Credentials,Content-Type, Cookie, X-CSRF-TOKEN, Accept, Authorization, X-XSRF-TOKEN ");
header("Access-Control-Expose-Headers: Content-Length,Content-Range");
// 处理预检请求
if ($request->isMethod('OPTIONS')) {
return response()->json([], 204);
}
return $next($request);
}
}
得到结果
标绿的是以上配置得到的结果,3者缺一不可。
总结
前端通过设置请求头携带cookie和凭证,Nginx配置中添加了必要的CORS相关header,而后端PHP中间件则设置了允许的源和请求方法。这些配置缺一不可,最终实现了跨域请求的成功调用。
通过本次项目跨域问题还是学到了很全面的应对跨域调用接口的知识经验,分享给也遇到同样难题的小伙伴😁