SSM框架系列學(xué)習(xí)總結(jié)10之SpringMVC 攔截器&異常處理器

SpringMVC的攔截器實現(xiàn)權(quán)限的驗證

攔截器攔截請求,然后再判斷相關(guān)信息,是否請求可以繼續(xù)下去

Springmvc如何實現(xiàn)攔截器功能:
只需要自定義一個類, 去實現(xiàn)springmvc提供的一個接口,HandlerInterceptor

image.png

/**
 * 自定義攔截器
 * Author menglanyingfei
 * Created on 2018.01.25 9:41
 */
public class InterceptorDemo1 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        /**
         * 在執(zhí)行Handler方法前調(diào)用該方法, 真正實現(xiàn)攔截的方法
         * return true: 表示將該請求放行
         * return false: 表示不讓請求繼續(xù)往下執(zhí)行
         */
        System.out.println("InterceptorDemo1 攔截請求! preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        /**
         * 在進(jìn)入Handler以后, 返回ModelAndView之前執(zhí)行
         */
        System.out.println("InterceptorDemo1 postHandle ");
    }

    /**
     * 該方法是在Handler執(zhí)行完畢以后再調(diào)用該方法
     * @param httpServletRequest
     * @param httpServletResponse
     * @param o
     * @param e
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("InterceptorDemo1 afterCompletion");
    }
}

然后只要在springmvc.xml文件中配置一個攔截器,配置具體攔截的請求url

    <!-- 配置攔截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <!-- path: 指定將要攔截的路徑
            /**: 表示攔截所有請求
            class: 自定義攔截器的全限定名 -->
            <mvc:mapping path="/**"/>
            <bean class="com.wtu.ssm.interceptor.InterceptorDemo1"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <!-- path: 指定將要攔截的路徑
            /**: 表示攔截所有請求
            class: 自定義攔截器的全限定名 -->
            <mvc:mapping path="/**"/>
            <bean class="com.wtu.ssm.interceptor.InterceptorDemo2"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <!-- path: 指定將要攔截的路徑
            /**: 表示攔截所有請求
            class: 自定義攔截器的全限定名 -->
            <mvc:mapping path="/**"/>
            <bean class="com.wtu.ssm.interceptor.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
攔截器鏈的執(zhí)行順序:
SpringMVC中攔截器.png

Interceptor1 ----> interceptor2 ----> intercptor3 ...

  1. 如果兩個攔截器都放行:
    攔截的方法是根據(jù)配置文件的配置順序進(jìn)行攔截。其他方法相反


    image.png

2.如果Interceptor1放行, interceptor2不放行
要執(zhí)行Controller的方法, 一定要所有的攔截器都放行才能執(zhí)行


image.png

3.如果兩個Interceptor都返回false


image.png

示例演示: 實現(xiàn)用戶登錄權(quán)限的校驗

  1. 攔截器攔截所有的請求
  2. 判斷session中是否存在用戶信息,如果存在則放行,如果不存在,接續(xù)判斷請求路徑是否是登錄頁面
  3. 如果請求路徑是登錄頁面,那么就放行,如果不是, 則重定向到登錄頁面進(jìn)行登錄
定義登錄的Controller
@Controller
public class UserController {

    @RequestMapping("/login.do")
    public String login(HttpSession session, String username, String password) {
        if (username != null && !"".equals(username)) {
            // 將用戶信息保存到session中
            session.setAttribute("username", username);
            return "redirect:/findItemsByName.do";
        } else {
            return "forward:/jsp/login.jsp";
        }

    }

    @RequestMapping("/exit.do")
    public String exit(HttpSession session) {
        // 清除session
        session.invalidate();

        return "redirect:/jsp/login.jsp";
    }
}
定義攔截器
public class LoginInterceptor implements HandlerInterceptor {
    // 攔截所有請求
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
        // 判斷session中是否存在用戶信息
        HttpSession session = request.getSession();
        // 獲取session中的用戶信息
        String username = (String) session.getAttribute("username");
        if (username != null) {
            return true; // 放行
        }
        // 獲取請求路徑
        String path = request.getRequestURI();
        if (path.contains("/login.do")) {
            return true;
        }
        // 重定向到登錄頁面
//        request.getRequestDispatcher("/jsp/login.jsp").forward(request, response);
        response.sendRedirect(request.getContextPath() + "/jsp/login.jsp");
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}

配置攔截器
   <mvc:interceptors>     
        <mvc:interceptor>
            <!-- path: 指定將要攔截的路徑
            /**: 表示攔截所有請求
            class: 自定義攔截器的全限定名 -->
            <mvc:mapping path="/**"/>
            <bean class="com.wtu.ssm.interceptor.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

SpringMVC參數(shù)校驗

使用Hibernate-validator對controller綁定的POJO形參進(jìn)行校驗。

  1. 導(dǎo)入校驗所需要的jar包


    image.png
  2. 在xml文件中配置校驗器

    <!-- 配置參數(shù)校驗器 -->
    <bean id="validator"
          class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
        <!-- 校驗器-->
        <property name="providerClass" value="org.hibernate.validator.HibernateValidator" />

        <!-- 指定校驗使用的資源文件,如果不指定則默認(rèn)使用classpath下的ValidationMessages.properties -->
        <property name="validationMessageSource" ref="messageSource" />
    </bean>

    <!-- 校驗錯誤信息配置文件 -->
    <bean id="messageSource"
          class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <!-- 資源文件名-->
        <property name="basenames">
            <list>
                <value>classpath:CustomValidationMessages</value>
            </list>
        </property>
        <!-- 資源文件編碼格式 -->
        <property name="fileEncodings" value="utf-8" />
        <!-- 對資源文件內(nèi)容緩存時間,單位秒 -->
        <property name="cacheSeconds" value="120" />
    </bean>

3.引入校驗器:

    <!-- 配置注解形式的適配器和映射器
        conversion-service: 配置自定義參數(shù)綁定器
        validator: 引入一個校驗器
    -->
    <mvc:annotation-driven conversion-service="conversionService"
        validator="validator"/>

錯誤信息屬性文件:CustomValidationMessages.properties

#配置錯誤提示信息
name.length.error=商品名稱過長或者過短!!!
date.is.null=date can't be null

4.在pojo屬性中添加參數(shù)的校驗

public class Items {

    private Integer id;
    @Size(min = 2, max = 15, message = "{name.length.error}")
    private String name;
    private Double price;
    private String detail;
    private String pic;
    @NotNull(message = "{date.is.null}")
    private Date createtime;
    // ...
}
  1. Controller方法中的代碼
    // 修改信息
    @RequestMapping(value = "/updateItems.do")
    public String updateItems(Model model,
                              @Validated Items items, BindingResult bindingResult) throws Exception {

        if (bindingResult.hasErrors()) {
            // 獲取錯誤的信息
            List<ObjectError> errorList = bindingResult.getAllErrors();
            // 將錯誤信息保存到request域中
//            errorList.get(0).getDefaultMessage();
            model.addAttribute("errorList", errorList);
            // 轉(zhuǎn)發(fā)到商品修改頁面
            return "forward:/jsp/editItem.jsp";
        }

        itemsService.updateItems(items);
        return "redirect:/findItemsByName.do";
        
    }

image.png

如果添加了參數(shù)校驗, 那么必須在pojo參數(shù)前面加上@Validated注解,然后在參數(shù)后面加上BindingResult 參數(shù), 用來獲取錯誤的信息

數(shù)據(jù)的回寫

比如在商品修改頁面 提交以后 如果出現(xiàn)錯誤 那么重新回到商品修改頁面的時候,商品的信息還在 這就是數(shù)據(jù)的回寫

默認(rèn)情況下,如果出現(xiàn)異常,那么會自動將pojo的類型名 并且首字母小寫作為request域的鍵,然后將pojo的對象作為值, 然后傳遞到頁面。

如果在頁面接收值的時候使用的變量名不是pojo的類型首字母小寫,那么在POJO參數(shù)的前面加上@ModelAttribute("items1") 明確指定 request域中的鍵為items1,那么這個時候就可以在頁面上 通過items1來獲取值

    // 修改信息
    @RequestMapping(value = "/updateItems.do")
    public String updateItems(Model model,
                              @Validated @ModelAttribute("items1") Items items, BindingResult bindingResult ) throws Exception {

如果上面的方式不能理解,那么可以手動回寫: 加上如下代碼即可

image.png

如果是簡單類型的參數(shù)回寫:那么只能通過
model.addAttribute("key", value);

Springmvc的全局異常處理

  1. 自定義一個異常類
/**
 * 自定義異常類
 * Author menglanyingfei
 * Created on 2018.01.25 16:37
 */
