码迷,mamicode.com
首页 > 编程语言 > 详细

java restful 后端 Exception 的处理架构总结

时间:2015-01-12 19:14:56      阅读:203      评论:0      收藏:0      [点我收藏+]

标签:exception   架构   异常处理   面向对象   interface   

本文主要结合实现的restfuljava 后端,总结Exception 的处理架构。

1.    exception 的分类和处理方法

异常是面向对象语言非常重要的一个特性,良好的异常设计对程序的可扩展性、可维护性、健壮性都起到至关重要。
JAVA根据用处的不同,定义两类异常
    Checked Exception: Exception的子类,方法签名上需要显示的声明throws,编译器迫使调用者处理这类异常或者声明throws继续往上抛。
    Unchecked Exception: RuntimeException的子类,方法签名不需要声明throws,编译器也不会强制调用者处理该类异常。

 

我们要谈的是 CheckedException:

     应用级的exception: 由应用在运行过程中抛出的exception,主要跟应用的业务逻辑有关, 并且这些exception实在应用中定义的, 比如:输入的登陆账号找不到,登录失败。

     系统级的exception:应用在运行过程中系统抛出的异常,如IO错误,运算错误等等。

我要谈的只要就是这两类exception的处理。

    技术分享

 

 

2.    集中的Exception handler 如何定义及使用

System Exception: Exception 和 SQL exception

Business Exception: BadRequestException, NotFoundException,ServerRejectException, SystemException

 

在restful 后端系统的任何地方都可以抛出这些Exception,通过Spring的Exception handler来集中处理所有抛出的 Exception

@ControllerAdvice
public class GlobalExceptionController {
	private static final Log logger = LogFactory
			.getLog(GlobalExceptionController.class);

	@ExceptionHandler(SQLException.class)
	@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
	public ModelAndView handleSQLException(HttpServletRequest request,
			SQLException ex) {
		handleLog(request, ex);
		Map<String, Object> errorMap = new HashMap<String, Object>();
		errorMap.put("code", HttpStatus.INTERNAL_SERVER_ERROR);
		errorMap.put("Requested", request.getRequestURL());
		errorMap.put("message", ex.toString());

		return new ModelAndView(JSONUtils.VIEW_NAME,
				JSONStringView.JSON_MODEL_DATA, errorMap);
	}

	@ExceptionHandler(BadRequestException.class)
	@ResponseStatus(value = HttpStatus.BAD_REQUEST)
	public ModelAndView handleBadRequestException(
			HttpServletRequest request, BadRequestException ex) {
		handleLog(request, ex);
		ExceptionModel exceptionModel = getExceptionModel(
				HttpStatus.BAD_REQUEST, ex);
		return new ModelAndView(JSONUtils.VIEW_NAME,
				JSONStringView.JSON_MODEL_DATA, exceptionModel);
	}

	@ExceptionHandler(ServerRejectException.class)
	@ResponseStatus(value = HttpStatus.FORBIDDEN)
	public ModelAndView handleServerRejectException(
			HttpServletRequest request, ServerRejectException ex) {
		handleLog(request, ex);
		ExceptionModel exceptionModel = getExceptionModel(HttpStatus.FORBIDDEN,
				ex);
		return new ModelAndView(JSONUtils.VIEW_NAME,
				JSONStringView.JSON_MODEL_DATA, exceptionModel);
	}

	@ExceptionHandler(NotFoundException.class)
	@ResponseStatus(value = HttpStatus.NOT_FOUND)
	public ModelAndView handleNotFoundException(HttpServletRequest request,
			NotFoundException ex) {
		handleLog(request, ex);
		ExceptionModel exceptionModel = getExceptionModel(HttpStatus.NOT_FOUND,
				ex);
		return new ModelAndView(JSONUtils.VIEW_NAME,
				JSONStringView.JSON_MODEL_DATA, exceptionModel);
	}

	@ExceptionHandler(SystemException.class)
	@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
	public ModelAndView handleSystemException(HttpServletRequest request,
			SystemException ex) {
		handleLog(request, ex);
		ExceptionModel exceptionModel = getExceptionModel(
				HttpStatus.INTERNAL_SERVER_ERROR, ex);
		return new ModelAndView(JSONUtils.VIEW_NAME,
				JSONStringView.JSON_MODEL_DATA, exceptionModel);
	}

