码迷,mamicode.com
首页 > 其他好文 > 详细

Jersey Rest 异常统一处理机制

时间:2016-04-11 14:31:13      阅读:136      评论:0      收藏:0      [点我收藏+]

标签:

前言:

        异常分为运行时异常和非运行时异常,所谓的运行时异常是指那些不需要异常捕获的异常,总是交由虚拟机接管,如: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操作案例

Jersey Rest 异常统一处理机制

标签:

原文地址:http://blog.csdn.net/niityzu/article/details/51112878

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!