public class MyException extends Exception {
    // 保存錯誤信息
    private String message;

    public MyException(String message) {
        this.message = message;
    }

    @Override
    public String getMessage() {
        return message;
    }
}
  1. 定義一個類實現(xiàn)HandlerExceptionResolver接口
/**
 * 這個異常解析器會在遇到異常的時候
 * 會被處理器適配器來調(diào)用
 * Author menglanyingfei
 * Created on 2018.01.25 16:40
 */
public class MyExceptionResolver implements HandlerExceptionResolver {
    /*
        判斷異常的信息: 如果是我們自定義的異常就直接獲取異常信息
        然后跳轉(zhuǎn)到錯誤頁面:
        如果不是自定義異常, 那么給出一個未知錯誤的提示
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        ModelAndView mv = new ModelAndView();
        MyException me = null;
        // 判斷捕獲到的異常是否為自定義異常
        if (e instanceof MyException) {
            // 將異常強轉(zhuǎn)為MyException類型
            me = (MyException) e;
            // 獲取異常信息
            String errorMessage = me.getMessage();
            mv.addObject("errorMessage", errorMessage);

        } else {
            mv.addObject("errorMessage", "未知錯誤, 請于管理員聯(lián)系!");
        }
        // 這里配置了視圖解析器
        mv.setViewName("/error");
        return mv;
    }
}

3.在xml文件中配置異常解析器:

    <!-- 配置異常解析器 -->
    <bean class="com.wtu.ssm.exception.MyExceptionResolver"/>

錯誤頁面error.jsp:

<%--
  User: menglanyingfei
  Date: 2018/1/25
  Time: 16:50
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <title>錯誤提示</title>
</head>
<body>
    ${errorMessage}
</body>
</html>

關(guān)于項目中異常處理注意的問題:

  1. 如果使用springmvc Mapper的異常拋給service ,service的異常拋給controller
  2. 開發(fā)階段不需要將異常處理掉
  3. 在controller也是將異常給拋出去,那么會由處理器適配器捕獲異常調(diào)用我們自己編寫的異常解析器進(jìn)行異常的處理。

完整代碼地址

https://github.com/menglanyingfei/SSMLearning/tree/master/jar%E5%8C%85
https://github.com/menglanyingfei/SSMLearning/tree/master/SSM

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內(nèi)容