服务端交互最佳实践

JUI使用前后端分离的架构模式,通过 API 的形式和任何技术栈的服务端应用一起工作。

api
├── axios.js #HTTP配置
├── index.js #API请求
└── request.js #请求方法

HTTP配置

JUI对axios做了基本的封装,配置了请求和响应拦截器。可以在axios.js文件中扩展更多axios的配置。

/**
 * axios 基础配置
 */
import axios from 'axios'
// import auth from "@/utils/auth"

axios.defaults.timeout = 5000
axios.defaults.baseURL = 'http://localhost:3000'
axios.defaults.withCredentials = true //发起请求携带cookie(CROS跨域需要服务端的支持)

// 将token传回服务器认证登录状态
// axios.defaults.headers.common['Authorization'] = auth.getToken()

//请求拦截
axios.interceptors.request.use((config) => {
  return config
}, (err) => {
  return Promise.reject(err)
})

//响应拦截//
axios.interceptors.response.use((res) => {
  return res
}, (err) => {
  //错误处理

  // 可处理用户登录基础信息,比如用户名,token,过期时间等

  // if (error.response.status === 403) {
  //   // 403
  // }
  // if (error.response.status === 500) {
  //   // 500
  // }
  // if (error.response.status === 502) {
  //   // 502
  // }
  // if (error.response.status === 404) {
  //   // 404
  // }

  return Promise.reject(err)
})

export default axios

请求方法

JUI对HTTP请求做了GETPOST基本封装,可以在request.js文件中扩展更多方法。

/**
 * 统一管理和封装HTTP请求
 */

import axios from './axios'
import qs from 'qs'

export default async (url, params = {}, method = 'POST', isUpload = false) => {
  method = method.toUpperCase()
  if (method === 'GET') {
    const res = await axios.get(url, {
      params
    })
    return res.data
  } else if (method === 'POST') {
    const normal = {
      transformRequest: [
        function (data) {
          return qs.stringify(data)
        }
      ]
    }
    const upload = {
      headers: {
        'Content-Type': 'multipart/form-data'
      }
    }
    const res = await axios.post(url, params, isUpload ? upload : normal)
    return res.data
  }

  // 请自行添加其它方法
}

集中管理API

JUI使用集中管理请求接口的方式,便于维护和调用。

/**
 * 集中管理请求接口
 */
import fetch from './request'

// 接口demo
export const apiDemo = params => fetch('/api/demo', params, 'POST')

调用API

import React, { PureComponent, Fragment } from 'react'
import { apiDemo } from '@/api' //调用接口

export default class User extends PureComponent {
  state = {
    apiData: {},
  }
  initData = async () => { //异步方式处理请求
    const res = await apiDemo({})
    this.setState({
      apiData: res.data
    })
  }
  componentDidMount() {
    this.initData()
  }
  render() {
    const { apiData } = this.state
    console.log(apiData) //获取API请求数据
    return (
      <Fragment>
        ...
      </Fragment>
    )
  }
}

Service层

在前后端分离的情况下,前后端常常是各成体系与团队,那么前后端的沟通也就成了项目开发中的主要矛盾之一。后端提供的数据格式常常并不符合前端界面渲染所需要的数据格式,并且考虑到业务需求的经常变更,后台接口也可能会发生频繁变动。此时就需要前端能够建立专门的接口服务层(service层)对上屏蔽这种变化,并且将数据处理与渲染逻辑分离,保证界面层的稳定性、及逻辑功能模块扩展性和可维护性。

建立Service.js文件处理数据

// service.js

export default async (data) => {
  // 处理 data
  return {...data}
}

使用Service

import React, { PureComponent, Fragment } from 'react'
import { apiDemo } from '@/api' //调用接口
import Service from './service' //引入Service服务层

export default class User extends PureComponent {
  state = {
    apiData: {},
  }
  initData = async () => { //异步方式处理请求
    const apiDatas = await apiDemo({})
    const res = await Service(apiDatas) //处理或封装接口数据
    this.setState({
      apiData: res.data
    })
  }
  componentDidMount() {
    this.initData()
  }
  render() {
    const { apiData } = this.state
    console.log(apiData) //获取经过service层处理后的数据
    return (
      <Fragment>
        ...
      </Fragment>
    )
  }
}

跨域访问

JUI使用前后端分离的架构模式,前端可以单独部署发布,当访问后端服务接口就会出现跨域。建议使用跨域资源共享(CORS)实现跨域。所有现代浏览器都支持该功能(IE8+:IE8/9需要使用XDomainRequest对象来支持CORS)),CORS也已经成为主流的跨域解决方案。

普通跨域请求只需服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要携带cookie请求,前后端都需要设置。若后端设置成功,前端浏览器控制台则不会出现跨域报错信息,反之,说明没设成功。

前端设置

由于JUI使用了axios,只需在api/axios.js文件中配置:

axios.defaults.withCredentials = true //发起请求携带cookie(CROS跨域需要服务端的支持)

服务端设置

  • Java后台
/*
 * 导入包:import javax.servlet.http.HttpServletResponse;
 * 接口参数中定义:HttpServletResponse response
 */

// 允许跨域访问的域名:若有端口需写全(协议+域名+端口),若没有端口末尾不用加'/'
response.setHeader("Access-Control-Allow-Origin", "http://www.domain.com"); 

// 允许前端带认证cookie:启用此项后,上面的域名不能为'*',必须指定具体的域名,否则浏览器会提示
response.setHeader("Access-Control-Allow-Credentials", "true"); 

// 提示OPTIONS预检时,后端需要设置的两个常用自定义头
response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With");
  • Nodejs后台
// 允许所有的请求形式 (跨域)
app.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", req.headers.origin) //需要显示设置来源
  res.header("Access-Control-Allow-Credentials", true) //带cookies
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
  next()
})

需注意的是:由于同源策略的限制,所读取的cookie为跨域请求接口所在域的cookie,而非当前页。