nextjs代理转发fetch请求封装

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的,.

二.fecth封装带拦截携带cookie 跟 token

1,定义

根目录新建文件 /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
  })

}


2,调用

1.1 api定义 

/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';//地区列表

1.2 执行

/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})

    }

    one rrorClickUser = () => {
        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 <div className="container">
            <LeftNav/>
            {console.log(this.props.data)}
            Login {this.props.data[0]?this.props.data[0].zone_name:''}
            <List renderHeader={() => 'Customize to focus'}>
                <InputItem
                    {...getFieldProps('autofocus')}
                    clear
                    error={this.state.hasErrorUser}
                    one rrorClick={this.onErrorClickUser}
                    onChange={this.onChangeUsername}
                    value={this.state.username}
                    placeholder="auto focus"
                    ref={el => this.autoFocusInst = el}
                >用户名</InputItem>
                <InputItem
                    {...getFieldProps('focus')}
                    clear
                    type={`password`}
                    onChange={this.onChangePassword}
                    value={this.state.password}
                    placeholder="click the button below to focus"
                    ref={el => this.inputRef = el}
                >密码</InputItem>
                <List.Item>
                    <div
                        style={{ width: '100%', color: '#108ee9', textAlign: 'center' }}
                        onClick={this.handleClickSubmit}
                    >
                       login
                    </div>
                </List.Item>
            </List>
        </div>
    }
}


// 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

nextjs代理转发fetch请求封装

上一篇:CSCI3120


下一篇:leetcode787+s到目的地最多停K次最小花费,Dp