您的位置:首页 > 其它

Jersey Rest 异常统一处理机制

2016-04-11 11:05 495 查看
前言:

异常分为运行时异常和非运行时异常,所谓的运行时异常是指那些不需要异常捕获的异常,总是交由虚拟机接管,如:ArrayIndexOutOfBoundsException,我们在写程序时,并没有使用try..catch来捕获它。

以前,我们进行项目开发时,习惯性的喜欢使用大量的try...catch...finally方法来进行异常处理,并且,只是将异常信息保存到log日志中即可,并没有将一些异常信息以可读性的方式返回给前端用户。而在一些比较大的项目中,进行异常统一处理是架构师或项目经理必须考虑的问题之一。

Jersey 提供了统一异常处理机制,使得在发生运行时异常时,自动跳转到相应的异常处理资源中,并将处理结果返回给前端用户。对于程序开发人员而言,不需要编写太多的try...catch块,只需要使用throw关键字将自定义的异常抛出即可。

一、Jersey异常处理统一机制

1、演示项目结构及技术

(1)技术:Spring4.1.4+Jersey2.21+JPA+PostMan,使用Spring Data JPA技术,其中PostMan是测试工具,可以在chrome浏览器中安装该插件,模拟前端向服务端发送请求。

(2)演示案例构建采用Maven

(3)案例结构如下:



(4)案例异常处理结构图



2、业务场景说明

本项目中,只是以分页查询中的page页码不能为负数为例,来演示Jersey的统一异常处理机制。当page页码为负数时,抛出UserException运行时异常,交由Jersey统一异常处理资源类ExceptionMappingResource处理,该类解析抛出的异常信息,解析ExceptionKeys定义的常量key,读取配置文件messages_zh_CN.properties定义的中文异常Unicode码,并能够实现参数配置。

3、代码片段说明

A、messages_zh_CN.properties异常配置内容

11110=\u67E5\u8BE2\u8BF7\u6C42\u8D77\u59CB\u9875 {0} \u662F\u8D1F\u6570
其中,{0},表示需要传递一个参数值来为其赋值,“11110”为key,等于号后面的unicode码为返回给前端用户显示的中文异常提示信息

B、ExceptionKeys异常key常量定义内容

package com.spring.jersy.jpa.hibernate.constants;

public class ExceptionKeys {

// 所有的异常key字符串开头都加了E标识,在后面的解析过程中会截取掉E字符,取E字符后面的编号
// 如下:“E11110”,变成“11110”,才能与messages_zh_CN.properties中的key一一对应
public final static String page_number_greater_zero = "E11110";
}

C、MessageUtil异常信息解析工具类

package com.spring.jersy.jpa.hibernate.util;

import java.text.MessageFormat;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;

import com.spring.jersy.jpa.hibernate.exception.BaseException;

public final class MessageUtil {

private static MessageUtil instance = new MessageUtil();

// 用于读取资源属性文件(properties)
private ResourceBundle resourceBoudle = null;

public static MessageUtil getInstance() {
return instance;
}

/**
* 根据异常key获取对应的异常信息
*
* @param exceptionId
* @return
*/
public String getMessage(String exceptionId) {
String message = resourceBoudle.getString(getErrorID(exceptionId));
return message;
}

/**
* 根据异常获取对应的中文异常
*
* @param e
* @return
*/
public String getMessage(BaseException e) {
String message = resourceBoudle.getString(getErrorID(e.getMessage()));
Object[] arguments = e.getValues();
if (arguments != null) {
message = MessageFormat.format(message, arguments);
}
return message;
}

private MessageUtil() {
init();
}

/**
* 读取、加载存放key/value形式,并被Unicode编码的properties配置信息
*/
private void init() {
try {
resourceBoudle = new PropertyResourceBundle(
getClass()
.getClassLoader()
.getResourceAsStream(
"com/spring/jersy/jpa/hibernate/message/messages_zh_CN.properties"));
} catch (Exception ex) {
// LOGGER.error("Error loading messages properties", ex);
}
}

/**
* 根据抛出的异常编号截取与配置文件对应的异常编号 编号E11110——>变成11110
*
* @param exceptionID
*            :example 编号E11110
* @return:变成11110
*/
private String getErrorID(String exceptionID) {
exceptionID = exceptionID.substring(1);
return exceptionID;
}
}
其中,使用java.util.ResourceBundle类来读取、解析properties文件,使用java.text.MessageFormat类来格式化消息,即将配置文件中的{0}参数自动替换为传递过来的值。

D、BaseException异常基类,继承RuntimeException

