【跨域攻略】Nuxt3与PHP的无缝对接:攻克CORS难题的实战技巧!

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中间件则设置了允许的源和请求方法。这些配置缺一不可,最终实现了跨域请求的成功调用。

通过本次项目跨域问题还是学到了很全面的应对跨域调用接口的知识经验,分享给也遇到同样难题的小伙伴😁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值