开发前后端交互规范与请求封装

一,背景说明

假设要实现系统登录的功能,用户输入用户名和密码,点击发送,后端接收到请求,拿到输入的数据,并对输入的数据进行验证,如果账号密码都正确返回正确标识,错误则返回失败标识。这里涉及到三个需要考虑的地方,数据对象的封装,前后端交互采用哪种方式,前端请求如何发送。

二,Axios请求封装

在这里插入图片描述
Axios是一个基于promise的网络请求库,作用于node.js和浏览器中,在Vue项目中使用前需先安装

npm install axios --save --在生产环境中安装
import axios from 'axios'
// import ElementUI from 'element-ui'
const caseRequest = axios.create({
  baseURL: 'http://localhost:7080/tick_tack',
  headers: { 'Content-Type': 'application/json' }
  // timeout: 5000
})
export default caseRequest

三,数据对象封装

1,前端封装

前端数据假设只有账号,密码两个字段,业务比较复杂可以再新加验证码这些。数据对象采用模块化的方式,方便后期管理和维护,在Vue项目的views文件夹下新建一个sign-in文件夹,用来实现用户的登录功能。也可以建在component下,只是component中一般存放组件等功能相对通用的模块。
在这里插入图片描述
login-user-system.class.js:存放字段信息,可引入Vue文件中使用

class UserInSystemClass {
  constructor (userAccount, userName) {
    this.userAccount = userAccount
    this.password = password
  }
}
export default UserInSystemClass

login-to-system.service.js:caseRequest是对axios请求的封装

import caseRequest from '@/request/request'

const loginToSystem = {
  queryPerson (data) {
    // 因为params是添加到url的请求字符串中的,用于get请求。
    // data是添加到请求体(body)中的, 用于post请求。
    return caseRequest.request({
      url: '/loginUser',
      method: 'POST',
      data
    })
  }
}

login-to-system.vue:

<template>
 <div>
  <el-form ref="form" :model="loginUserSystemForm" label-width="80px">
   <el-form-item label="用户名">
    <el-input v-model="loginUserSystemForm.userAccount"></el-input>
   </el-form-item>
   <el-form-item label="密码">
    <el-input v-model="loginUserSystemForm.password"></el-input>
   </el-form-item>
   <el-form-item>
    <el-button type="primary" @click="login()">登录</el-button>
   </el-form-item>
  </el-form>
 </div>
</template>

