重慶做網(wǎng)站_重慶網(wǎng)站建設(shè)_重慶網(wǎng)絡(luò)推廣_重慶網(wǎng)絡(luò)公司制作網(wǎng)頁app
在處理網(wǎng)絡(luò)請求時,有一部分功能是需要抽出來統(tǒng)一處理的,與業(yè)務(wù)隔開。
登錄校驗(yàn)
可以利用spring mvc的攔截器Interceptor,實(shí)現(xiàn)HandlerInterceptor接口即可。實(shí)現(xiàn)該接口后,會在把請求發(fā)給Controller之前進(jìn)行攔截處理。
攔截器的實(shí)現(xiàn)分為以下兩個步驟:
- 創(chuàng)建?定義攔截器,實(shí)現(xiàn) HandlerInterceptor 接?的 preHandle(執(zhí)?具體?法之前的預(yù)處理)?法。
- 將?定義攔截器加? WebMvcConfigurer 的 addInterceptors ?法中。
我們使用session來作為登錄校驗(yàn)的例子,實(shí)現(xiàn)如下:
package com.demo;import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;/*** 登錄攔截器*/
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {/*** 為 false 則不能繼續(xù)往下執(zhí)行;為 true 則可以。*/ @Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 判斷session的信息是否合法HttpSession session = request.getSession(false);if (session != null && session.getAttribute("userinfo") != null) {return true;}log.error("當(dāng)前用戶沒有訪問權(quán)限");response.setStatus(401);return false;}
}
將寫好的?定義攔截器通過WebMvcConfigurer注冊到容器中,使得攔截器生效,具體實(shí)現(xiàn)代碼如下:
package com.demo;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class MyConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/**") // 攔截所有請求.excludePathPatterns("/user/login"); // 排除不攔截的 url}
}
如果不注入對象的話,addInterceptor() 的參數(shù)也可以直接 new 一個對象:
@Configuration // 一定不要忘記
public class MyConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**") // 攔截所有請求.excludePathPatterns("/user/login"); // 排除不攔截的 url}
}
原理
所有的 Controller 執(zhí)?都會通過spring mvc的調(diào)度器 DispatcherServlet 來實(shí)現(xiàn),所有?法都會執(zhí)? DispatcherServlet 中的 doDispatch 調(diào)度?法,doDispatch 源碼如下:
protected void doDispatch(HttpServletRequest request, HttpServletResponseresponse) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {try {ModelAndView mv = null;Object dispatchException = null;try {// ... 忽略不重要的代碼// 調(diào)?預(yù)處理if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 執(zhí)? Controller 中的業(yè)務(wù)mv = ha.handle(processedRequest, response, mappedHandler.getHandler());// ... 忽略不重要的代碼} catch (Exception var20) {dispatchException = var20;} catch (Throwable var21) {dispatchException = new NestedServletException("Handler dispatch failed", var21);}this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);} catch (Exception var22) {this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);} catch (Throwable var23) {this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));}} finally {if (asyncManager.isConcurrentHandlingStarted()) {if (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}} else if (multipartRequestParsed) {this.cleanupMultipart(processedRequest);}}
}
從上述源碼可以看出在開始執(zhí)? Controller 之前,會先調(diào)? 預(yù)處理?法 applyPreHandle,? applyPreHandle ?法的實(shí)現(xiàn)源碼如下:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex= i++) {// 獲取項?中使?的攔截器 HandlerInterceptorHandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);if (!interceptor.preHandle(request, response, this.handler)) {this.triggerAfterCompletion(request, response, (Exception)null);return false;}}return true;
}
異常處理
請求時的異常處理也是比較常見的統(tǒng)一處理的對象。
統(tǒng)?異常處理使?的是 @ControllerAdvice + @ExceptionHandler 來實(shí)現(xiàn)的,@ControllerAdvice 表示控制器通知類,@ExceptionHandler 是異常處理器,兩個結(jié)合表示當(dāng)出現(xiàn)異常的時候執(zhí)?某個通知,也就是執(zhí)?某個?法事件,具體實(shí)現(xiàn)代碼如下:
package com.demo;import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;import java.util.HashMap;/*** 統(tǒng)一處理異常* 一般都需要自定義一個異常對象,這里為了簡單說明只用一個map對象來說明*/
@ControllerAdvice
public class ErrorAdive {@ExceptionHandler(Exception.class)@ResponseBodypublic HashMap<String, Object> exceptionAdvie(Exception e) {HashMap<String, Object> result = new HashMap<>();result.put("code", "-1");result.put("msg", e.getMessage());return result;}@ExceptionHandler(ArithmeticException.class)@ResponseBodypublic HashMap<String, Object> arithmeticAdvie(ArithmeticException e) {HashMap<String, Object> result = new HashMap<>();result.put("code", "-2");result.put("msg", e.getMessage());return result;}}
此時若出現(xiàn)異常就不會報錯了,代碼會繼續(xù)執(zhí)行,但是會把自定義的異常信息返回給前端!
原理
@ControllerAdvice是spring的aop對于Controller進(jìn)行切面所有屬性的,包括切入點(diǎn)和需要織入的切面邏輯,配合@ExceptionHandler來捕獲Controller中拋出的指定類型的異常,從而達(dá)到不同類型的異常區(qū)別處理的目的。
返回數(shù)據(jù)結(jié)構(gòu)
統(tǒng)?的返回數(shù)據(jù)結(jié)構(gòu)可以使用 @ControllerAdvice + ResponseBodyAdvice接口 的方式實(shí)現(xiàn),具體實(shí)現(xiàn)代碼如下:
package com.demo;import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyA
dvice;import java.util.HashMap;/*** 統(tǒng)一返回數(shù)據(jù)的處理*/
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {/*** 內(nèi)容是否需要重寫(通過此?法可以選擇性部分控制器和?法進(jìn)?重寫)* 返回 true 表示重寫*/@Overridepublic boolean supports(MethodParameter returnType, Class converterTyp
e) {return true;}/*** ?法返回之前調(diào)?此?法*/@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType,MediaType selectedContentType,Class selectedConverterType, ServerHttpRequest request,ServerHttpResponse response) {// 構(gòu)造統(tǒng)?返回對象HashMap<String, Object> result = new HashMap<>();result.put("state", 1);result.put("msg", "");result.put("data", body);return result;}
}