	@ExceptionHandler(Exception.class)
	@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
	public ModelAndView handleAllException(HttpServletRequest request,
			Exception ex) {

		handleLog(request, ex);
		Map<String, Object> errorMap = new HashMap<String, Object>();
		errorMap.put("code",
				Integer.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()));
		errorMap.put("message", ex.toString());
		return new ModelAndView(JSONUtils.VIEW_NAME,
				JSONStringView.JSON_MODEL_DATA, errorMap);

	}

	private ExceptionModel getExceptionModel(HttpStatus httpStatus,
			CommonException ex) {
		ExceptionModel exceptionModel = new ExceptionModel();
		ErrorENUM errorEnum = ex.getErrorEnum();
		exceptionModel.setStatus(httpStatus.value());
		exceptionModel.setMoreInfo(ex.getMoreInfo());
		if (errorEnum != null) {
			exceptionModel.setErrorCode(errorEnum.getCode());
			exceptionModel.setMessage(errorEnum.toString());
		}
		return exceptionModel;
	}

	private void handleLog(HttpServletRequest request, Exception ex) {
		Map parameter = request.getParameterMap();
		StringBuffer logBuffer = new StringBuffer();
		if (request != null) {
			logBuffer.append("  request method=" + request.getMethod());
			logBuffer.append("  url=" + request.getRequestURL());
		}
		if (ex instanceof CommonException) {
			logBuffer.append("  moreInfo="
					+ ((CommonException) ex).getMoreInfo());
		}
		if (ex != null) {
			logBuffer.append("  exception:" + ex);
		}
		logger.error(logBuffer.toString());
	}

}


想要深入了解的 可以参考这里

http://www.journaldev.com/2651/spring-mvc-exception-handling-exceptionhandler-controlleradvice-handlerexceptionresolver-json-response-example

 

3.    HTTP request 的 exception 返回设计

在每个应用Exception 中设计不同的返回消息和errorcode:

并且返回的status 也不同。

针对RESTFUL interface 返回错误主要有以下两种不同的方式:

技术分享

第一种 Facebook,无论正常返回还是错误返回,HTTPstatus 都是 200 OK, 返回信息需要check 返回的Object

第二种 Twilio 和 SimpleGeo ,HTTP status 正常返回 200, 出错 返回 400,404, 403 等等。

笔者采用的是第二种方式。

 

     Http status 的返回值选择:

根据restful应用的具体情况笔者主要选择了:

    

·        200 – OK

·        400 - Bad Request

·        403 – Forbidden

·        404 – Not Found

·        503 – Internal servererror


Restful 返回error 信息的model class定义:

public classExceptionModel {
    private Integer status;  
    private Integer errorCode;
    private String message;
    private String moreInfo;
   
   
    public Integer getErrorCode(){
        return errorCode;
    }
    public void setErrorCode(Integer errorCode) {
        this.errorCode = errorCode;
    }
    public Integer getStatus() {
        return status;
    }
    public void setStatus(Integer status) {
        this.status = status;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public String getMoreInfo() {
        return moreInfo;
    }
    public void setMoreInfo(String moreInfo) {
        this.moreInfo = moreInfo;
    }
}

{
"message":"400202: The specific parameter is exist in system. ",
"status":400,
"errorCode":400202,
"moreInfo":"06d690e7-fb4d-45a8-91e0-7d82b0c60ca5 and 09f5edd6-1be3-4949-a302-9f487f82d723' is exist in system "
}


详情可参考:

https://blog.apigee.com/detail/restful_api_design_what_about_errors

4.    使用Spring AOP 进行输入和返回验证

@Aspect
public class InfoObjectInterceptor {
 
@Autowired
private ValidatorBean validatorBean;
 
private static final Log logger =LogFactory
           .getLog(InfoObjectInterceptor.class);
 
@Pointcut("execution(* com.json.BeanLang.createInfoObject(..))")
private voidcreateInfoObjectMethod() {
}//
 
 
@Before("createInfoObjectMethod()&& args(infoObjectModel)")
public voiddoAccessCreateCheck(InfoObjectModel infoObjectModel)
           throws Exception {
      if(validatorBean.checkInfoObjectExist(infoObjecId)) {
           String moreInfo = newStringBuffer()
                    .append(infoObjecId)
                    .append(" isexist in system").toString();
           throw newBadRequestException(
                    ErrorENUM.PARAMETER_EXIST_IN_SYSTEM,moreInfo);
      }
}   
 
}


Spring AOP 可参考:
  http://www.mkyong.com/spring3/spring-aop-aspectj-annotation-example/

5.    异常处理的原则和技巧

参考:http://lavasoft.blog.51cto.com/62575/18920/

1、避免过大的try块,不要把不会出现异常的代码放到try块里面,尽量保持一个try块对应一个或多个异常。
2、细化异常的类型,不要不管什么类型的异常都写成Excetpion。
3、catch块尽量保持一个块捕获一类异常,不要忽略捕获的异常,捕获到后要么处理,要么转译,要么重新抛出新类型的异常。
4、不要把自己能处理的异常抛给别人。
5、不要用try...catch参与控制程序流程,异常控制的根本目的是处理程序的非正常情况

 

6.    优点

a)    Business 方面的 信息反馈给client, 方便系统集成和调试

b)    系统级的Exception 有利于 function 测试 发现隐藏的bug

 

 

参考:

http://mariuszprzydatek.com/2013/07/29/spring-localized-exception-handling-in-rest-api/

java restful 后端 Exception 的处理架构总结

标签:exception   架构   异常处理   面向对象   interface   

原文地址:http://blog.csdn.net/jingshuaizh/article/details/42646063

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