在springmvc开发中,我们经常遇到这样的问题;逻辑正常执行时返回客户端指定格式的数据,比如json,但是遇NullPointerException空指针异常,NoSuchMethodException调用的方法不存在异常,返回给客户端的是服务端异常堆栈信息,导致客户端不能正常解析数据;这明显不是我们想要的。

  幸好从spring3.2提供的新注解@ControllerAdvice,从名字上可以看出大体意思是控制器增强。原理是使用AOP对Controller控制器进行增强(前置增强、后置增强、环绕增强,AOP原理请自行查阅);那么我没可以自行对控制器的方法进行调用前(前置增强)和调用后(后置增强)的处理。

  spring提供了@ExceptionHandler异常增强注解。程序如果在执行控制器方法前或执行时抛出异常,会被@ExceptionHandler注解了的方法处理。

  配置applicationContext-mvc.xml:

  1. <!-- 使用Annotation自动注册Bean,扫描@Controller和@ControllerAdvice-->
  2. <context:component-scan base-package="com.drskj.apiservice" use-default-filters="false">
  3. <!-- base-package 如果多个,用“,”分隔 -->
  4. <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
  5. <!--控制器增强,使一个Contoller成为全局的异常处理类,类中用@ExceptionHandler方法注解的方法可以处理所有Controller发生的异常-->
  6. <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />
  7. </context:component-scan>

  全局异常处理类:

  1. package com.drskj.apiservice.handler;
  2.  
  3. import java.io.IOException;
  4.  
  5. import org.springframework.beans.ConversionNotSupportedException;
  6. import org.springframework.beans.TypeMismatchException;
  7. import org.springframework.http.converter.HttpMessageNotReadableException;
  8. import org.springframework.http.converter.HttpMessageNotWritableException;
  9. import org.springframework.web.HttpMediaTypeNotAcceptableException;
  10. import org.springframework.web.HttpRequestMethodNotSupportedException;
  11. import org.springframework.web.bind.MissingServletRequestParameterException;
  12. import org.springframework.web.bind.annotation.ControllerAdvice;
  13. import org.springframework.web.bind.annotation.ExceptionHandler;
  14. import org.springframework.web.bind.annotation.ResponseBody;
  15.  
  16. import com.drskj.apiservice.common.utils.ReturnFormat;
  17. /**
  18. * 异常增强,以JSON的形式返回给客服端
    * 异常增强类型:NullPointerException,RunTimeException,ClassCastException,
             NoSuchMethodException,IOException,IndexOutOfBoundsException
             以及springmvc自定义异常等,如下:
    SpringMVC自定义异常对应的status code
  1. Exception HTTP Status Code
  2. ConversionNotSupportedException 500 (Internal Server Error)
  3. HttpMessageNotWritableException 500 (Internal Server Error)
  4. HttpMediaTypeNotSupportedException 415 (Unsupported Media Type)
  5. HttpMediaTypeNotAcceptableException 406 (Not Acceptable)
  6. HttpRequestMethodNotSupportedException 405 (Method Not Allowed)
  7. NoSuchRequestHandlingMethodException 404 (Not Found)
  8. TypeMismatchException 400 (Bad Request)
  9. HttpMessageNotReadableException 400 (Bad Request)
  10. MissingServletRequestParameterException 400 (Bad Request)
  11. *
  12. */
  13. @ControllerAdvice
  14. public class RestExceptionHandler{
  15. //运行时异常
  16. @ExceptionHandler(RuntimeException.class)
  17. @ResponseBody
  18. public String runtimeExceptionHandler(RuntimeException runtimeException) {
  19.  
  20. return ReturnFormat.retParam(1000, null);
  21. }
  22.  
  23. //空指针异常
  24. @ExceptionHandler(NullPointerException.class)
  25. @ResponseBody
  26. public String nullPointerExceptionHandler(NullPointerException ex) {
  27. ex.printStackTrace();
  28. return ReturnFormat.retParam(1001, null);
  29. }
  30. //类型转换异常
  31. @ExceptionHandler(ClassCastException.class)
  32. @ResponseBody
  33. public String classCastExceptionHandler(ClassCastException ex) {
  34. ex.printStackTrace();
  35. return ReturnFormat.retParam(1002, null);
  36. }
  37.  
  38. //IO异常
  39. @ExceptionHandler(IOException.class)
  40. @ResponseBody
  41. public String iOExceptionHandler(IOException ex) {
  42. ex.printStackTrace();
  43. return ReturnFormat.retParam(1003, null);
  44. }
  45. //未知方法异常
  46. @ExceptionHandler(NoSuchMethodException.class)
  47. @ResponseBody
  48. public String noSuchMethodExceptionHandler(NoSuchMethodException ex) {
  49. ex.printStackTrace();
  50. return ReturnFormat.retParam(1004, null);
  51. }
  52.  
  53. //数组越界异常
  54. @ExceptionHandler(IndexOutOfBoundsException.class)
  55. @ResponseBody
  56. public String indexOutOfBoundsExceptionHandler(IndexOutOfBoundsException ex) {
  57. ex.printStackTrace();
  58. return ReturnFormat.retParam(1005, null);
  59. }
  60. //400错误
  61. @ExceptionHandler({HttpMessageNotReadableException.class})
  62. @ResponseBody
  63. public String requestNotReadable(HttpMessageNotReadableException ex){
  64. System.out.println("400..requestNotReadable");
  65. ex.printStackTrace();
  66. return ReturnFormat.retParam(400, null);
  67. }
  68. //400错误
  69. @ExceptionHandler({TypeMismatchException.class})
  70. @ResponseBody
  71. public String requestTypeMismatch(TypeMismatchException ex){
  72. System.out.println("400..TypeMismatchException");
  73. ex.printStackTrace();
  74. return ReturnFormat.retParam(400, null);
  75. }
  76. //400错误
  77. @ExceptionHandler({MissingServletRequestParameterException.class})
  78. @ResponseBody
  79. public String requestMissingServletRequest(MissingServletRequestParameterException ex){
  80. System.out.println("400..MissingServletRequest");
  81. ex.printStackTrace();
  82. return ReturnFormat.retParam(400, null);
  83. }
  84. //405错误
  85. @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
  86. @ResponseBody
  87. public String request405(){
  88. System.out.println("405...");
  89. return ReturnFormat.retParam(405, null);
  90. }
  91. //406错误
  92. @ExceptionHandler({HttpMediaTypeNotAcceptableException.class})
  93. @ResponseBody
  94. public String request406(){
  95. System.out.println("404...");
  96. return ReturnFormat.retParam(406, null);
  97. }
  98. //500错误
  99. @ExceptionHandler({ConversionNotSupportedException.class,HttpMessageNotWritableException.class})
  100. @ResponseBody
  101. public String server500(RuntimeException runtimeException){
  102. System.out.println("500...");
  103. return ReturnFormat.retParam(406, null);
  104. }
  105. } 

  以上包括了常见的服务端异常类型,@ResponseBody表示以json格式返回客户端数据。我们也可以自定义异常类(这里我把它叫做MyException)并且继承RunTimeException,并且在全局异常处理类新增一个方法来处理异常,使用@ExceptionHandler(MyException.class)注解在方法上实现自定义异常增强。

  格式化response数据类ReturnFormat:

  1. package com.drskj.apiservice.common.utils;
  2.  
  3. import java.lang.reflect.Field;
  4. import java.text.SimpleDateFormat;
  5. import java.util.ArrayList;
  6. import java.util.Date;
  7. import java.util.HashMap;
  8. import java.util.List;
  9. import java.util.Map;
  10.  
  11. import com.alibaba.fastjson.JSON;import com.google.common.collect.Maps;
  12. //格式化返回客户端数据格式(json)
  13. public class ReturnFormat {
  14. private static Map<String,String>messageMap = Maps.newHashMap();
  15. //初始化状态码与文字说明
  16. static {
  17. messageMap.put("0", "");
  18.  
  19. messageMap.put("400", "Bad Request!");
  20. messageMap.put("401", "NotAuthorization");
  21. messageMap.put("405", "Method Not Allowed");
  22. messageMap.put("406", "Not Acceptable");
  23. messageMap.put("500", "Internal Server Error");
  24.  
  25. messageMap.put("1000", "[服务器]运行时异常");
  26. messageMap.put("1001", "[服务器]空值异常");
  27. messageMap.put("1002", "[服务器]数据类型转换异常");
  28. messageMap.put("1003", "[服务器]IO异常");
  29. messageMap.put("1004", "[服务器]未知方法异常");
  30. messageMap.put("1005", "[服务器]数组越界异常");
  31. messageMap.put("1006", "[服务器]网络异常");
  32.  
  33. messageMap.put("1010", "用户未注册");
  34. messageMap.put("1011", "用户已注册");
  35. messageMap.put("1012", "用户名或密码错误");
  36. messageMap.put("1013", "用户帐号冻结");
  37. messageMap.put("1014", "用户信息编辑失败");
  38. messageMap.put("1015", "用户信息失效,请重新获取");
  39.  
  40. messageMap.put("1020", "验证码发送失败");
  41. messageMap.put("1021", "验证码失效");
  42. messageMap.put("1022", "验证码错误");
  43. messageMap.put("1023", "验证码不可用");
  44. messageMap.put("1029", "短信平台异常");
  45.  
  46. messageMap.put("1030", "周边无店铺");
  47. messageMap.put("1031", "店铺添加失败");
  48. messageMap.put("1032", "编辑店铺信息失败");
  49. messageMap.put("1033", "每个用户只能添加一个商铺");
  50. messageMap.put("1034", "店铺不存在");
  51.  
  52. messageMap.put("1040", "无浏览商品");
  53. messageMap.put("1041", "添加失败,商品种类超出上限");
  54. messageMap.put("1042", "商品不存在");
  55. messageMap.put("1043", "商品删除失败");
  56.  
  57. messageMap.put("2010", "缺少参数或值为空");
  58.  
  59. messageMap.put("2029", "参数不合法");
  60. messageMap.put("2020", "无效的Token");
  61. messageMap.put("2021", "无操作权限");
  62. messageMap.put("2022", "RSA解密失败,密文数据已损坏");
  63. messageMap.put("2023", "请重新登录");
  64. }
  65. public static String retParam(int status,Object data) {
  66. OutputJson json = new OutputJson(status, messageMap.get(String.valueOf(status)), data);
  67. return json.toString();
  68. }
  69. }

