正经学徒,佛系记录,不搞事情
为什么将三个问题结合起来记录,因为这三个问题往往都是息息相关的,虽说看起来不那么有技术含量,但却是一个项目规范的重要起步,现在通过一个springboot项目来解释一下这三点
统一返回值
springMVC项目只要加上一个@ResponseBody注解就可以返回任意的数据类型,但也就是因为没有对这一层进行把控,导致不同的人写的代码返回值千奇百怪,有的返回集合、有的返回字符串、有的返回数字甚至是返回布尔。尤其是在没有代码审查的项目中,一旦有新人加入,那将会为以后维护的人埋下一个又一个的坑。
统一返回值的优点:
- 规范后台返回的数据格式
- 前端可以统一根据返回的结果触发相应的消息通知(尤其是在前后端分离的项目)
接下来创建一个普通的springboot项目,添加web依赖
这是我的自定义统一返回对象 ResultJson.Java
public class ResultJson {
/**
* @escription 成功返回true,失败返回false
*/
private boolean success;
/**
* @返回的附带信息,用于客户端提示给用户
*/
private String message;
/**
* @返回的数据
*/
private Map<String, Object> data;
public ResultJson(){}
public ResultJson(Map<String, Object> data, Boolean success, String message){
this.success=success;
this.message=message;
if(data != null){
this.data=data;
}else{
this.data=null;
}
}
/**
* 返回成功的请求
* @param data 成功的数据
* @param message 成功的提示
* @return
*/
public static ResultJson success(Map<String, Object> data,String message) {
return new ResultJson(data,true, message);
}
/**
* 返回成功的请求
* @param data 成功的数据
* @return
*/
public static ResultJson success(Map<String, Object> data) {
return new ResultJson(data,true, "请求成功!");
}
/**
* 返回成功的请求
* @param message 成功的提示
* @return
*/
public static ResultJson success(String message) {
return new ResultJson(null,true, message);
}
/**
* 返回失败的请求
* @param message 失败的提示
* @return
*/
public static ResultJson fail(String message) {
return new ResultJson(null,false, message);
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Map<String, Object> getData() {
return data;
}
public void setData(Map<String, Object> data) {
this.data = data;
}
}
- success:判断请求是否成功,值为true、false
- message:通知的消息,前端可直接输出到页面提示给用户
- data:返回值,之所以使用map,是为了适应不同的返回类型
- 各种静态方法:一些默认值得控制
使用统一返回对象,创建一个TestController.java
@RestController
public class TestController {
@RequestMapping("/test")
public ResultJson test (){
Map<String,Object> retMap = new HashMap<>();
retMap.put("data1","来自四方的数据1");
retMap.put("data2",new String[]{"来自四方的数据2","来自四方的数据3"});
return ResultJson.success(retMap);
}
}
@RestController 等价于 @Controller + @ResponseBody
全局异常处理
首先来试试看,如果没有使用全局异常处理返回值将会是怎么样的
修改TestController方法
@RequestMapping("/test")
public ResultJson test (){
int i=1/0;
Map<String,Object> retMap = new HashMap<>();
retMap.put("data1","来自四方的数据1");
retMap.put("data2",new String[]{"来自四方的数据2","来自四方的数据3"});
return ResultJson.success(retMap);
}
由于添加了 1/0 的方法,这里将会抛出异常,而异常又区分为,请求数据异常和访问页面异常。请求数据时,一般使用ajax,而其余请求我们则返回404页面。
请求数据异常:
访问页面异常:
返回的数据格式和页面都不是我们所需要的,我们也不希望用户看到这样的数据和页面,也正是因为返回的数据格式和页面不是我们所需要的,所以才要结合全局异常处理和统一返回值,springboot通过@ControllerAdvice+@ExceptionHandler来捕捉异常
添加 GlobalExceptionHandler.java
/**
* @description 全局异常处理
*/
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = Exception.class)
public Object errorHandler(HttpServletRequest request,
HttpServletResponse response, Exception e) throws Exception{
e.printStackTrace();
if(isAjax(request)){
try {
response.setContentType("application/json; charset=UTF-8");
response.getWriter().write(JSON.toJSONString(ResultJson.fail("操作失败,原因:“"+e.getMessage()+"”,请联系管理员处理")));
response.getWriter().close();
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}else{
//返回错误页面
ModelAndView mav = new ModelAndView();
mav.addObject("exception",e);
mav.addObject("url",request.getRequestURL());
mav.setViewName("404");
return mav;
}
}
/**
* @description 判断是否是ajax请求
* @return boolean
*/
public static boolean isAjax(HttpServletRequest httpRequest){
return (httpRequest.getHeader("X-Requested-With")!=null
&& "XMLHttpRequest"
.equals(httpRequest.getHeader("X-Requested-With").toString()));
}
}
这里通过isAjax方法判断当前的请求是请求数据异常还是访问页面异常,由于需要请求数据异常要返回我们自定义的对象,这里使用到了alibaba fastjson,访问页面异常使用到了页面(springboot推荐使用thymeleaf模板引擎)所以在pom.xml中引入
<!--fastjson数据解析器-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.33</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
配置文件application.properties添加:
# 定位模板的目录
spring.mvc.view.prefix=classpath:/templates/
# 给返回的页面添加后缀名
spring.mvc.view.suffix=.html
templates下添加一个404.html页面
在进行测试,访问页面异常:
请求数据异常,给postman添加request模拟ajax请求:
注:/ by zero即是分母为0报出的错误
自定义异常
最后就是这个自定义异常,有时候我们需要自己抛出异常,比如某某参数值为空,某某查询结果为空
添加BaseException.java
public class BaseException extends RuntimeException {
public BaseException() {
}
public BaseException(String message) {
super(message);
}
public BaseException(String message, Throwable cause) {
super(message, cause);
}
public BaseException(Throwable cause) {
super(cause);
}
public BaseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
继承运行时异常,修改GlobalExceptionHandler.java,追加baseErrorHandler方法来处理自定义异常
@ExceptionHandler(value = BaseException.class)
public Object baseErrorHandler(HttpServletRequest request,
HttpServletResponse response, Exception e) throws Exception{
e.printStackTrace();
//抛出自定义异常
try {
response.setContentType("application/json; charset=UTF-8");
response.getWriter().write(JSON.toJSONString(ResultJson.fail(e.getMessage())));
response.getWriter().close();
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
测试:主动throw异常
@RequestMapping("/test")
public ResultJson test (){
if(true){
throw new BaseException("用户名不能为空");
}
int i=1/0;
Map<String,Object> retMap = new HashMap<>();
retMap.put("data1","来自四方的数据1");
retMap.put("data2",new String[]{"来自四方的数据2","来自四方的数据3"});
return ResultJson.success(retMap);
}
结果:
项目地址: https://pan.baidu.com/s/1E_PbVzGGptv218AlmI40bw 提取码: m6em