美文网首页JavaJava&Spring基础技术Spring Step By Step
11 Spring MVC 访问入参获取和拦截器使用(@Requ

11 Spring MVC 访问入参获取和拦截器使用(@Requ

作者: 赖赖oO | 来源:发表于2016-12-12 20:58 被阅读670次

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

    导语

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

    前面介绍了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>

    相关文章

      网友评论

      本文标题:11 Spring MVC 访问入参获取和拦截器使用(@Requ

      本文链接:https://www.haomeiwen.com/subject/cxcvmttx.html