返回格式实体类OutPutJson;这里用到了知名的fastjson将对象转json:

  1. package com.drskj.apiservice.common.utils;
  2.  
  3. import java.io.Serializable;
  4.  
  5. import com.alibaba.fastjson.JSON;
  6.  
  7. public class OutputJson implements Serializable{
  8.  
  9. /**
  10. * 返回客户端统一格式,包括状态码,提示信息,以及业务数据
  11. */
  12. private static final long serialVersionUID = 1L;
  13. //状态码
  14. private int status;
  15. //必要的提示信息
  16. private String message;
  17. //业务数据
  18. private Object data;
  19.  
  20. public OutputJson(int status,String message,Object data){
  21. this.status = status;
  22. this.message = message;
  23. this.data = data;
  24. }
  25. public int getStatus() {
  26. return status;
  27. }
  28. public void setStatus(int status) {
  29. this.status = status;
  30. }
  31. public String getMessage() {
  32. return message;
  33. }
  34. public void setMessage(String message) {
  35. this.message = message;
  36. }
  37. public Object getData() {
  38. return data;
  39. }
  40. public void setData(Object data) {
  41. this.data = data;
  42. }
  43. public String toString(){
  44. if(null == this.data){
  45. this.setData(new Object());
  46. }
  47. return JSON.toJSONString(this);
  48. }
  49. }

