springmvc 通过异常增强返回给客户端统一格式
在springmvc开发中,我们经常遇到这样的问题;逻辑正常执行时返回客户端指定格式的数据,比如json,但是遇NullPointerException空指针异常,NoSuchMethodException调用的方法不存在异常,返回给客户端的是服务端异常堆栈信息,导致客户端不能正常解析数据;这明显不是我们想要的。
幸好从spring3.2提供的新注解@ControllerAdvice,从名字上可以看出大体意思是控制器增强。原理是使用AOP对Controller控制器进行增强(前置增强、后置增强、环绕增强,AOP原理请自行查阅);那么我没可以自行对控制器的方法进行调用前(前置增强)和调用后(后置增强)的处理。
spring提供了@ExceptionHandler异常增强注解。程序如果在执行控制器方法前或执行时抛出异常,会被@ExceptionHandler注解了的方法处理。
配置applicationContext-mvc.xml:
- <!-- 使用Annotation自动注册Bean,扫描@Controller和@ControllerAdvice-->
- <context:component-scan base-package="com.drskj.apiservice" use-default-filters="false">
- <!-- base-package 如果多个,用“,”分隔 -->
- <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
- <!--控制器增强,使一个Contoller成为全局的异常处理类,类中用@ExceptionHandler方法注解的方法可以处理所有Controller发生的异常-->
- <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />
- </context:component-scan>
全局异常处理类:
- package com.drskj.apiservice.handler;
- import java.io.IOException;
- import org.springframework.beans.ConversionNotSupportedException;
- import org.springframework.beans.TypeMismatchException;
- import org.springframework.http.converter.HttpMessageNotReadableException;
- import org.springframework.http.converter.HttpMessageNotWritableException;
- import org.springframework.web.HttpMediaTypeNotAcceptableException;
- import org.springframework.web.HttpRequestMethodNotSupportedException;
- import org.springframework.web.bind.MissingServletRequestParameterException;
- import org.springframework.web.bind.annotation.ControllerAdvice;
- import org.springframework.web.bind.annotation.ExceptionHandler;
- import org.springframework.web.bind.annotation.ResponseBody;
- import com.drskj.apiservice.common.utils.ReturnFormat;
- /**
- * 异常增强,以JSON的形式返回给客服端
* 异常增强类型:NullPointerException,RunTimeException,ClassCastException,
NoSuchMethodException,IOException,IndexOutOfBoundsException
以及springmvc自定义异常等,如下:
SpringMVC自定义异常对应的status code
- Exception HTTP Status Code
- ConversionNotSupportedException 500 (Internal Server Error)
- HttpMessageNotWritableException 500 (Internal Server Error)
- HttpMediaTypeNotSupportedException 415 (Unsupported Media Type)
- HttpMediaTypeNotAcceptableException 406 (Not Acceptable)
- HttpRequestMethodNotSupportedException 405 (Method Not Allowed)
- NoSuchRequestHandlingMethodException 404 (Not Found)
- TypeMismatchException 400 (Bad Request)
- HttpMessageNotReadableException 400 (Bad Request)
- MissingServletRequestParameterException 400 (Bad Request)
- *
- */
- @ControllerAdvice
- public class RestExceptionHandler{
- //运行时异常
- @ExceptionHandler(RuntimeException.class)
- @ResponseBody
- public String runtimeExceptionHandler(RuntimeException runtimeException) {
- return ReturnFormat.retParam(1000, null);
- }
- //空指针异常
- @ExceptionHandler(NullPointerException.class)
- @ResponseBody
- public String nullPointerExceptionHandler(NullPointerException ex) {
- ex.printStackTrace();
- return ReturnFormat.retParam(1001, null);
- }
- //类型转换异常
- @ExceptionHandler(ClassCastException.class)
- @ResponseBody
- public String classCastExceptionHandler(ClassCastException ex) {
- ex.printStackTrace();
- return ReturnFormat.retParam(1002, null);
- }
- //IO异常
- @ExceptionHandler(IOException.class)
- @ResponseBody
- public String iOExceptionHandler(IOException ex) {
- ex.printStackTrace();
- return ReturnFormat.retParam(1003, null);
- }
- //未知方法异常
- @ExceptionHandler(NoSuchMethodException.class)
- @ResponseBody
- public String noSuchMethodExceptionHandler(NoSuchMethodException ex) {
- ex.printStackTrace();
- return ReturnFormat.retParam(1004, null);
- }
- //数组越界异常
- @ExceptionHandler(IndexOutOfBoundsException.class)
- @ResponseBody
- public String indexOutOfBoundsExceptionHandler(IndexOutOfBoundsException ex) {
- ex.printStackTrace();
- return ReturnFormat.retParam(1005, null);
- }
- //400错误
- @ExceptionHandler({HttpMessageNotReadableException.class})
- @ResponseBody
- public String requestNotReadable(HttpMessageNotReadableException ex){
- System.out.println("400..requestNotReadable");
- ex.printStackTrace();
- return ReturnFormat.retParam(400, null);
- }
- //400错误
- @ExceptionHandler({TypeMismatchException.class})
- @ResponseBody
- public String requestTypeMismatch(TypeMismatchException ex){
- System.out.println("400..TypeMismatchException");
- ex.printStackTrace();
- return ReturnFormat.retParam(400, null);
- }
- //400错误
- @ExceptionHandler({MissingServletRequestParameterException.class})
- @ResponseBody
- public String requestMissingServletRequest(MissingServletRequestParameterException ex){
- System.out.println("400..MissingServletRequest");
- ex.printStackTrace();
- return ReturnFormat.retParam(400, null);
- }
- //405错误
- @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
- @ResponseBody
- public String request405(){
- System.out.println("405...");
- return ReturnFormat.retParam(405, null);
- }
- //406错误
- @ExceptionHandler({HttpMediaTypeNotAcceptableException.class})
- @ResponseBody
- public String request406(){
- System.out.println("404...");
- return ReturnFormat.retParam(406, null);
- }
- //500错误
- @ExceptionHandler({ConversionNotSupportedException.class,HttpMessageNotWritableException.class})
- @ResponseBody
- public String server500(RuntimeException runtimeException){
- System.out.println("500...");
- return ReturnFormat.retParam(406, null);
- }
- }
以上包括了常见的服务端异常类型,@ResponseBody表示以json格式返回客户端数据。我们也可以自定义异常类(这里我把它叫做MyException)并且继承RunTimeException,并且在全局异常处理类新增一个方法来处理异常,使用@ExceptionHandler(MyException.class)注解在方法上实现自定义异常增强。
格式化response数据类ReturnFormat:
- package com.drskj.apiservice.common.utils;
- import java.lang.reflect.Field;
- import java.text.SimpleDateFormat;
- import java.util.ArrayList;
- import java.util.Date;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import com.alibaba.fastjson.JSON;import com.google.common.collect.Maps;
- //格式化返回客户端数据格式(json)
- public class ReturnFormat {
- private static Map<String,String>messageMap = Maps.newHashMap();
- //初始化状态码与文字说明
- static {
- messageMap.put("0", "");
- messageMap.put("400", "Bad Request!");
- messageMap.put("401", "NotAuthorization");
- messageMap.put("405", "Method Not Allowed");
- messageMap.put("406", "Not Acceptable");
- messageMap.put("500", "Internal Server Error");
- messageMap.put("1000", "[服务器]运行时异常");
- messageMap.put("1001", "[服务器]空值异常");
- messageMap.put("1002", "[服务器]数据类型转换异常");
- messageMap.put("1003", "[服务器]IO异常");
- messageMap.put("1004", "[服务器]未知方法异常");
- messageMap.put("1005", "[服务器]数组越界异常");
- messageMap.put("1006", "[服务器]网络异常");
- messageMap.put("1010", "用户未注册");
- messageMap.put("1011", "用户已注册");
- messageMap.put("1012", "用户名或密码错误");
- messageMap.put("1013", "用户帐号冻结");
- messageMap.put("1014", "用户信息编辑失败");
- messageMap.put("1015", "用户信息失效,请重新获取");
- messageMap.put("1020", "验证码发送失败");
- messageMap.put("1021", "验证码失效");
- messageMap.put("1022", "验证码错误");
- messageMap.put("1023", "验证码不可用");
- messageMap.put("1029", "短信平台异常");
- messageMap.put("1030", "周边无店铺");
- messageMap.put("1031", "店铺添加失败");
- messageMap.put("1032", "编辑店铺信息失败");
- messageMap.put("1033", "每个用户只能添加一个商铺");
- messageMap.put("1034", "店铺不存在");
- messageMap.put("1040", "无浏览商品");
- messageMap.put("1041", "添加失败,商品种类超出上限");
- messageMap.put("1042", "商品不存在");
- messageMap.put("1043", "商品删除失败");
- messageMap.put("2010", "缺少参数或值为空");
- messageMap.put("2029", "参数不合法");
- messageMap.put("2020", "无效的Token");
- messageMap.put("2021", "无操作权限");
- messageMap.put("2022", "RSA解密失败,密文数据已损坏");
- messageMap.put("2023", "请重新登录");
- }
- public static String retParam(int status,Object data) {
- OutputJson json = new OutputJson(status, messageMap.get(String.valueOf(status)), data);
- return json.toString();
- }
- }
返回格式实体类OutPutJson;这里用到了知名的fastjson将对象转json:
- package com.drskj.apiservice.common.utils;
- import java.io.Serializable;
- import com.alibaba.fastjson.JSON;
- public class OutputJson implements Serializable{
- /**
- * 返回客户端统一格式,包括状态码,提示信息,以及业务数据
- */
- private static final long serialVersionUID = 1L;
- //状态码
- private int status;
- //必要的提示信息
- private String message;
- //业务数据
- private Object data;
- public OutputJson(int status,String message,Object data){
- this.status = status;
- this.message = message;
- this.data = data;
- }
- public int getStatus() {
- return status;
- }
- public void setStatus(int status) {
- this.status = status;
- }
- public String getMessage() {
- return message;
- }
- public void setMessage(String message) {
- this.message = message;
- }
- public Object getData() {
- return data;
- }
- public void setData(Object data) {
- this.data = data;
- }
- public String toString(){
- if(null == this.data){
- this.setData(new Object());
- }
- return JSON.toJSONString(this);
- }
- }
实例:CodeController继承自BaseController,有一个sendMessage方法调用Service层发送短信验证码;
1.如果客户端请求方式为非POST,否则抛出HttpMediaTypeNotSupportedException异常;
2.如果username、forType或userType没传,则抛出MissingServletRequestParameterException异常;
3.如果springmvc接收无法进行类型转换的字段,会报TypeMismatchException异常;
.....
大部分的请求异常,springmvc已经为我们定义好了,为我们开发restful应用提高了测试效率,方便排查问题出在哪一环节。
- @RestController
@RequestMapping("/api/v1/code")
- public class CodeController extends BaseController {
- @Autowired
- private CodeService codeService;
- /**
- * 发送短信
- * @param username 用户名
- * @param type register/backpwd
- * @return
- * status: 0 2010 2029 1011 1010 1006 1020
- */
- @RequestMapping(value="/sendMessage",method=RequestMethod.POST,produces="application/json")
- public String sendMessage(@RequestParam(value="username",required=true)String username,
- @RequestParam(value="forType",required=true)String forType,
- @RequestParam(value="userType",required=true)String userType){
- if(null == username || "".equals(username)){
- return retContent(2010, null);
- }
- if(!"user".equals(userType) && !"merchant".equals(userType)){
- return retContent(2029, null);
- }
- if(!"register".equals(forType) && !"backpwd".equals(forType)){
- return retContent(2029, null);
- }
- return codeService.sendMessage(username, forType, userType);
- }
- }
- public abstract class BaseController {
- protected String retContent(int status,Object data) {
- return ReturnFormat.retParam(status, data);
- }
- }
最终,不管是正常的业务逻辑还是服务端异常,都会调用ReturnFormat.retParam(int status,Object data)方法返回格式统一的数据。
springmvc 通过异常增强返回给客户端统一格式的更多相关文章
- [转]SpringMVC使用@ResponseBody时返回json的日期格式、@DatetimeFormat使用注意
一.SpringMVC使用@ResponseBody时返回json的日期格式 前提了解: @ResponseBody 返回json字符串的核心类是org.springframework.http.co ...
- SpringMVC使用@ResponseBody时返回json的日期格式、@DatetimeFormat使用注意
一.SpringMVC使用@ResponseBody时返回json的日期格式 前提了解: @ResponseBody 返回json字符串的核心类是org.springframework.http.co ...
- springmvc全局异常后返回JSON异常数据
转自:http://www.cnblogs.com/exmyth/p/5601288.html (1)自定义或者使用spring自带的各种异常处理器 例如spring基于注解的异常解析器Annotat ...
- SpringMVC使用@ResponseBody时返回json的日期格式及可能产生的问题
http://blog.csdn.net/z69183787/article/details/40375831 遇到的问题: 1 条件: 1.1.表单里有两个时间参数,都是作为隐藏项随表单一起提交: ...
- WCF实现将服务器端的错误信息返回到客户端
转载:http://www.cnblogs.com/zeroone/articles/2299001.html http://www.it165.net/pro/html/201403/11033.h ...
- SpringMVC全局异常统一处理
SpringMVC全局异常统一处理以及处理顺序最近在使用SpringMVC做全局异常统一处理的时候遇到的问题,就是想把ajax请求和普通的网页请求分开返回json错误信息或者跳转到错误页. 在实际做的 ...
- springmvc的异常统一处理
在项目实际开发中,异常的统一处理是一个常态.假如不使用异常统一处理,我们往往需要在service层中捕获异常,并且根据不同的异常在result中的设置不同的code并给予相应的提示.这样可能会导致不同 ...
- 【swagger】2.swagger提供开发者文档--返回统一格式篇【spring mvc】【spring boot】
接着上一篇来说, 不管正常返回结果还是后台出现异常,应该返回给前台统一的响应格式. 所以这一篇就为了应对解决这个问题. ======================================== ...
- RestFul API 统一格式返回 + 全局异常处理
一.背景 在分布式.微服务盛行的今天,绝大部分项目都采用的微服务框架,前后端分离方式.前端和后端进行交互,前端按照约定请求URL路径,并传入相关参数,后端服务器接收请求,进行业务处理,返回数据给前端. ...
随机推荐
- 每日Scrum(6)
今天是小组正式冲刺的第六天,软件的各种结尾工作,还有一些模块就已经全部实现了: 遇到的问题主要是对于自己能力的担忧,以前总是想,如果自己努力,就会怎样成功,其实并不是那样,小小的距离就是很远的能力差距 ...
- informatica powercenter学习笔记(LookUp 使用)
LOOKUP TRANSFORMATION的使用点评: LOOKUP基本用法不熟的话请参考下附属信息. 用法感受: 1 LOOKUP的作用跟我们以前在EXCEL的函数功能类似,就是隔表取值.优点就是用 ...
- CSS之旅——第二站 如何更深入的理解各种选择器
上篇我们说了为什么要使用css,这篇我们就从选择器说起,大家都知道浏览器会把远端过来的html解析成dom模型,有了dom模型,html就变成 了xml格式,否则的话就是一堆“杂乱无章”的string ...
- SQL Server 分隔字符串函数实现
在SQL Server中有时候也会遇到字符串进行分隔的需求.平时工作中常常遇到这样的需求,例如:人员数据表和人员爱好数据表,一条人员记录可以多多人员爱好记录,而往往人员和人员爱好在界面展示层要一并提交 ...
- iOS Build Active Architecture Only 属性的理解(及 not found for architecture i386 的解决方案)
最近做项目过程遇到一个问题: 涉及到这个属性:Build Active Architecture Only Yes .No的区别: 设置为yes,是只编译当前的architecture版本,是为了编译 ...
- Swift内存管理、weak和unowned以及两者区别
Swift 是自动管理内存的,这也就是说,我们不再需要操心内存的申请和分配.当我们通过初始化创建一个对象时,Swift 会替我们管理和分配内存.而释放的原则遵循了自动引用计数 (ARC) 的规则:当一 ...
- 理解 QEMU/KVM 和 Ceph(2):QEMU 的 RBD 块驱动(block driver)
本系列文章会总结 QEMU/KVM 和 Ceph 之间的整合: (1)QEMU-KVM 和 Ceph RBD 的 缓存机制总结 (2)QEMU 的 RBD 块驱动(block driver) (3)存 ...
- .Net程序员之Python基础教程学习----判断条件与循环[Fourth Day]
今天学习Python的判断条件与循环操作. 一. 布尔变量: 在学习判断条件之前必须的了解bool变量,在Python中bool变量与C语言比较类似,与.net差别比较大,其中下面集中情况需要 ...
- ajax小结
1. http是一种无状态协议 2. http请求:四部分组成 ① http 请求的方法或动作,如:GET / POST ② 正在请求的URL,总得知道请求的地址是什么 ③ 请求头,包含一些客户端环境 ...
- Use getopt() & getopt_long() to Parse Arguments
Today I came across a function [getopt] by accident. It is very useful to parse command-line argumen ...