注意:由于内容有点多,在看本文章前,一定要按照下面菜单中的教程,一步一步来
菜单
接着上一章所讲,本节主要实现一个简单的登录认证系统,涉及到的技术点包括API协议包装,全局异常统一处理等,这是真正的企业级核心技术,也是基础之一
1.创建rqto包和rpto
rqto即request transfer object,是API接口请求数据的包装
rpto即response transfer object,是API接口返回数据的包装
2.创建LoginRPTO类
@Data
public class LoginRPTO {
private long user_id;
private String nickname;
}
3.创建core包,新建ActionCode枚举类
public enum ActionCode {
ERROR(0,"请求失败"),
SUCCESS(1,"请求成功"),
EXCEPTION(10,"服务器异常"),
VALID(100,"参数校验异常");
public int code;
public String message;
ActionCode(int code, String message) {
this.code = code;
this.message = message;
}
}
在这个枚举类中定义常见的通用异常code
4.新建RPTO协议类
@Data
public class RPTO<T> {
private int code;
private String message;
private T data;
public RPTO(String message){
this(ActionCode.ERROR,message,null);
}
public RPTO(ActionCode action){
this(action,null);
}
public RPTO(ActionCode action,T data){
this(action.code,action.message,data);
}
public RPTO(ActionCode action,String message,T data){
this(action.code,message,data);
}
public RPTO(int code, String message) {
this(code,message,null);
}
public RPTO(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
}
协议体如下:
code | mssage | data |
接口状态码 | 接口处理消息 | 返回数据,泛型结构 |
最终希望看到在请求一个接口后,返回如下信息
{"code":1,"message":"请求成功","data":{"user_id":1,"nickname":"admin"}}
5.创建LoginRQTO
@Data
public class LoginRQTO {
@NotEmpty(message = "昵称不能为空")
@Length(max = 12,message = "昵称最大长度12")
private String nickname;
@NotEmpty(message = "密码不能为空")
@Length(max = 20,message = "密码最大长度20")
private String password;
}
6.修改GlobalExceptionHandler类
@RestControllerAdvice
public class GlobalExceptionHandler {
@ResponseBody
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.OK)
public RPTO exceptionHandler(Exception ex) throws IOException {
if (ex instanceof MethodArgumentNotValidException) {
BindingResult result = ((MethodArgumentNotValidException) ex).getBindingResult();
if (result.hasErrors()) {
StringBuffer sb = new StringBuffer();
printBindException(sb, result);
return new RPTO<>(ActionCode.VALID,sb.toString(),null);
}
}
return new RPTO<>(ActionCode.EXCEPTION);
}
private void printBindException(StringBuffer message, BindingResult result) {
List<ObjectError> allErrors = result.getAllErrors();
for (int i = 0; i < allErrors.size(); i++) {
message.append(allErrors.get(i).getDefaultMessage());
if (allErrors.size() > 1 && i < allErrors.size() - 1) {
message.append(",");
}
}
}
}
当发生异常时,我们希望接口返回
{"code":100,"message":"密码不能为空","data":null}
7.创建UserController类
@Controller
@RequestMapping("/user")
public class UserController {
@ResponseBody
@RequestMapping(value = "/login",method = RequestMethod.POST)
public RPTO login(@RequestBody @Valid LoginRQTO login) {
if(!login.getNickname().equals("admin") ||
!login.getPassword().equals("123")){
return new RVO<>("用户名密码错误");
}
LoginRPTO rvo = new LoginRPTO();
rvo.setUser_id(1);
rvo.setNickname(login.getNickname());
return new RPTO<>(ActionCode.SUCCESS,rvo);
}
}
8.新建login.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<h2>用户登录</h2><hr>
<input id="nickname" type="text" value="admin"/></br>
<input id="password" type="text" value="123"/></br>
<button onclick='login()'>登录</button></br>
<div id="tip"></div>
<script src="jquery.min.js"></script>
<script>
function login(){
var nickname = $("#nickname").val();
var password = $("#password").val();
$.ajax({
url: 'http://localhost:8080/user/login',
type: "post",
contentType: 'application/json;charset=utf-8',
dataType: 'json',
data: JSON.stringify({
nickname: nickname,
password: password
}),
success: function (res) {
if(res.code == 1){
$("#tip").html("用户ID = "+res.data.user_id+" 用户名 = "+res.data.nickname);
}else{
$("#tip").html(res.message);
}
},
error: function () {
$("#tip").html("连接失败");
}
});
}
</script>
</body>
</html>
此时目录结构如下
9.运行
打开浏览器,输入
http://localhost:8080/login.html
抓包分析,查看一下结构
删除密码,再次点击登录测试
抓包分析,查看一下结构
-----------------------------------------------------------------------------