实例:CodeController继承自BaseController,有一个sendMessage方法调用Service层发送短信验证码;

  1.如果客户端请求方式为非POST,否则抛出HttpMediaTypeNotSupportedException异常;

  2.如果username、forType或userType没传,则抛出MissingServletRequestParameterException异常;

  3.如果springmvc接收无法进行类型转换的字段,会报TypeMismatchException异常;

  .....

  大部分的请求异常,springmvc已经为我们定义好了,为我们开发restful应用提高了测试效率,方便排查问题出在哪一环节。

  1. @RestController
    @RequestMapping("/api/v1/code")
  1. public class CodeController extends BaseController {
  2. @Autowired
  3. private CodeService codeService;
  4. /**
  5. * 发送短信
  6. * @param username 用户名
  7. * @param type register/backpwd
  8. * @return
  9. * status: 0 2010 2029 1011 1010 1006 1020
  10. */
  11. @RequestMapping(value="/sendMessage",method=RequestMethod.POST,produces="application/json")
  12. public String sendMessage(@RequestParam(value="username",required=true)String username,
  13. @RequestParam(value="forType",required=true)String forType,
  14. @RequestParam(value="userType",required=true)String userType){
  15. if(null == username || "".equals(username)){
  16. return retContent(2010, null);
  17. }
  18. if(!"user".equals(userType) && !"merchant".equals(userType)){
  19. return retContent(2029, null);
  20. }
  21. if(!"register".equals(forType) && !"backpwd".equals(forType)){
  22. return retContent(2029, null);
  23. }
  24. return codeService.sendMessage(username, forType, userType);
  25. }
  26. }
  1. public abstract class BaseController {
  2. protected String retContent(int status,Object data) {
  3. return ReturnFormat.retParam(status, data);
  4. }
  5. }

  最终,不管是正常的业务逻辑还是服务端异常,都会调用ReturnFormat.retParam(int status,Object data)方法返回格式统一的数据。

  

