假设要实现系统登录的功能,用户输入用户名和密码,点击发送,后端接收到请求,拿到输入的数据,并对输入的数据进行验证,如果账号密码都正确返回正确标识,错误则返回失败标识。这里涉及到三个需要考虑的地方,数据对象的封装,前后端交互采用哪种方式,前端请求如何发送。
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
前端数据假设只有账号,密码两个字段,业务比较复杂可以再新加验证码这些。数据对象采用模块化的方式,方便后期管理和维护,在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>
后端数据对象封装,简单点假设只有两个字段,账号和密码
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 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进行封装,发送请求时调用封装后的类还有一个好处就是可以进行拦截操作,只需在封装处做拦截,其它调用的地方不用再做重复的操作。
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
URL | 说明 |
---|---|
http://localhost:7080/test_demo/user/query/1 | GET 根据用户id查询用户数据 |
http://localhost:7080/test_demo/save | POST 新增用户 |
http://localhost:7080/test_demo/user/update POST | 修改用户信息 |
http://localhost:7080/test_demo/user/delete/1 | GET/POST 删除用户信息 |
URL | 说明 |
---|---|
http://localhost:7080/test_demo/user/1 | GET 根据用户id查询用户数据 |
http://localhost:7080/test_demo/user | POST 新增用户 |
http://localhost:7080/test_demo/user | PUT 修改用户信息 |
http://localhost:7080/test_demo/user | DELETE 删除用户信息 |
之前的操作其实也没什么问题,query表示查询,以Get的方式,save表示保存,以POST方式发送,但是有的大神觉得这样不太好,太繁琐了,比如Get请求,再加个id,那就是要做查询操作,没必要路径里面还要加个query,保存操作也是,POST方式就是要保存数据,没必要再加个save,在这种情况下,Restful规范诞生了。这里也说了,Restful是一种规范,建议大家能够遵守,如果不遵守那也没事,只是会产生很多多余的URL,不方便接口的维护和文档的维护。
http方法 | 资源操作 | 幂等性 | 安全性 |
---|---|---|---|
GET | SELECT | 是 | 是 |
POST | INSERT | 否 | 否 |
PUT | UPDATE | 是 | 否 |
DELETE | DELETE | 是 | 否 |
幂等性:对同一接口的多次访问,得到的资源状态是相同的
安全性:对该REST接口访问,不会使服务器端资源的状态发生改变。
前端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