
一、前言Spring Boot對異常的處理有一套默認的機制當應用中產(chǎn)生異常時Spring Boot根據(jù)發(fā)送請求頭中的accept是否包含text/html來分別返回不同的響應信息。當從瀏覽器地址欄中訪問應用接口時請求頭中的accept便會包含text/html信息產(chǎn)生異常時Spring Boot通過org.springframework.web.servlet.ModelAndView對象來裝載異常信息并以HTML的格式返回而當從客戶端訪問應用接口產(chǎn)生異常時客戶端訪問時請求頭中的accept不包含text/htmlSpring Boot則以JSON的格式返回異常信息。下面來驗證一下。二、默認異常處理機制假設(shè)應用中有如下一個ControllerRestControllerRequestMapping(calculate)publicclassCalculateController{GetMapping(/{divisor:\\d}/{dividend:\\d})publicvoidcalculate(PathVariableLongdivisor,PathVariableLongdividend){thrownewRuntimeException(calculate is error);}}在代碼中我們主動的拋出了一個RuntimeException使用瀏覽器訪問http://localhost:8080/calculate/1/0可看到頁面返回了一些異常描述并且請求頭的accpet包含了text/html片段。接著使用模擬發(fā)送REST請求,使用IDEA插件HTTP Client發(fā)送http://localhost:8080/calculate/1/0可以看到請求頭的accept值為*/*并且返回一段JSON格式的信息。查看Spring Boot的BasicErrorController類便可看到這一默認機制的具體實現(xiàn)可看到errorHtml和error方法的請求地址和方法是一樣的唯一的區(qū)別就是errorHtml通過produces {“text/html”}判斷請求頭的accpet屬性中是否包含text/html如果包含便走該方法。三、自定義html異常頁面我們可以通過在src/main/resources/resources/error路徑下定義友好的異常頁面比如定義一個500.html頁面!DOCTYPEhtmlhtmllangenheadmetacharsetUTF-8title500/title/headbody系統(tǒng)內(nèi)部異常/body/html使用瀏覽器再次訪問http://localhost:8080/calculate/1/0同樣的我們也可以定義404.html等常見的HTTP狀態(tài)碼對應的異常頁面。!DOCTYPEhtmlhtmllangenheadmetacharsetUTF-8title404/title/headbody鏈接走丟了/body/html使用瀏覽器訪問http://localhost:8080/calculater/1/0通過自定義html異常頁面并不會影響客戶端發(fā)送請求異常返回的結(jié)果。四、自定義異常處理除了可以通過自定義html異常頁面來改變?yōu)g覽器訪問接口時產(chǎn)生的異常信息我們也可以自定義異常處理來改表默認的客戶端訪問接口產(chǎn)生的異常信息。我們手動定義一個CalculateErrorException繼承RuntimeException。publicclassCalculateErrorExceptionextendsRuntimeException{privatestaticfinallongserialVersionUID-1L;privateStringid;publicCalculateErrorException(Stringid){super(calculate is error);this.idid;}publicStringgetId(){returnid;}publicvoidsetId(Stringid){this.idid;}}然后定義一個Controller異常處理類ControllerExceptionHandlerControllerAdvicepublicclassControllerExceptionHandler{ExceptionHandler(CalculateErrorException.class)ResponseBodyResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)publicMapString,ObjecthandleUserNotExistsException(CalculateErrorExceptione){MapString,ObjectmapnewHashMap();map.put(id,e.getId());map.put(message,e.getMessage());returnmap;}}其中注解ExceptionHandler指定了要處理的異常類型注解ResponseStatus指定異常處理方法返回的HTTP狀態(tài)碼為HttpStatus.INTERNAL_SERVER_ERROR即500。HttpStatus是一個spring自帶的枚舉類型封裝了常見的HTTP狀態(tài)碼及描述publicenumHttpStatus{CONTINUE(100,Continue),SWITCHING_PROTOCOLS(101,Switching Protocols),PROCESSING(102,Processing),CHECKPOINT(103,Checkpoint),OK(200,OK),CREATED(201,Created),ACCEPTED(202,Accepted),NON_AUTHORITATIVE_INFORMATION(203,Non-Authoritative Information),NO_CONTENT(204,No Content),RESET_CONTENT(205,Reset Content),PARTIAL_CONTENT(206,Partial Content),MULTI_STATUS(207,Multi-Status),ALREADY_REPORTED(208,Already Reported),IM_USED(226,IM Used),MULTIPLE_CHOICES(300,Multiple Choices),MOVED_PERMANENTLY(301,Moved Permanently),FOUND(302,Found),/** deprecated */DeprecatedMOVED_TEMPORARILY(302,Moved Temporarily),SEE_OTHER(303,See Other),NOT_MODIFIED(304,Not Modified),/** deprecated */DeprecatedUSE_PROXY(305,Use Proxy),TEMPORARY_REDIRECT(307,Temporary Redirect),PERMANENT_REDIRECT(308,Permanent Redirect),BAD_REQUEST(400,Bad Request),UNAUTHORIZED(401,Unauthorized),PAYMENT_REQUIRED(402,Payment Required),FORBIDDEN(403,Forbidden),NOT_FOUND(404,Not Found),METHOD_NOT_ALLOWED(405,Method Not Allowed),NOT_ACCEPTABLE(406,Not Acceptable),PROXY_AUTHENTICATION_REQUIRED(407,Proxy Authentication Required),REQUEST_TIMEOUT(408,Request Timeout),CONFLICT(409,Conflict),GONE(410,Gone),LENGTH_REQUIRED(411,Length Required),PRECONDITION_FAILED(412,Precondition Failed),PAYLOAD_TOO_LARGE(413,Payload Too Large),/** deprecated */DeprecatedREQUEST_ENTITY_TOO_LARGE(413,Request Entity Too Large),URI_TOO_LONG(414,URI Too Long),/** deprecated */DeprecatedREQUEST_URI_TOO_LONG(414,Request-URI Too Long),UNSUPPORTED_MEDIA_TYPE(415,Unsupported Media Type),REQUESTED_RANGE_NOT_SATISFIABLE(416,Requested range not satisfiable),EXPECTATION_FAILED(417,Expectation Failed),I_AM_A_TEAPOT(418,Im a teapot),/** deprecated */DeprecatedINSUFFICIENT_SPACE_ON_RESOURCE(419,Insufficient Space On Resource),/** deprecated */DeprecatedMETHOD_FAILURE(420,Method Failure),/** deprecated */DeprecatedDESTINATION_LOCKED(421,Destination Locked),UNPROCESSABLE_ENTITY(422,Unprocessable Entity),LOCKED(423,Locked),FAILED_DEPENDENCY(424,Failed Dependency),UPGRADE_REQUIRED(426,Upgrade Required),PRECONDITION_REQUIRED(428,Precondition Required),TOO_MANY_REQUESTS(429,Too Many Requests),REQUEST_HEADER_FIELDS_TOO_LARGE(431,Request Header Fields Too Large),UNAVAILABLE_FOR_LEGAL_REASONS(451,Unavailable For Legal Reasons),INTERNAL_SERVER_ERROR(500,Internal Server Error),NOT_IMPLEMENTED(501,Not Implemented),BAD_GATEWAY(502,Bad Gateway),SERVICE_UNAVAILABLE(503,Service Unavailable),GATEWAY_TIMEOUT(504,Gateway Timeout),HTTP_VERSION_NOT_SUPPORTED(505,HTTP Version not supported),VARIANT_ALSO_NEGOTIATES(506,Variant Also Negotiates),INSUFFICIENT_STORAGE(507,Insufficient Storage),LOOP_DETECTED(508,Loop Detected),BANDWIDTH_LIMIT_EXCEEDED(509,Bandwidth Limit Exceeded),NOT_EXTENDED(510,Not Extended),NETWORK_AUTHENTICATION_REQUIRED(511,Network Authentication Required);...}編寫完自定義異常處理邏輯后我們將CalculateController中的方法拋出的異常改為CalculateErrorExceptionGetMapping(/{divisor:\\d}/{dividend:\\d})publicvoidcalculate(PathVariableLongdivisor,PathVariableLongdividend){thrownewCalculateErrorException(500);}重啟項目再次訪問http://localhost:8080/calculater/1/0響應如下