springmvc 通过异常增强返回给客户端统一格式的更多相关文章

  1. [转]SpringMVC使用@ResponseBody时返回json的日期格式、@DatetimeFormat使用注意

    一.SpringMVC使用@ResponseBody时返回json的日期格式 前提了解: @ResponseBody 返回json字符串的核心类是org.springframework.http.co ...

  2. SpringMVC使用@ResponseBody时返回json的日期格式、@DatetimeFormat使用注意

    一.SpringMVC使用@ResponseBody时返回json的日期格式 前提了解: @ResponseBody 返回json字符串的核心类是org.springframework.http.co ...

  3. springmvc全局异常后返回JSON异常数据

    转自:http://www.cnblogs.com/exmyth/p/5601288.html (1)自定义或者使用spring自带的各种异常处理器 例如spring基于注解的异常解析器Annotat ...

  4. SpringMVC使用@ResponseBody时返回json的日期格式及可能产生的问题

    http://blog.csdn.net/z69183787/article/details/40375831 遇到的问题: 1 条件: 1.1.表单里有两个时间参数,都是作为隐藏项随表单一起提交: ...

  5. WCF实现将服务器端的错误信息返回到客户端

    转载:http://www.cnblogs.com/zeroone/articles/2299001.html http://www.it165.net/pro/html/201403/11033.h ...

  6. SpringMVC全局异常统一处理

    SpringMVC全局异常统一处理以及处理顺序最近在使用SpringMVC做全局异常统一处理的时候遇到的问题,就是想把ajax请求和普通的网页请求分开返回json错误信息或者跳转到错误页. 在实际做的 ...

  7. springmvc的异常统一处理

    在项目实际开发中,异常的统一处理是一个常态.假如不使用异常统一处理,我们往往需要在service层中捕获异常,并且根据不同的异常在result中的设置不同的code并给予相应的提示.这样可能会导致不同 ...

  8. 【swagger】2.swagger提供开发者文档--返回统一格式篇【spring mvc】【spring boot】

    接着上一篇来说, 不管正常返回结果还是后台出现异常,应该返回给前台统一的响应格式. 所以这一篇就为了应对解决这个问题. ======================================== ...

  9. RestFul API 统一格式返回 + 全局异常处理

    一.背景 在分布式.微服务盛行的今天,绝大部分项目都采用的微服务框架,前后端分离方式.前端和后端进行交互,前端按照约定请求URL路径,并传入相关参数,后端服务器接收请求,进行业务处理,返回数据给前端. ...

随机推荐

  1. 每日Scrum(6)

    今天是小组正式冲刺的第六天,软件的各种结尾工作,还有一些模块就已经全部实现了: 遇到的问题主要是对于自己能力的担忧,以前总是想,如果自己努力,就会怎样成功,其实并不是那样,小小的距离就是很远的能力差距 ...

  2. informatica powercenter学习笔记(LookUp 使用)

    LOOKUP TRANSFORMATION的使用点评: LOOKUP基本用法不熟的话请参考下附属信息. 用法感受: 1 LOOKUP的作用跟我们以前在EXCEL的函数功能类似,就是隔表取值.优点就是用 ...

  3. CSS之旅——第二站 如何更深入的理解各种选择器

    上篇我们说了为什么要使用css,这篇我们就从选择器说起,大家都知道浏览器会把远端过来的html解析成dom模型,有了dom模型,html就变成 了xml格式,否则的话就是一堆“杂乱无章”的string ...

  4. SQL Server 分隔字符串函数实现

    在SQL Server中有时候也会遇到字符串进行分隔的需求.平时工作中常常遇到这样的需求,例如:人员数据表和人员爱好数据表,一条人员记录可以多多人员爱好记录,而往往人员和人员爱好在界面展示层要一并提交 ...

  5. iOS Build Active Architecture Only 属性的理解(及 not found for architecture i386 的解决方案)

    最近做项目过程遇到一个问题: 涉及到这个属性:Build Active Architecture Only Yes .No的区别: 设置为yes,是只编译当前的architecture版本,是为了编译 ...

  6. Swift内存管理、weak和unowned以及两者区别

    Swift 是自动管理内存的,这也就是说,我们不再需要操心内存的申请和分配.当我们通过初始化创建一个对象时,Swift 会替我们管理和分配内存.而释放的原则遵循了自动引用计数 (ARC) 的规则:当一 ...

  7. 理解 QEMU/KVM 和 Ceph(2):QEMU 的 RBD 块驱动(block driver)

    本系列文章会总结 QEMU/KVM 和 Ceph 之间的整合: (1)QEMU-KVM 和 Ceph RBD 的 缓存机制总结 (2)QEMU 的 RBD 块驱动(block driver) (3)存 ...

  8. .Net程序员之Python基础教程学习----判断条件与循环[Fourth Day]

        今天学习Python的判断条件与循环操作. 一. 布尔变量: 在学习判断条件之前必须的了解bool变量,在Python中bool变量与C语言比较类似,与.net差别比较大,其中下面集中情况需要 ...

  9. ajax小结

    1. http是一种无状态协议 2. http请求:四部分组成 ① http 请求的方法或动作,如:GET / POST ② 正在请求的URL,总得知道请求的地址是什么 ③ 请求头,包含一些客户端环境 ...

  10. 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 ...