浅谈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 通过异常增强返回给客户端统一格式就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Springmvc自定义异常处理器实现流程解析

    当dispatchServlet接收到controller抛出的异常时,会将异常交由 HandlerExceptionResolver 异常处理器处理!我们可以创建自定义异常处理器实现该接口来处理自定义异常 1) 自定义异常类 public class MyException extends Exception { // 异常信息 private String message; public MyException() { super(); } public MyException(String

  • 简单了解SpringMVC全局异常处理常用方法

    项目中,可能会抛出多个异常,我们不可以直接将异常的堆栈信息展示给用户,有两个原因: 用户体验不好 非常不安全 所以,针对异常,我们可以自定义异常处理,SpringMVC 中,针对全局异常也提供了相应的解决方案,主要是通过 @ControllerAdvice 和@ExceptionHandler 两个注解来处理的. 以上传大小超出限制为例,自定义异常,只需要提供一个异常处理类即可: @ControllerAdvice//表示这是一个增强版的 Controller,主要用来做全局数据处理 publi

  • SpringMVC异常处理器编写及配置

    一.编写自定义异常类(作为提示信息) @Data public class SysException extends Exception { private String message; public SysException(String message) { this.message = message; } } 二.编写异常处理器 public class SysExceptionResolve implements HandlerExceptionResolver { @Overrid

  • Springmvc异常映射2种实现方法

    请求出现 想要跳转到错误页面 就需要对springmvc进行配置 方法1:基于xml的配置 springmvc.xml配置类 <!--配置基于xml的异常映射--> <bean id="simpleMappingExceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <!--配置异常和对应页面的映

  • SpringMvc @Valid如何抛出拦截异常

    SpringMvc中,校验参数可以使用 @Valid 注解,同时在相应的对象里使用 @NotBlank( message = "昵称不能为空") @NotNull( message = "ID不能为空") @Pattern( message = "不能包括空格" , regexp = "\\S+" ) 等等. 这个校验会把所有的参数都校验一遍,所以它的异常里会好些列表,直接使用e.getMessage(),会输出很多累赘的东西

  • SpringMVC统一异常处理三种方法详解

    这篇文章主要介绍了SpringMVC-统一异常处理三种方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在 Spring MVC 应用的开发中,不管是对底层数据库操作,还是业务层或控制层操作,都会不可避免地遇到各种可预知的.不可预知的异常需要处理. 如果每个过程都单独处理异常,那么系统的代码耦合度高,工作量大且不好统一,以后维护的工作量也很大. 如果能将所有类型的异常处理从各层中解耦出来,这样既保证了相关处理过程的功能单一,又实现了异常信

  • 浅谈springmvc 通过异常增强返回给客户端统一格式

    在springmvc开发中,我们经常遇到这样的问题:逻辑正常执行时返回客户端指定格式的数据,比如json,但是遇NullPointerException空指针异常,NoSuchMethodException调用的方法不存在异常,返回给客户端的是服务端异常堆栈信息,导致客户端不能正常解析数据:这明显不是我们想要的. 幸好从spring3.2提供的新注解@ControllerAdvice,从名字上可以看出大体意思是控制器增强.原理是使用AOP对Controller控制器进行增强(前置增强.后置增强.

  • 浅谈SpringMVC请求映射handler源码解读

    请求映射源码 首先看一张请求完整流转图(这里感谢博客园上这位大神的图,博客地址我忘记了): 前台发送给后台的访问请求是如何找到对应的控制器映射并执行后续的后台操作呢,其核心为DispatcherServlet.java与HandlerMapper.在spring boot初始化的时候,将会加载所有的请求与对应的处理器映射为HandlerMapper组件.我们可以在springMVC的自动配置类中找到对应的Bean. @Bean @Primary @Override public RequestM

  • 浅谈CI脚本异常退出问题定位

    背景 在CI脚本中,使用类似如下脚本进行项目编译的计时,但在执行过程中,有时会出现CI脚本(命名为ci.sh)未完全执行的情况: #!/bin/bash -e sleep_time=$1 start_time=`date "+%s"` # do sth, this sleep would simulate project compilation sleep $sleep_time end_time=`date "+%s"` process_time=`expr \(

  • 浅谈SpringMVC的执行流程

    #简易版 1.客户发送请求经过 DisPatcherServlet 核心过滤器 2.DisPatcherServlet 核心控制器在去找一个或多个HandlerMappering 找到需要处理的Controller 3.DisPatcherServlet 通过HandlerAdapter将请求转发给 Controller 4.Controller 调用业务处理后返回结果给 ModelAndView 5.DisPatcherServlet 找到一个或者多个 ViewResolver 视图解析器 找

  • 浅谈SpringMVC中post checkbox 多选框value的值(隐藏域方式)

    我这里往后端传递checkbox 多选框value的值是通过字符串方式传递,先调用js对选定checkbox遍历获取选的的boxvalue,然后写进隐藏域,最后作文对象的属性提交.见代码:` 前端: <form:form commandName="user" method="post"> <c:forEach items="${deploys}" var="deploy" varStatus="de

  • 浅谈Springmvc中的页面跳转问题

    SpringMvc跳转问题 SpringMvc的Controller每次处理完数据后都会返回一个逻辑视图(view)和模型(model) 所以我们会看到原生的Controller是返回一个ModelAndView(内部包含了view和model). 正常情况下(除非被@ModelAttribute注解了的方法),否则最终都会返回ModelAndView. 当然有时候一个功能处理方法不一定要返回一个逻辑视图,也可以重定向到另一个功能方法 服务器内部转发到一个逻辑视图或者另一个功能方法. --- S

  • 浅谈SpringMVC对RESTfull的支持

    本文研究的主要是SpringMVC对RESTfull的支持的相关内容,具体如下. RESTful架构,就是目前流行的一种互联网软件架构.它结构清晰.符合标准.易于理解.扩展方便,所以正得到越来越多网站的采用.RESTful架构对url进行规范,写RESTful格式的url是什么样子的呢?我们一般请求的url是类似这样子的: http://...../xxx.action?id=001&type=aaa 而REST的url风格是什么样子呢?一般它类似于: http://..../xxx/001 所

  • 浅谈angular2的http请求返回结果的subcribe注意事项

    实例如下: this.monitorSer.getVehicleLonAndLat(vehicleIds) .subscribe( data => { //将data下的data字符串转化为vehdata数组 this.vehData=JSON.parse(data.data); //功能实现 this.loadOverLay(); }, error => this.errorMessage = <any>error );//错误处理 subscribe()异步处理,如果需要用返回

  • 浅谈SpringMVC中的session用法及细节记录

    前言 初学SpringMVC,最近在给公司做的系统做登录方面,需要用到session. 在网上找了不少资料,大致提了2点session保存方式: 1.javaWeb工程通用的HttpSession 2.SpringMVC特有的@SessionAttributes 我个人比较关注@SessionAttributes的用法,毕竟现在是在用SpringMVC嘛.但是我看网上那些文章,基本都是只说明了基础用法,详细的使用和细节却基本没有,我想这是不够的,所以我自己做了一些测试,然后整理了下代码做了个de

  • 浅谈SpringMVC jsp前台获取参数的方式 EL表达式

    JAVA: request.setAttribute("msg", "1234"); session.setAttribute("msg2", "1234"); JSP: ${requestScope.msg} ${sessionScope.msg2} JAVA: ModelAndView ModelMap Model里添加的参数 JSP: 直接用${参数名} JAVA: 前台表单里的信息,或者是直接在url后面以?name=

随机推荐