<script>
import loginToSystemService from '@/views/sign-in/login-to-system.service'
import LoginUserSystemClass from '@/views/sign-in/class/login-user-system.class'
const resource = loginToSystemService.resource
export default {
  name: 'LoginToSystem',
  data () {
   return {
      loginUserSystemForm: new LoginUserSystemClass()
  },
  methods:{
   async login () {
        const result = await resource.loginToSystem.queryPerson(this.loginUserSystemForm)
        if (result.code === '200') {
          this.$message.success('登录成功')
          this.$router.push('/front/home')//路由跳转
        } else {
          this.$message.error('Failure')
        }
    }
  }
</script>

2,后端封装

后端数据对象封装,简单点假设只有两个字段,账号和密码

public class LoginUser{
    private String userAccount;
    private String password;
}

Controller方法

    @PostMapping("/loginUser")
    //@RequestBody:将前端传过来的json转成后端的对象
    public Result loginSystem(@RequestBody LoginUser user) {
        if (StringUtils.isBlank(user.getUserAccount())||StringUtils.isBlank(user.getPassword())){
            return Result.error(Constants.CODE_400, "参数错误");
        }
        //校验用户输入的信息,此处可以自行发挥即可
        LoginUser loginUser = loginService.loginSystem(user);
        return Result.success(loginUser);
    }

Result接口统一返回包装类

@Data
@NoArgsConstructor //无参构造
@AllArgsConstructor //有参构造
public class Result {
    private String code;//状态码
    private String msg;//返回信息
    private Object data;//返回数据参数
    //无参请求成功
    public static Result success(){
        return new Result(Constants.CODE_200,"",null);
    }
    //有参数返回请求成功
    public static Result success(Object object){
        return new Result(Constants.CODE_200,"",object);
    }
    //返回错误信息
    public static Result error(String code,String msg){
        return new Result(code, msg, null);
    }
    //返回简单的错误信息
    public static Result error(){
        return new Result(Constants.CODE_500, "系统错误", null);
    }
}

接口中定义变量

  • 接口中定义的变量都是静态变量
  • 接口中定义的变量都默认加上public static final关键字,可以直接使用,无需初始化
  • 接口中的变量可起到在多个类中共享变量的效果
public interface Constants {
    String CODE_200 = "200";//成功
    String CODE_401 = "401";//权限不足
    String CODE_400 = "400";//参数错误
    String CODE_500 = "500";//系统错误
    String CODE_600 = "600";//其它业务异常
}

四,总结

在前端和后端定义相同类型的对象,且约定好状态码的意义,例如200代表成功,500是系统错误,401是权限不足。前端通过Axios发送请求,后端接收到请求后进行业务处理,返回相应的状态码,说明信息,以及响应的参数,前端拿到后端返回的数据后,根据不同的状态码进行对应的操作,例如赋值,或是报错信息的提示。
在开发过程中应该运用模块化的思想,高内聚,低耦合,这在后期的维护和扩展中是相当有益的。

五,Axios拦截器

对Axios进行封装,发送请求时调用封装后的类还有一个好处就是可以进行拦截操作,只需在封装处做拦截,其它调用的地方不用再做重复的操作。

import axios from 'axios'
import ElementUI from 'element-ui'
const caseRequest = axios.create({
  baseURL: 'http://localhost:7080/test_demo',
  headers: { 'Content-Type': 'application/json' }
  // timeout: 5000
})

// request拦截器
// 可以在请求发送前对请求做一些处理
// 比如统一加token,对请求参数统一加密
caseRequest.interceptors.request.use(config => {
  config.headers['Content-Type'] = 'application/json;charset=utf-8'
  //此处是将token信息放入了浏览器的localStorage中,通过getItem()方法取出
  let user = localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user')) : null
  if (user) {
    config.headers['token'] = user.token// 设置请求头
  }
  return config
}, error => {
  return Promise.reject(error)
})

// response响应拦截器
caseRequest.interceptors.response.use(
  response => {
    let res = response.data
    // 如果返回的是文件
    if (response.config.responseType === 'blob') {
      return res
    }
    // 兼容服务端返回的字符串数据
    if (typeof res === 'string') {
      // JSON.parse是从一个字符串中解析出json
      // JSON.stringify是从一个对象中解析出字符串
      res = res ? JSON.parse(res) : res
    }
    // 当权限验证不通过的时候给出提示
     if (res.code === '401') {
       ElementUI.Message({
         message: '权限校验未通过',
         type: 'error'
       })
     }
    return res
  }
)

export default caseRequest

七,Restful请求

1,Restful的由来

  • 在Restful之前的操作:
URL说明
http://localhost:7080/test_demo/user/query/1GET 根据用户id查询用户数据
http://localhost:7080/test_demo/savePOST 新增用户
http://localhost:7080/test_demo/user/update POST修改用户信息
http://localhost:7080/test_demo/user/delete/1GET/POST 删除用户信息
  • RESTful用法:
URL说明
http://localhost:7080/test_demo/user/1GET 根据用户id查询用户数据
http://localhost:7080/test_demo/userPOST 新增用户
http://localhost:7080/test_demo/userPUT 修改用户信息
http://localhost:7080/test_demo/userDELETE 删除用户信息

之前的操作其实也没什么问题,query表示查询,以Get的方式,save表示保存,以POST方式发送,但是有的大神觉得这样不太好,太繁琐了,比如Get请求,再加个id,那就是要做查询操作,没必要路径里面还要加个query,保存操作也是,POST方式就是要保存数据,没必要再加个save,在这种情况下,Restful规范诞生了。这里也说了,Restful是一种规范,建议大家能够遵守,如果不遵守那也没事,只是会产生很多多余的URL,不方便接口的维护和文档的维护。

2,Restful方法说明

http方法资源操作幂等性安全性
GETSELECT
POSTINSERT
PUTUPDATE
DELETEDELETE

幂等性:对同一接口的多次访问,得到的资源状态是相同的
安全性:对该REST接口访问,不会使服务器端资源的状态发生改变。

3,Restful请求实例

前端Axios请求

import caseRequest from '@/request/request'
//将userAccount拼接在URL后端,类似于http://localhost:7080/test_demo/user?userAccount=2
queryParam (userAccount) {
    return caseRequest.request({
      url: '/user/param?userAccount=' + userAccount,
      method: 'GET'
    })
  },
  //将userAccount拼接在URL上,类似于http://localhost:7080/test_demo/user/2
  query (userAccount) {
    return caseRequest.request({
      url: '/user/' + userAccount,
      method: 'GET'
    })
  },
save(data) {
    return caseRequest.request({
      url: '/user',
      method: 'POST',
      data
    })
  },
 update (data) {
    return caseRequest.request({
      url: '/user',
      method: 'PUT',
      data
    })
  },
delete (userId) {
    return caseRequest.request({
      url: '/user/' + userId,
      method: 'DELETE'
    })
  }

后端Controller代码

@RestController
@RequestMapping("/user")
public class UserController {
    @GetMapping("/param")
    public Result queryParam(@RequestParam String userAccount){
       User user= userService.query(userAccount);
       return Result.success(user);
    }
    @GetMapping("/{userAccount}")
    public Result query(@PathVariable String userAccount){
       User user= userService.query(userAccount);
       return Result.success(user);
    }
    @DeleteMapping("/{userId}")
    //@PathVariable:取路径中传过来的值
    public boolean delete(@PathVariable Integer userId) {
        return userService.delete(userId);
    }

    @PostMapping
    //通过@requestBody可以将请求体中的JSON字符串绑定到相应的bean上
    public boolean save(@RequestBody User user) {
        return userService.save(user);
    }

    @PutMapping
    public boolean update(@RequestBody User user) {
        return userService.save(user);
    }
}

说明:
1.@PathVariable 注解:参数拼接在url上,value的方式入参

http://localhost:7080/test_demo/user/2

2.@RequestParam 注解:参数拼接在url上(只能用Params方式,不能用Body),以key=value的方式入参

http://localhost:7080/test_demo/user?userAccount=2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值