springmvc 經過異常加強返回給客戶端統一格式

  在springmvc開發中,咱們常常遇到這樣的問題;邏輯正常執行時返回客戶端指定格式的數據,好比json,可是遇NullPointerException空指針異常,NoSuchMethodException調用的方法不存在異常,返回給客戶端的是服務端異常堆棧信息,致使客戶端不能正常解析數據;這明顯不是咱們想要的。java

  幸虧從spring3.2提供的新註解@ControllerAdvice,從名字上能夠看出大致意思是控制器加強。原理是使用AOP對Controller控制器進行加強(前置加強、後置加強、環繞加強,AOP原理請自行查閱);那麼我沒能夠自行對控制器的方法進行調用前(前置加強)和調用後(後置加強)的處理。web

  spring提供了@ExceptionHandler異常加強註解。程序若是在執行控制器方法前或執行時拋出異常,會被@ExceptionHandler註解了的方法處理。
spring

  配置applicationContext-mvc.xml:express

複製代碼
<!-- 使用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>
複製代碼

  全局異常處理類:json

複製代碼
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)註解在方法上實現自定義異常加強。api

  格式化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層發送短信驗證碼;restful

  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)方法返回格式統一的數據。

相關文章
相關標籤/搜索