javascript
springboot转发http请求_网易后端实习生分享:Springboot异常和错误处理规范
由于錯誤在所難免,異常處理已經成為開發工作中不可或缺的部分。
在web開發中,我們通常不希望用戶看到一個寫滿StackTrace的錯誤頁面;同時,我們希望出現錯誤或發生異常時,開發運維人員可以看到詳細的錯誤信息,以便進行查錯和DEBUG。
所以,在開發過程中,應重視異常處理。在進行業務邏輯開發之前,就應該定義好自己的異常處理流程。
1. 異常處理流程概述
異常處理的對象分為兩類:
Springboot會統一處理第1種錯誤,由ErrorController捕獲并進行處理,根據請求的Accpet字段返回錯誤頁面或json數據。這里貼出Springboot自己的BasicErrorController實現:
@Controllerpublic class BasicErrorController implements ErrorController { @Value("${error.path:/error}") private String errorPath; private final ErrorAttributes errorAttributes; public BasicErrorController(ErrorAttributes errorAttributes) { Assert.notNull(errorAttributes, "ErrorAttributes must not be null"); this.errorAttributes = errorAttributes; } public String getErrorPath() { return this.errorPath; } @RequestMapping( value = {"${error.path:/error}"}, produces = {"text/html"} ) public ModelAndView errorHtml(HttpServletRequest request) { return new ModelAndView("error", this.getErrorAttributes(request, false)); } @RequestMapping({"${error.path:/error}"}) @ResponseBody public ResponseEntity> error(HttpServletRequest request) { Map body = this.getErrorAttributes(request, this.getTraceParameter(request)); HttpStatus status = this.getStatus(request); return new ResponseEntity(body, status); } //……}也可以自己寫一個ErrorController返回自定義頁面。
對于程序中發送的異常,可以手動進行捕獲。如果沒有手動捕獲或有所遺漏,Springboot提供了@ExceptionHandler(value={})對某種類型的異常統一進行處理。通常,可以通過轉發(forward)或重定向(redirect)方式將該異常轉給自己定制的ExceptionController進行處理。ExceptionController可以根據請求類型返回錯誤頁面或json數據。
2. 自定義RuntimeException
可以定制自己的Exception進行異常信息記錄:
public abstract class BaseRuntimeException extends RuntimeException { private static final long serialVersionUID = -1842796916322056555L; public BaseRuntimeException() { super(); } public BaseRuntimeException(String message) { super(message); } public BaseRuntimeException(String message, Throwable cause) { super(message, cause); } /** * 返回異常的錯誤碼 */ public abstract Integer getErrorCode(); /** * 返回異常的描述(不帶應用前綴) */ public abstract String getErrorMessage(); /** * 返回異常的日志(帶應用前綴) */ public abstract String getErrorLog();}然后再Service層定義該服務模塊異常信息。
public class MiscServiceException extends BaseRuntimeException implements IReThrowException { private static final long serialVersionUID = -1844670008823631700L; private MiscErrorCode miscErrorCode = MiscErrorCode.SUCCESS; private String errorLog = MiscErrorCode.SUCCESS.getDesc(); public MiscServiceException() { super(); } public MiscServiceException(String message, Throwable cause) { super(message, cause); this.errorLog = message; } public MiscServiceException(String message) { super(message); this.errorLog = message; } public MiscServiceException(String message, MiscErrorCode miscErrorCode) { super(message); this.miscErrorCode = miscErrorCode; this.errorLog = message; } /** * 返回異常的錯誤碼.* 方便日志查看追蹤. * * @see BaseRuntimeException#getErrorCode() */ @Override public Integer getErrorCode() { return miscErrorCode.getValue(); } /** * 返回異常的描述.
* 可直接用于前端錯誤提示. * * @see BaseRuntimeException#getErrorMessage() */ @Override public String getErrorMessage() { return miscErrorCode.getDesc(); } /** * 返回異常的日志.
* 用于服務器日志打印. * * @see BaseRuntimeException#getErrorLog() */ @Override public String getErrorLog() { return errorLog; }}
在Service層生成異常對象并拋出。
if (…) throw new MiscServiceException(“log message…”, MiscErrorCode.XXXERRIR);在Manager層繼續向上拋出。
public interface SomeService { DTO someFunc() throws MiscServiceException { //…}在Controller層捕獲異常,進行處理——返回相關頁面。
try {//…} catch (MiscServiceException e) {log.error(e.getErrorLog());return ResponseView.fail(e.getErrorCode(), e.getErrorMessage());}如此以來,代碼中定義的異常和錯誤均可以捕捉。
由于BaseRuntimeException是一種RuntimeException,Mananger層聲明方法是不加throws Exception也可以通過編譯。小猿建議每一個Manager的方法都加上throws Exception聲明。另外,BaseRuntimeException實際上也可以直接繼承Exception,這樣編譯器會強制要求對其進行異常進行處理。
3. @ExceptionHandler
上述方案解決了一部分自定義異常。對于其他的自己未定義的Runtime Exception,例如Null Pointer Exception,Springboot提供了ExceptionHandler,用于捕獲所有代碼中沒有主動catch的異常。通常,我們該異常轉發(forward)或重定向(redirect)至某個自定義的Controller進行處理。
轉發和重定向的效果不同:在瀏覽器端,轉發的請求,頁面不會跳轉,URL不會改變;重定向的請求,URL會改變。重定向實際上是瀏覽器進行了兩次請求。對于參數傳遞,兩種方式采取的方法也不同。重定向方式可以使用RedirectAttributes傳遞參數,轉發方式則將參數放置在Request的Attributes中。
轉發:
@ControllerAdvicepublic class CustomExceptionHandler { /** * 將異常頁轉發到錯誤頁 */ @ExceptionHandler(Exception.class) public ModelAndView handleError(HttpServletRequest req, Exception ex) { log.info("Exception e : " + ex, ex); if (BaseRuntimeException.class.isAssignableFrom(ex.getClass())) { BaseRuntimeException e = (BaseRuntimeException) ex; req.setAttribute("code", e.getErrorCode()); req.setAttribute("message", e.getErrorMessage()); } else { req.setAttribute("code", FrontCode.OTHER_ERROR.getCode()); req.setAttribute("message", FrontCode.OTHER_ERROR.getDesc()); } return new ModelAndView("forward:/exception"); }}重定向:
/** * 將異常頁使用重定向方式到錯誤頁 * * @param req * @param ex * @param mode * @return */@ExceptionHandler(Exception.class) public ModelAndView handleError(HttpServletRequest req, Exception ex ,RedirectAttributes mode) { log.info("Exception e : " + ex,ex); ModelAndView mav = new ModelAndView(); if (BaseRuntimeException.class.isAssignableFrom(ex.getClass())) { BaseRuntimeException e = (BaseRuntimeException) ex; mode.addAttribute("code",e.getErrorCode()); mode.addAttribute("message",e.getErrorMessage()); } else { mode.addAttribute("code", FrontCode.OTHER_ERROR.getCode()); mode.addAttribute("message", FrontCode.OTHER_ERROR.getDesc()); } return new ModelAndView("redirect:/exception");}4. ErrorController
在下文貼出的示例中,我們將異常處理的Controller也放在ErrorController中。其中,errorHtml方法同于對沒有經過Controller層的錯誤進行處理,返回自定義錯誤頁;exception和exceptionHtml方法負責接收ExceptionHandler轉發或重定向的異常處理流,根據produces的類型是”json/application”還是“text/html”,分別返回json和錯誤頁面。
@Controllerpublic class CommonErrorController implements ErrorController { @Autowired private UserSecurityHelper userSecurityHelper; private static final String ERROR_PATH = "error"; private static final String EXCEPTION_PATH = "exception"; @RequestMapping(value = ERROR_PATH) public ModelAndView errorHtml(HttpServletRequest request) { ModelAndView modelAndView = new ModelAndView("/error/error"); Object statusCode = request.getAttribute("javax.servlet.error.status_code"); //當請求的錯誤類型非404、403、402、401時,返回500的錯誤頁面 if (statusCode == null || (!statusCode.equals(HttpStatus.NOT_FOUND.value()) && !statusCode.equals(HttpStatus.UNAUTHORIZED.value()) && !statusCode.equals(HttpStatus.PAYMENT_REQUIRED.value()) && !statusCode .equals(HttpStatus.FORBIDDEN.value()))) { statusCode = HttpStatus.INTERNAL_SERVER_ERROR.value(); } modelAndView.addObject("code", statusCode); modelAndView.addObject("message", "你很神,找到了不存在的頁面。"); return modelAndView; } /* * 使用forward轉發. */ @RequestMapping(value = EXCEPTION_PATH, produces = "application/json") @ResponseBody public ResponseEntity exception(HttpServletRequest request) { Integer code = (Integer) request.getAttribute("code"); String message = (String) request.getAttribute("message"); return ResponseView.fail(code, message); } @RequestMapping(value = EXCEPTION_PATH, produces = {"text/html"}) public ModelAndView exceptionHtml(HttpServletRequest request) { EduWebUser eduWebUser = userSecurityHelper.getEduWebUser(); ModelAndView mav = new ModelAndView("/error/error"); mav.addObject("code", (Integer) request.getAttribute("code")); mav.addObject("message", (String) request.getAttribute("message")); mav.addObject("webUser", eduWebUser); return mav; } @Override public String getErrorPath() { return ERROR_PATH; }}如果使用Redirect跳轉,ErrorController中接收參數的相應代碼要隨之改變。
/*使用Redirect跳轉*/@RequestMapping(value = EXCEPTION_PATH, produces = "application/json")@ResponseBodypublic ResponseEntity exception(HttpServletRequest request , @RequestParam(required = false) String message, @RequestParam(required = false) Integer code) { Map body = new HashMap<>(4); body.put("code", code); body.put("message", message); return new ResponseEntity(body, HttpStatus.OK);}@RequestMapping(value = EXCEPTION_PATH, produces = { "text/html"})public ModelAndView exceptionHtml(HttpServletRequest request , @RequestParam(required = false) String message, @RequestParam(required = false) Integer code) { ModelAndView mav = new ModelAndView("/error/error"); EduWebUser eduWebUser = userSecurityHelper.getEduWebUser(); mav.addObject("code", code); mav.addObject("message", message); mav.addObject("webUser", eduWebUser); return mav;}5. 測試
我們定義了專門用于測試的Service和Controller。其中,throw測試程序中代碼捕獲異常,silent測試由ExceptionHandler捕獲的異常。
public interface ExceptionService { public void testThrowException() throws MiscServiceException; public void testSilentException();}@Service("exceptionService")public class ExceptionServiceImpl implements ExceptionService { @Override public void testThrowException() throws MiscServiceException { throw new ForumException("Log Message"); } @Override public void testSilentException() { throw new ForumException("Log Message"); }}@RestController@RequestMapping("/exception")public class ExceptionController { @Resource private ExceptionService exceptionService; @RequestMapping("/throw") public ResponseEntity testThrow() { try { exceptionService.testThrowException(); return ResponseView.success(null); } catch (MiscServiceException e) { e.printStackTrace(); return ResponseView.fail(e.getErrorCode(), e.getErrorMessage()); } } @RequestMapping("/silent") public ResponseEntity testSilent() { exceptionService.testSilentException(); return ResponseView.success(null); }}測試記錄如下。
代碼主動捕獲異常:
Springboot通過forward方式統一捕獲異常:
Springboot通過redirect方式統一捕獲異常:
Springboot統一捕獲異常,返回json:
對于Controller之前發生的錯誤和異常,返回自定義頁面:
如果對我的更新內容很感興趣,希望可以得到您的一個點贊和關注,這將是對我最大的鼓勵和支持,感謝~不論多與少,我都會堅持更新。另外,私聊我【Java學習資料】可以收獲互聯網大廠Java面經和Java最新最全資料~幫助你早日拿到互聯網大廠的offer~
總結
以上是生活随笔為你收集整理的springboot转发http请求_网易后端实习生分享:Springboot异常和错误处理规范的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: npu算力如何计算_异构计算神器来了,它
- 下一篇: html调用接口_Spring 自调用事