Next 是一个 react 框架,能够前后端一体化,用来做项目是一个不错的选择。本文记录一下几天前开发过程处理跨域问题的过程。【推荐方案二,方案二也更简单,不过需要一点点 nodejs 基础】

开始前请确认自己准备的跨域接口可以正常调用,如果实践过程中出现错误也请先检查接口是否正常,然后再检查本地配置的跨域是否有错。避免忙碌半天发现原本的接口就存在问题

方案一:项目目录下新建 server.js 文件,搭配 package.json scripts 启动选项,实现开发环境跨域

实现>>>

  • 创建 server.js 文件,目录为项目根目录,也就是 package.json 同级目录,文件代码如下
// server.js
const express = require('express')
const next = require('next')
const { createProxyMiddleware } = require('http-proxy-middleware')

//此处为跨域具体配置
const devProxy = {
    '/email': {
        target: 'https://api.postmarkapp.com/', 
        changeOrigin: true
    }
}

//以下内容保持不变,如果你不懂,请不要修改
const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({
    dev
})
const handle = app.getRequestHandler()

app.prepare()
    .then(() => {
        const server = express()
        if (dev && devProxy) {
            Object.keys(devProxy).forEach(function (context) {
                // http-proxy-middleware 新用法2
                server.use(createProxyMiddleware(context, devProxy[context]))
            })
        }

        server.all('*', (req, res) => {
            handle(req, res)
        })

        server.listen(port, err => {
            if (err) {
                throw err
            }
            console.log(`> Ready on http://localhost:${port}`)
        })
    })
    .catch(err => {
        console.log('An error occurred, unable to start the server')
        console.log(err)
    })
  • 示例内容中调用的是一个 postmark 发送邮件的接口,原接口地址 https://api.postmarkapp.com/emailget 请求。
  • 按照以上写法,实际请求方法如下
import axios from "axios";

export const waitlistEmailSend = (data: any) => {
  return new Promise((resolve, reject) => {
    try {
      axios({
        method: "post",
        url: "/email",
        data,
        headers: {
          Accept: "application/json",
          "X-Postmark-Server-Token": "***TOKEN***",
          "Content-Type": "application/json",
        },
      }).then((res: any) => {
        // console.log(res.code);
        if (res.status == 200) {
          resolve(res);
        } else {
          reject(res);
        }
      });
    } catch (error) {
      console.error(error);
      reject(error);
    }
  });
};
  • package.json 配置跨域启动 "dev:node-middleware": "node server.js",然后通过 npm run dev:node-middleware 启动项目,调用上面封装的请求。
{
...
"scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "dev:node-middleware": "node server.js"
  }
...
}
  • 可见,请求本地 /email 接口实际请求被中间件转发到了 https://api.postmarkapp.com/email ,跨域成功,其中f12页面网络请求显示为对 localhost 请求。

方案二:跨域接口封装在 Next 自带的后端 api ,前端请求封装后的 api 实现项目跨域

一般 Next 新建的项目下都会有 pages/api/hello.ts 这个文件,api 文件夹下也就是 Next 自带的 nodejs 后端,在此处请求跨域接口不会触发跨域报错,其实也很好理解,后端就相当于本地,你的所有请求都是直接发起的,并没用像前端那样有自己的域名,所以也就不存在跨域问题。

如果你懂 nodejs 的话可以跳过教程了,一切都和你 nodejs 后端的用法一样

实现>>>

  • pages/api/waitlistemail.ts 创建 Next 的后端 api ,示例代码如下
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import axios from "axios";
import type { NextApiRequest, NextApiResponse } from "next";

type Data = {
  code: number;
  data: any;
  errMsg: string;
};

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<Data>
) {
  console.log(req.body);
  if (req.body.To) {
    console.log("send");
    try {
      axios({
        method: "post",
        url: "https://api.postmarkapp.com/email",
        data: req.body,
        headers: {
          Accept: "application/json",
          "X-Postmark-Server-Token": "***TOKEN***",
          "Content-Type": "application/json",
        },
      }).then((r: any) => {
        console.log(r.status);
        if (r.status == 200) {
          res
            .status(200)
            .json({ code: 0, data: "", errMsg: "Email send success!" });
        } else {
          res
            .status(200)
            .json({ code: 1, data: "", errMsg: "Email send fail!" });
        }
      });
    } catch (error) {
      res
        .status(200)
        .json({ code: 1, data: "", errMsg: "Email send success!" });
    }
  } else {
    res.status(200).json({ code: 1, data: "", errMsg: "Email send fail!" });
  }
}
  • Next 前端调用接口,代码如下
import axios from "axios";

export const waitlistEmailSend = (data: any) => {
  return new Promise((resolve, reject) => {
    try {
      axios({
        method: "post",
        url: "/api/waitlistemail",
        data,
      }).then((res: any) => {
        // console.log(res);
        if (res.data.code == 0) {
          resolve(res);
        } else {
          reject(res);
        }
      });
    } catch (error) {
      reject(error);
    }
  });
};
  • 调用接口,请求成功,依然是对 localhost 的请求。

相关链接: Nextjs axios