demo 请狠狠的戳这里 https://download.lllomh.com/cliect/#/product/G909746950517113
使用 create-next-app 创建的 Next.js 项目配置接口跨域代理转发需要用到 custom server 功能。
先安装好 express 和 http-proxy-middleware
yarn add express http-proxy-middleware
在项目根目录下新建 server.js 文件,写入以下代码
const express = require('express')
const next = require('next')
const createProxyMiddleware = require('http-proxy-middleware').createProxyMiddleware ;
const baseUrl='http://localhost:8000'
const devProxy = {
'/api': {
target: baseUrl, // 端口自己配置合适的
pathRewrite: {
'^/api': '/'
},
changeOrigin: true
}
}
const port = parseInt(process.env.PORT, 10) || 8087
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((context)=> {
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)
})
相应地修改 package.json
npm install cross-env ---save
"scripts": {
"dev": "set PORT=8087 && node server.js",
"build": "next build",
"start": "cross-env NODE_ENV=production set PORT=8087 && node server.js",
"lint": "next lint"
},
如下,所有接口以 /api
开头即可。
const { data } = await axios.post('/api/users/', options)
然后, 如果就这么写,在axios 是没事 ,但在fetch却是会报错Next.js - Error: only absolute urls are supported的,.
根目录新建文件 /serve/index.js
import getConfig from 'next/config'
import QS from 'qs'
import {Toast} from 'antd-mobile';
const {publicRuntimeConfig} = getConfig()
let baseUrl = 'http://localhost:8087/api/'
// if (typeof window == 'undefined') {
// baseUrl = publicRuntimeConfig.baseUrl
// }
export function $fetch(method, url, body) {
method = method.toUpperCase();
if (method === 'GET') {
// fetch的GET不允许有body,参数只能放在url中
body = undefined;
} else {
body = body && QS.stringify(body);
}
console.log(baseUrl + url)
return fetch(baseUrl + url, {
method,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Access-Control-Allow-credentials': 'true', // 每次携带cookie 必须加,不然不会携带
'Authorization': 'token' // 从localStorageStorage中获取access token
},
body,
},{credentials: 'include'}).then((res) => res.json()).then((data) => {
if (data.code == '00001') { //token失效
} else if (data.data ? data.data.token : "") { // 判断token是否存在,如果存在说明需要更新token
// app.$cookies.set('USERINFO',JSON.stringify(res.data.data))
} else if (data.code != '00000') {
Toast.info(data.msg, 1);//后端错误弹出
}
return data
})
}
/pages/api/index.js
let $api = "/api/"; //dev
if (process.env.NODE_ENV === "production") {
// 为线上环境修改配置...
//Modify the configuration for the online environment
$api="http://localhost:8080/" //线上
}
export const P_ZONLIST= 'api/index.php?route=address/zonelist';//地区列表
/pages/login.js
import React from 'react';
import Link from 'next/link'
import { Cookie, withCookie } from 'next-cookie'
import { List, InputItem, Toast } from 'antd-mobile';
import { createForm } from 'rc-form';
import Router, { withRouter } from 'next/router'
import ButtonMaterial from '@material-ui/core/Button';
import LeftNav from '../../components/LeftNav'
import {$fetch} from '../../serve'
import {P_ZONLIST,P_LOGIN} from '../api' // api/index.js
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
data:[],
datas:[],
pause:false,
end:false,
username:'',
password:'',
hasErrorUser:false,
ispaly:true,
type: 'money',
}
}
async componentDidMount() {
// let param={
// reqName:'Home'
// }
// let result = await $fetch('post',P_ZONLIST,{
// param
// })
// let res = await result.json(); //必须通过此方法才可返回数据
// // console.log(res.data[0])
// this.setState({datas:res.data})
}
onErrorClickUser = () => {
if (this.state.hasErrorUser) {
Toast.info('Please enter 11 digits');
}
}
handleClick = () => {
this.inputRef.focus();
}
onChangeUsername = (value) => {
if (value.replace(/\s/g, '').length < 11) {
this.setState({
hasErrorUser: true,
});
} else {
this.setState({
hasErrorUser: false,
});
}
this.setState({
username:value,
},()=>{
console.log(this.state.username,'usernameusernameusernameusername')
});
}
onChangePassword = (value) => {
if (value.replace(/\s/g, '').length < 11) {
this.setState({
hasError: true,
});
} else {
this.setState({
hasError: false,
});
}
this.setState({
password:value,
},()=>{
console.log(this.state.password,'valuevaluevalue')
});
}
handleClickSubmit= async ()=>{
const { cookie } = this.props
let param={
reqName:'P_LOGIN',
email:this.state.username,
password:this.state.password,
}
let result = await $fetch('post',P_LOGIN,param)
// let res = await result; //必须通过此方法才可返回数据
if(result.code='00000'){
cookie.set('USER', result.data)
}
console.log(result,'resultresult')
// this.setState({datas:res.data})
// Router.push({pathname:'/user'})
}
render() {
const { getFieldProps } = this.props.form;
const { cookie } = this.props
return
{console.log(this.props.data)}
Login {this.props.data[0]?this.props.data[0].zone_name:''}
'Customize to focus'}>
this.autoFocusInst = el}
>用户名
this.inputRef = el}
>密码
login
}
}
// Home.constructor=(props)=>{
// // super(props);
// this.state = {date: new Date()};
// }
// Home.getInitialProps = async (ctx) => {
// let datas={
// reqName:'Home'
// }
// let result = await $fetch('post','/api/launch/home',{
// datas
// })
//
// let res = await result.json(); //必须通过此方法才可返回数据
// const data = res.data
// return {
// stars: data,
// props: {
// data //props值传导render函数中
// }
// }
// }
//
// Home.componentDidMount=async ()=>{
// console.log(this.state.date)
// }
export async function getServerSideProps() {
let param={
reqName:'Home'
}
let result = await $fetch('post',P_ZONLIST,{
param
})
let res = await result; //必须通过此方法才可返回数据
// const data = res.data
console.log(res)
return {
props:{data:res.data}
}
}
Login = createForm()(Login);
export default withCookie(Login)
效果 ssr渲染 数据 携带 token 跟cookie