Jersey Rest 异常统一处理机制

标签: jersey rest 异常 | 发表时间:2016-04-11 19:05 | 作者:NIITYZU
出处:http://blog.csdn.net

前言:

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

作者:NIITYZU 发表于2016/4/11 11:05:30 原文链接
阅读:275 评论:0 查看评论

相关 [jersey rest 异常] 推荐:

Jersey Rest 异常统一处理机制

- - CSDN博客综合推荐文章
        异常分为运行时异常和非运行时异常,所谓的运行时异常是指那些不需要异常捕获的异常,总是交由虚拟机接管,如:ArrayIndexOutOfBoundsException,我们在写程序时,并没有使用try..catch来捕获它.     以前,我们进行项目开发时,习惯性的喜欢使用大量的try...catch...finally方法来进行异常处理,并且,只是将异常信息保存到log日志中即可,并没有将一些异常信息以可读性的方式返回给前端用户.

Jersey JdbcTemplate的使用

- - CSDN博客编程语言推荐文章
Jersey JdbcTemplate的使用. 经过前面的介绍,我们可以实现简单RESTful服务了,并使用map集合模拟了,学生信息的增删改查. 但是实际开发中我们更多的是与数据库打交道,使用数据库来处理数据,因此在下面的章节中我们将会介绍Jersey框架与数据库进行交互的操作. 本章节我们介绍Jersey与JdbcTemplate的结合使用.

Restful 和 Jersey介绍(Web Service )

- - CSDN博客架构设计推荐文章
REST 2000 年由 Roy Fielding 在博士论文中提出,他是 HTTP 规范 1.0 和 1.1 版的首席作者之一. REST 中最重要的概念是资源(resources) ,使用全球 ID(通常使用 URI)标识. 客户端应用程序使用 HTTP 方法(GET/ POST/ PUT/ DELETE )操作资源或资源集.

经典论文 — REST

- ripwu - kernelchina
牛人Roy Thomas Fielding的博士论文,此处可以访问到英文版,中文版可以google一下. HTTP1.0,1.1版本以及URI规范的主要作者,Apache的co-founder. 在写这篇论文之前已经很牛了,笔者不明白的是这种档次牛人还要读博士,文凭有这么重要吗. 文中没有任何令人眼花的数学公式和统计图表,实际上是一篇描述URI,HTTP设计经验教训总结的文章.

MongoDB REST Api介绍

- peigen - NoSQLFan
MongoDB默认会开启一个HTTP协议的端口提供REST的服务,这个端口是你Server端口加上1000,比如你的Server端口为27017,那么这个HTTP端口就是28017,默认的HTTP端口功能是有限的,你可以通过添加–rest参数启动更多功能. 下面是在这个端口通过其RESTFul 的API操作MongoDB数据的几个例子,来源是MongoDB官方文档.

WebSockets与REST之争?

- - 酷勤网-挖经验 [expanded by feedex.net]
WebSockets变得越来越流行并积累了不少用户. 去年年底,WebSockets成为了. W3C的推荐候选,这使得其向标准更迈进了一步. Oracle等其他厂商最近也提交了申请,来启动在Java企业版的下一个版本中引入WebSockets(. JSR 356)的标准流程工作. 绝大部分的主流浏览器,如Chrome、Firefox、Safari和IE,都支持某个WebSockets修正本,并最终会采用最后成形的标准.

REST 与 SOAP巅峰对话

- - CSDN博客互联网推荐文章
随着Restful的流行,soap似乎有点贵族落寞了. 下面我们先详细分析一下他们的各自的特点. REST(Representational State Transfer)是 Roy Fielding 提出的一个描述互联系统架构风格的名词. Web 本质上由各种各样的资源组成,资源由 URI 唯一标识.

为啥REST如此重要?

- - 博客园_知识库
  英文原文: Why REST is so important.   本文我们将讨论 REST,它定义了一组体系架构原则,您可以根据这些原则设计以系统资源为中心的 Web 服务,这是一个非常容易让人误解的概念. 本文主要是写给那些想设计 WebService API 但却对 REST 没有十分清晰认识的开发者们.

REST服务开发实战

- - 互联网 - ITeye博客
本文转自http://kb.cnblogs.com/page/91827/.   如果要说什么是REST的话,那最好先从Web(万维网)说起. 读者可以查看维基百科的词条( http://zh.wikipedia.org/zh-cn/Web),具体的我就不多说了. 总之,Web是我们在互联网上最常用的服务,甚至在某些人的心中,互联网就是Web.