标签: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); } } }
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