package com.spring.jersy.jpa.hibernate.exception;

public class BaseException extends RuntimeException {

/**
*
*/
private static final long serialVersionUID = 1L;
private Object[] values;

private int code = 500;

public BaseException() {

}

public BaseException(String msg) {
super(msg);
}

public BaseException(String msg, String... params) {
super(msg);
if (null != params) {
values = new Object[params.length];
for (int i = 0; i < params.length; i++) {
values[i] = params[i];
}
}
}

public BaseException(String msg, int code, String... params) {
this(msg, params);
this.code = code;
}

public BaseException(String msg, int code) {
super(msg);
this.code = code;
}

public BaseException(String message, Throwable cause, String... params) {
super(message, cause);
if (null != params) {
values = new Object[params.length];
for (int i = 0; i < params.length; i++) {
values[i] = params[i];
}
}
}

public BaseException(int code, String message, Throwable cause,
String... params) {
this(message, cause, params);
this.code = code;
}

public Object[] getValues() {
return values;
}

public void setValues(Object[] values) {
this.values = values;
}

public int getCode() {
return code;
}

public void setCode(int code) {
this.code = code;
}
}
E、用户异常处理类UserException

package com.spring.jersy.jpa.hibernate.exception;

import com.spring.jersy.jpa.hibernate.constants.ExceptionKeys;

public class UserException extends BaseException{

/**
*
*/
private static final long serialVersionUID = 1L;

public UserException(){

}

public UserException(String... params){
super(ExceptionKeys.page_number_greater_zero,params);
}
}
F、异常统一处理类ExceptionMappingResource

package com.spring.jersy.jpa.hibernate.resource;

import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

import com.spring.jersy.jpa.hibernate.bean.ExceptionResponse;
import com.spring.jersy.jpa.hibernate.exception.BaseException;
import com.spring.jersy.jpa.hibernate.util.MessageUtil;

/**
* 必须添加该注解使得在程序的任何地方发生运行时异常时,自动的进行异常统一处理
* 同时该类要能够被Jersey扫描到
*/
@Provider
public class ExceptionMappingResource implements ExceptionMapper<Exception> {

@Override
public Response toResponse(Exception exception) {
ResponseBuilder responseBuilder = null;

// 用户自定义的运行时异常处理
if (exception instanceof BaseException) {

//获取用户抛出的异常信息
String code = exception.getMessage();

//根据异常key获取对应的中文异常信息
String message = MessageUtil.getInstance().getMessage(
(BaseException) exception);
Throwable cause = exception.getCause();
if (cause != null) {
String realReason = cause.getMessage();
message += " 可能的原因是:" + realReason + "";
}

//自定义异常返回实体bean类
ExceptionResponse error = new ExceptionResponse();
error.setCode(code);
error.setMessage(message);
error.setStatus("error");

responseBuilder = Response.ok(error).status(
((BaseException) exception).getCode());
}
// 其他异常
else {
ExceptionResponse error = new ExceptionResponse();
error.setCode("E000000");
error.setMessage(exception.getMessage());
error.setStatus("error ");
responseBuilder = Response.ok(error).status(
Response.Status.INTERNAL_SERVER_ERROR);
}
return responseBuilder.build();
}
}
注意:该类必须被@Provider注解,且要实现JAX-RS提供的ExceptionMapper才能时刻跟踪处理项目中抛出的运行时异常,同时,该类需要被放在能够被JPA扫描器扫描到的包中。

G、用户资源类UserResource中的分页查询测试方法

// 分页查询
@GET
@Path("/findByPage")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response findByPage(
@DefaultValue("0") @QueryParam(value = "page") Integer page,
@DefaultValue("10") @QueryParam(value = "pageSize") Integer pageSize) {

if (page >= 0) {
List<User> listUser = userSpringDataJpaService.findUserByPage(page,
pageSize);
ResponseBuilder rb = null;
if (listUser == null) {
rb = Response.serverError().status(500);
} else {
rb = Response.ok(listUser).status(200);
}
return rb.build();
} else {
//如果page为负数,则抛出该异常
throw new UserException(page.toString());
}
}


其中,“page.toString”参数值是用来为messages_zh_CN.properties中的{0}参数赋值的,提示该值是负数。

二、PostMan测试

postman是一个很好的模拟前端发送请求,测试后端代码正确性的测试工具,可以通过chrome浏览器安装插件,界面如下:



接下来,我们准备测试Jersey rest统一异常处理机制是否搭建成功,发送如下请求连接:



附:

测试源码:Spring Data JPA+Jersey+TestNG用户CRUD操作案例
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: