一. 抛出异常
接下来我们要使用的相关数据基于本站《SpringBoot:对密码进行MD5加密》一文。在UserService中,我们的login和register两个方法的返回值都是Map,之所以使用map是因为我们需要将登录和注册的可能产生的错误结果等信息存储在Map中返回。
除了这种方式以外,我们还可以将这一类的操作统一用异常来处理,可以大大简化我们的程序架构,提高开发效率。
首先将UserService中的login和register的返回值修改为User和boolean
public interface UserService { User login(String name, String password); boolean register(String name,String password); // Map<String,Object> login(String name, String password); // Map<String,Object> register(String name,String password); }
在实现类中,将之前需要put进map中的错误信息,直接作为异常抛出:
@Service("userService") public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public User login(String name, String password) { User user = userMapper.queryByName(name); if (user == null) { throw new RuntimeException("用户名不存在"); } String password2 = user.getPassword(); String md5 = MD5Util.getMd5(name.concat(password)); if (!password2.equals(md5)) { throw new RuntimeException("密码错误"); } user.setPassword(null); return user; } @Override public boolean register(String name, String password) { if (userMapper.queryByName(name) != null) { throw new RuntimeException("用户名已经存在"); } User user = new User(); String md5 = MD5Util.getMd5(name.concat(password)); user.setName(name); user.setPassword(md5); Integer result = userMapper.doInsert(user); return result == 1; } }
此时的UserController不需要再做error相关的判断:
@Controller public class U serController { @Autowired private UserService userService; @PostMapping(value = "/login",produces = "application/json;charset=utf-8") @ResponseBody public R login(String name, String password) { User user = userService.login(name,password); return R.success(user); } @PostMapping(value = "/register",produces = "application/json;charset=utf-8") @ResponseBody public R register(String name, String password) { boolean register = userService.register(name, password); return register ? R.success() : R.error(); } }
假如我们的数据库中有一个用户:zhangjia,密码也是zhangjia,那么我们访问http://localhost:8888/login?name=zhangjia&password=zhang,密码不正确,则页面显示
{ "timestamp": "2019-07-30T14:48:20.814+0000", "status": 500, "error": "Internal Server Error", "message": "密码错误", "path": "/login" }
该json字符串是spring帮我们自动生成的默认的错误格式,但是一般情况下我们不会采用其默认的格式,通常会把返回Json字符串的所有方法的json格式统一,这里我们先暂时不去处理,具体的处理方法详情请看本文第三段。
如果输入一个正确的用户名和密码,则页面显示:
{ "data": { "userId": 2, "name": "zhangjia", "password": null, "status": 1, "createTime": "2019-07-03T02:47:15.000+0000" }, "success": true }
二. 统一处理异常的类
在Controller包下新建ExceptionHandler.java(放在util包下也可以),并添加@ControllerAdvice注解,添加该注解后,ExceptionHandler就可以作为异常处理的切面类,Controller包中的任何类产生异常,都将会交给ExceptionHandler来处理。
import io.zhangjia.springbootmd5.util.R; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler @ResponseBody public R handle(Exception e) { return R.error().put("msg",e.getMessage()); } }
我们访问http://localhost:8888/login?name=zhangjia&password=zhang,密码不正确,则页面显示
{ "msg": "密码错误", "success": false }
三. 统一格式
在上文中我们曾说过,如果一个项目中有多个返回json字符串的方法,通常会把这些方法返回的json字符串格式统一,比如统一成以下格式:
{ "code": "0", "msg": "错误信息", "data": { ... } }
我们可以新建一个工具类Result.java来生成上述格式的json字符串:
package io.zhangjia.springbootmd5.util; public class Result { private int code; //状态码 private String msg; //错误信息 private Object data; //如果需要用到data中的数据,则将Object修改为泛型T,同时Result也要变成泛型类Result<T> //因为使用泛型不需要向下转型,而Object需要 public Result() { } public Result(String msg) { this.msg = msg; } public Result(String msg, Object data) { this.msg = msg; this.data = data; } public Result(int code, String msg) { this.code = code; this.msg = msg; } public Result(int code, String msg, Object data) { this.code = code; this.msg = msg; this.data = data; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } }
接下来使用Result.java修改Controller即可:
@Controller public class UserController { @Autowired private UserService userService; @PostMapping(value = "/login",produces = "application/json;charset=utf-8") @ResponseBody public Result login(String name, String password,HttpSession session) { User user = userService.login(name,password); session.setAttribute("user",user); return new Result("登录成功"); } @PostMapping(value = "/register",produces = "application/json;charset=utf-8") @ResponseBody public Result register(String name, String password) { userService.register(name, password); return new Result("注册成功"); } }
最后使用Result处理ExceptionHandler.java
import io.zhangjia.springbootmd5.util.Result; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @ControllerAdvice public class GlobalExceptionHandler { /* * 自定义状态码 * 100001:密码错误 * 100002:用户名不存在 * 100003:用户名已存在 * */ @ExceptionHandler @ResponseBody public Result handle(Exception e) { return new Result(100001,e.getMessage()); } }
上述代码的code以100001为例,接下来我们想实现这样的一个功能:handle方法可以自动根据不同的异常,选择对应的状态码,该功能可以通过自定义异常类来完成:新建exception包,并添加自定义类,一般命名为:项目名+Exception,这里以TestException为例:
package io.zhangjia.springbootmd5.exception; public class TestException extends RuntimeException{ private int code; public int getCode() { return code; } public void setCode(int code) { this.code = code; } public TestException(int code, String msg) { //构造方法x //RuntimeException中有接受字符串的构造方法,通过super将msg传入该方法 super(msg); this.code = code; } }
接下来回到ExceptionHandler.java,将其修改如下:
import io.zhangjia.springbootmd5.exception.TestException; import io.zhangjia.springbootmd5.util.Result; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @ControllerAdvice public class GlobalExceptionHandler { /* * 自定义状态码对应的错误信息 * 100001:密码错误 * 100002:用户名不存在 * 100003:用户名已存在 * */ @ExceptionHandler @ResponseBody public Result handle(Exception e) { if(e instanceof TestException) { TestException testException = (TestException) e; int code = testException.getCode(); String message = testException.getMessage(); return new Result(code,message); } String message = e.getMessage(); if(StringUtils.isEmpty(message)){ message = "未定义错误"; } return new Result(-1,message); } }
最后修改UserServiceImpl ,将自定义的异常抛出即可。
import io.zhangjia.springbootmd5.entity.User; import io.zhangjia.springbootmd5.exception.TestException; import io.zhangjia.springbootmd5.mapper.UserMapper; import io.zhangjia.springbootmd5.service.UserService; import io.zhangjia.springbootmd5.util.MD5Util; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; @Service("userService") public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public User login(String name, String password) { User user = userMapper.queryByName(name); if (user == null) { // throw new RuntimeException("用户名不存在"); throw new TestException(100002,"用户名不存在"); } String password2 = user.getPassword(); String md5 = MD5Util.getMd5(name.concat(password)); if (!password2.equals(md5)) { // throw new RuntimeException("密码错误"); throw new TestException(100001,"密码错误"); } user.setPassword(null); return user; } @Override public boolean register(String name, String password) { if (userMapper.queryByName(name) != null) { // throw new RuntimeException("用户名已经存在"); throw new TestException(100003,"用户名已存在"); } User user = new User(); String md5 = MD5Util.getMd5(name.concat(password)); user.setName(name); user.setPassword(md5); Integer result = userMapper.doInsert(user); return result == 1; } }
我们访问http://localhost:8888/login?name=zhangjia&password=zhang,密码不正确,则页面显示
{ "code": 100001, "msg": "密码错误", "data": null }
接着访问http://localhost:8888/login?name=zhangjia11111&password=zhangjia,用户名不正确,则页面显示
{ "code": 100002, "msg": "用户名不存在", "data": null }
最后访问http://localhost:8888/login?name=zhangjia&password=zhangjia,用户名和密码都正确,则页面显示
{ "code": 0, "msg": "登录成功", "data": null }
其中code为0,说明没有产生任何异常,之所以为0是因为Result中的int code默认值就是0
四. 使用枚举类优化
上述代码还存在一个问题,我们可以设想一下,假如有一天我们需要修改我们的错误码code,那么需要挨个修改我们的Service,假如一个错误码被用到了10次,那么就需要修改十个地方,这非常的不方便,我们可以将错误码设计成一个枚举类来解决这个问题:
package io.zhangjia.springbootmd5.util; //枚举类,事先创建一系列的对象 public enum ExceptionCode { //枚举元素,多个之间用逗号分隔,最后用分号结束 INVALID_PASSWORD(100001, "密码错误"), USERNAME_NOT_EXIST(100002, "用户名不存在"), USERNAME_ALREADY_EXIST(100003, "用户名已存在"); private int code; private String msg; //枚举类的构造方法必须私有化,private修饰符可以不写 ExceptionCode(int code, String msg) { this.code = code; this.msg = msg; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
接下来修改ExceptionHandler.java
package io.zhangjia.springbootmd5.exception; import io.zhangjia.springbootmd5.util.ExceptionCode; public class TestException extends RuntimeException{ private int code; public int getCode() { return code; } /* public TestException(int code, String msg) { //构造方法x //RuntimeException中有接受字符串的构造方法,通过super将msg传入该方法 super(msg); this.code = code; }*/ public TestException(ExceptionCode exceptionCode) { //构造方法x //RuntimeException中有接受字符串的构造方法,通过super将msg传入该方法 super(exceptionCode.getMsg()); this.code = exceptionCode.getCode(); } }
最后在UserServiceImpl中使用枚举类即可:
@Service("userService") public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public User login(String name, String password) { User user = userMapper.queryByName(name); if (user == null) { // throw new RuntimeException("用户名不存在"); // throw new TestException(100002,"用户名不存在"); throw new TestException(ExceptionCode.USERNAME_NOT_EXIST); } String password2 = user.getPassword(); String md5 = MD5Util.getMd5(name.concat(password)); if (!password2.equals(md5)) { // throw new RuntimeException("密码错误"); // throw new TestException(100001,"密码错误"); throw new TestException(ExceptionCode.INVALID_PASSWORD); } user.setPassword(null); return user; } @Override public boolean register(String name, String password) { if (userMapper.queryByName(name) != null) { // throw new RuntimeException("用户名已经存在"); // throw new TestException(100003,"用户名已存在"); throw new TestException(ExceptionCode.USERNAME_ALREADY_EXIST); } User user = new User(); String md5 = MD5Util.getMd5(name.concat(password)); user.setName(name); user.setPassword(md5); Integer result = userMapper.doInsert(user); return result == 1; } }
请登录之后再进行评论