Jersey Rest 异常统一处理机制

前言:

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

时间: 2024-10-31 08:07:02

Jersey Rest 异常统一处理机制的相关文章

MVC 增加统一异常处理机制

原文地址:http://www.cnblogs.com/leoo2sk/archive/2008/11/05/1326655.html 摘要      本文将对"MVC公告发布系统"的发布公告功能添加日志功能和异常处理功能,借此来讨论ASP.NET MVC中拦截器的使用方法.一个小难题      我们继续完善"MVC公告发布系统",这次,我们的需求是对公告发布功能添加日志记录能力,即在发布公告前,记录一次,在公告发布成功后,再记录一次.然后还要使得其具备异常处理,即

Spring MVC异常统一处理(异常信息的国际化,日志记录)

JAVA EE项目中,不管是对底层的数据操作,还是业务层的处理过程,还是控制层的处理,都不可避免的会遇到各种可预知的(业务异常主动抛出).不可预知的异常需要处理.一般dao层.service层的异常都会直接抛出,最后由controller统一进行处理,每个过程都单独处理异常,且要考虑到异常信息和前端的反馈,代码的耦合度高,不统一,后期维护的工作也多. 同时还必须考虑异常模块和日志模块.国际化的支持. 因此需要一种异常处理机制将异常处理解耦出来,这样保证相关处理过程的功能单一,和系统其它模块解耦,

Spring MVC异常统一处理(包括普通请求异常以及ajax请求异常)

通常SpringMVC对异常的配置都是返回某个jsp视图给用户,但是通过ajax方式发起请求,即使发生异常,前台也无法获得任何异常提示信息.因此需要对异常进行统一的处理,对于普通请求以及ajax请求的异常都有效. 1.Spring MVC的异常处理机制 Spring MVC 通过HandlerExceptionResolver处理程序的异常,包括处理器映射,数据绑定以及处理器执行时发生的异常.HandlerExceptionResolver仅有一个接口方法: ModelAndView resol

Python之异常抛出机制

异常抛出机制 : 常见的Python异常:

完成这个例子,说出java中针对异常的处理机制。

有一个类为ClassA,有一个类为ClassB,在ClassB中有一个方法b,此方法抛出异常,在ClassA类中有一个方法a,请在这个方法中调用b,然后抛出异常.在客户端有一个类为TestC,有一个方法为c ,请在这个方法中捕捉异常的信息.完成这个例子,请说出java中针对异常的处理机制. [java] view plaincopy package com.itheima; import java.io.IOException; /** *第6题:有一个类为ClassA,有一个类为ClassB,

spring @ExceptionHandler注解方式实现异常统一处理

首先,在我们的工程中新建BaseController父类,内容如下: package com.ztesoft.zsmartcity.framework.exception; import java.io.IOException;import java.io.PrintWriter;import java.io.StringWriter;import java.util.Date;import javax.servlet.ServletException;import javax.servlet.

spring mvc 异常统一处理 【转】

SpringMVC 提供的异常处理主要有两种方式,一种是直接实现自己的HandlerExceptionResolver,另一种是使用注解的方式实现一个专门用于处理异 常的Controller——ExceptionHandler.前者当发生异常时,页面会跳到指定的错误页面,后者同样,只是后者会在每个 controller中都需要加入重复的代码.如何进行简单地统一配置异常,使得发生普通错误指定到固定的页面,ajax发生错直接通过js获取,展现给 用户,变得非常重要.下面先介绍下2种异常处理方式,同时

异常及异常的处理机制

异常处理机制 异常和异常处理机制 异常的概念: 程序在运行时,发生了不可预测的事件,它阻止了程序按照我们程序员的预期 正常执行! 程序中的异常:   那么,我们剩下的5行代码九部会执行了 那么什么是异常的处理机制呢? 能让程序在异常发生时,按照代码预先设定的异常处理逻辑,针对性地处理异常,     让程序恢复正常并继续执行. 异常的分类和结构图 所有的异常错误和错误的父类=======>Throwtable   Throwable类是Java异常类型的顶层父类,一个对象只有是 Throwable

springmvc请求参数异常统一处理,结合钉钉报告信息定位bug位置

参考之前一篇博客:springmvc请求参数异常统一处理 1.ExceptionHandlerController package com.oy.controller; import java.text.MessageFormat; import java.util.ResourceBundle; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.TypeMismatchException