11 Spring MVC 访问入参获取和拦截器使用(@RequestParam 和 Interceptors)登录模块demo

转载请注明来源 赖赖的博客

导语

核心思想明了,骨架已经建立,就可以一点一点的丰满细节,反哺骨架和核心思想

前面介绍了Spring MVC 是怎么接受一个访问的,想必你已经明白了基本的访问过程,作为一个应用系统,登录模块都必不可少,不妨今天就来做一个简单的登录模块,顺便熟悉一下Spring MVC的访问入参获取和拦截器的使用

实例

项目工程目录结构和代码获取地址

获取地址(版本Log将会注明每一个版本对应的课程)

https://github.com/laiyijie/SpringLearning

目录结构

目录结构

如图所示,虽然目录结构看起来复杂了一些(就是文件夹深度深了一点儿而已),其实最源文件只有五个。所以不要紧张,这非常简单,而且相比上一课,只多出了一个文件,和少量的配置

运行工程

运行方式
  • 右键整个项目
  • Run as
  • Run On Server
  • 在浏览器里依次输入 (demo是工程的名称,依照你的来替换)
    • http://localhost:8080/demo/user/hi
    • http://127.0.0.1:8080/demo/user/login?username=laiyijie&password=lailai
    • http://127.0.0.1:8080/demo/user/hi
运行结果
1. /user/hi结果
  • http://127.0.0.1:8080/demo/user/login?username=laiyijie&password=lailai
2. /user/login结果
  • http://127.0.0.1:8080/demo/user/hi
3. 第二次/user/hi结果

第一次和最后一次的输入是完全一样的确实完全不同的两个结果?

由于第一次没有登录,然后返回的是403权限不足,第二次是登录以后访问这个链接,可以正常返回。

项目详解

依照上一章的知识,访问会被引流到由@RequestMapping注解的函数执行,我们不妨直接找到UserController这个文件,访问是由这个控制器进行处理的

UserController.java(与上一课有变化)

package me.laiyijie.demo.controller;

import org.springframework.stereotype.Controller;
import javax.servlet.http.HttpSession;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/hi")
    @ResponseBody
    public String hello(@RequestParam(value = "name", defaultValue = "world") String name) {
        return "hello " + name;
    }

    @RequestMapping("/login")
    @ResponseBody
    public String login(@RequestParam("username") String username, @RequestParam("password") String password,
            HttpSession httpsession) {
        if ("laiyijie".equals(username) && "lailai".equals(password)) {
            httpsession.setAttribute("username", username);
            return "login success";
        }
        return "loging failed";
    }

}  

代码有几处变化:

  • 在类前面多了一个@RequestMapping注解,根据前面的执行结果可以知道,如果@RequestMapping注解应用到类上,那么这个类中的所有方法的URL都增加这个注解的前缀,在这个例子中,都增加了 /user,所以原来的/hi变成了/user/hi
  • 在方法参数中增加了由@RequestParam标注的入参,该参数就是映射入参
    • 在第二次访问的时候我们使用的URL为127.0.0.1/demo/user/login?username=laiyijie&password=lailai 而其对应的函数声明有如下两个参数@RequestParam("username") String username, @RequestParam("password") String password
    • 显而易见的,@RequestParam获取了问号后面的键值对参数
    • hi这个函数中我们这样使用@RequestParam(value = "name", defaultValue = "world") String name 设置了一个默认值,这样的话即使我们不传这个参数也会返回 hello world(例如第三次访问)
  • login函数中新增了一个HttpSession的入参
    • 首先,这个入参从何而来?这是SpringMVC在使用DispatcherServlet分配URL对应控制器的时候会根据函数声明自动注入的一个参数,相应的还可以参考官方文档
    • 作用很简单就是获取当前的HttpSession从而可以操作Session中的属性(Session在这个例子中是为了保存登录状态)
  • login函数中,在用户名和密码正确以后除了会返回login success以外还会设置一个名为usernameSession属性用来证明登录成功

那么问题来了,在第一次访问/uesr/hi的时候是怎么会返回403错误的呢?

这就要引入Spring MVC的一个重要组件,拦截器(Interceptor)

LoginInterceptor.java(新增)

package me.laiyijie.demo.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        String username = (String) request.getSession().getAttribute("username");
        if (username != null) {
            return true;
        }else{
            response.sendError(403);
            return false;
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {

    }

}

不用看这么多代码,其实就两点:

  • 实现了HandlerInterceptor接口
  • 写了preHandle方法(此方法用于在分配给Controller之前执行,当同意继续访问的时候返回true否则返回false
    • 首先获取了Sessionusername属性
      • String username = (String) request.getSession().getAttribute("username");
    • 如果这个属性设置了(代表处于登录状态)返回true继续执行
      • return true;
    • 如果这个属性没有设置(代表没有登录)设置返回403错误并且返回false
      • response.sendError(403);
      • return false;

是不是很简单?看了这个接口其实就可以明白拦截器处于的位置:

  • /demo/user/hi访问被/demo接受
  • 如果定义了拦截器则进入拦截器处理(preHandel方法)
  • 返回true则继续分发给/user/hi对应的方法hi否则返回错误

那么又有一个新问题来了,/user/login为什么没有被拦截?为什么没有返回403错误?

那就要看Interceptor的配置了:

servlet-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">


    <context:component-scan base-package="me.laiyijie.demo" />

    <mvc:annotation-driven />

    <bean
        class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"
        p:messageConverters-ref="stringHttpMessageConverter" />

    <bean id="stringHttpMessageConverter"
        class="org.springframework.http.converter.StringHttpMessageConverter" />

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**" />
            <mvc:exclude-mapping path="/user/login" />
            <bean class="me.laiyijie.demo.controller.LoginInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

</beans>

拦截器配置如下(相比上节课新增):

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**" />
        <mvc:exclude-mapping path="/user/login" />
        <bean class="me.laiyijie.demo.controller.LoginInterceptor"></bean>
    </mvc:interceptor>
</mvc:interceptors>
  • 首先配置了一个interceptors也就是拦截器组(对,里面可以有很多个拦截器)
  • 然后配置了里面的其中一个interceptor
    • 拦截所有进入/demo的访问
      • <mvc:mapping path="/**" />
      • 为何有两个*?因为一个*不可以代表/xx/xx只能代表/xx
    • 排除不需要拦截的路径(原来在这里排除了/user/login
      • <mvc:exclude-mapping path="/user/login" />
    • 设置对应的实现类

简单的两个功能实现了一个简单的登录模块。

小结

  • @RequstMapping可以在类前注释,代表这个类中所有处理函数都要带其标注的一个前缀
  • @RequestParamController处理函数的入参前注释,可以将例如?username=laiyijie&password=lailai这样的参数绑定到对应的入参中
  • @RequestParam可以设置默认值,这样即使访问不带这个参数也可以有一个默认的输入,使用方式是@RequestParam(defaultValue="xxx")
  • Controller中的处理函数可以增加HttpSession的入参,由DispatcherServlet自动注入,还有更多的属性可以参考官方文档
  • 拦截器通过继承HandlerInterceptor接口,并使用<mvc:interceptor>配置实现
  • 拦截器有三个钩子,其中一个是在访问进入Controller之前处理(preHandle),处理结果为false可以阻止其进入Controller中处理
  • 拦截器可以配置其拦截的路径(<mvc:mapping path="/**" />
  • 拦截器可以排除一些特殊的路径不进行拦截(<mvc:exclude-mapping path="/user/login" />

附:

pom.xml(新增一个依赖)

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>me.laiyijie</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
    </properties>

    <dependencies>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.3.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

新增一个依赖javax.servlet-api作用于为provided,由容器提供

web.xml(无变化)

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee">

    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
</web-app>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,497评论 18 139
  • 1、Spring MVC请求流程 (1)初始化:(对DispatcherServlet和ContextLoderL...
    拾壹北阅读 1,941评论 0 12
  • 本文包括:1、如何在 Struts2 中使用 Servlet 的相关 API?2、分析 <result> 结果页面...
    廖少少阅读 888评论 3 12
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,679评论 6 342
  • 你有没有想过从记忆中到现在,什么事情是你持续不间断过的事?这个问题我猛的一下想到就是好像没有什么事是每天都做...
    郑珍容阅读 82评论 0 0