静态资源和拦截器处理

1.默认资源映射

Spring Boot 默认为我们提供了静态资源处理,使用 WebMvcAutoConfiguration 中的配置各种属性。
建议大家使用Spring Boot的默认配置方式,提供的静态资源映射如下:

  • classpath:/META-INF/resources
  • classpath:/resources
  • classpath:/static
  • classpath:/public

sprngboot6-1.jpg          

优先级顺序为:META-INF/resources > resources > static > public
还有,你可以随机在上面一个路径下面放上index.html,当我们访问应用根目录http://lcoalhost:8080 时,会直接映射到index.html页面。 

对应的配置文件配置如下

# 默认值为 /**
spring.mvc.static-path-pattern=
# 默认值为 classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
spring.resources.static-locations=这里设置要指向的路径,多个使用英文逗号隔开

我们可以通过修改spring.mvc.static-path-pattern来修改默认的映射,例如我改成/cxy/**,那运行的时候访问 http://localhost:8080/cxy/index.html 才对应到index.html页面。

在Springboot中可以直接在配置文件中覆盖默认的静态资源路径的配置信息,来添加我们设置的磁盘某个目录:

cxy.picpath属于自定义的属性,指定了一个路径,注意要以/结尾;

spring.mvc.static-path-pattern=/ 表示所有的访问都经过静态资源路径;

spring.resources.static-locations 在这里配置静态资源路径,前面说了这里的配置是覆盖默认配置,所以需要将默认的也加上否则static、public等这些路径将不能被当作静态资源路径,在这个最末尾的 file:${cxy.picpath} 
加 file :是因为指定的是一个具体的硬盘路径,其他的使用classpath指的是系统环境变量。

#  params
cxy.picpath=D:/demo-images/
# springboot 之静态资源路径配置
spring.mvc.static-path-pattern=/**
spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,file:${cxy.picpath}

图片或静态资源直接放在cxy.picpath=D:/demo-images/目录下,访问:http://127.0.0.1:8077/0.jpg,会报错: 

集成shiro时,shiro对contextPath/后面的第一层path访问时,对标点“.”进行了截取,实际请求变成了:http://127.0.0.1:8077/0 , 交给dispatcherServlet处理,没有找到匹配的view视图“0”,就报错。

 建议:在demo-images下,根据时间创建文件夹,可以防止因整合shrio 出现的不能访问的问题。

添加一层或多层目录之后,springboot会在静态资源配置中依次找到匹配的目录,然后加载静态资源;

重点:默认是根据application.xml文件的静态资源路径配置查找图片等静态资源

2.配置类进行页面跳转addViewControllers

package com.example.demo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

@Configuration

    @param registry

public class MyMvcConfig extends WebMvcConfigurationSupport {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/tologin").setViewName("login");
        super.addViewControllers(registry);
    }
}

但是会和默认的index.冲突了,也就是会导致mvc自动配置失效,不能再http://localhost:8080/来访问index.html了,还有比如访问不到静态资源(js,css等)了,这是因为WebMvc的自动配置都在WebMvcAutoConfiguration类中,但是类中有这个注解@ConditionalOnMissingBean({WebMvcConfigurationSupport.class}),意思是一旦在容器中检测到WebMvcConfigurationSupport这个类,WebMvcAutoConfiguration类中的配置都不生效。所以一旦我们自己写的配置类继承了WebMvcConfigurationSupport,相当于容器中已经有了WebMvcConfigurationSupport,所以默认配置都不会生效,都得自己在配置文件中配置。

所以使用官方推荐的,直接实现WebMvcConfigurer接口,这个接口的方法都加了jdk1.8的 default方法修饰,不强制实现所有的方法,可以根据实际实现相关的方法。 (官方推荐)

package com.example.demo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration

    //@param registry
/*
    addViewControllers可以方便的实现一个请求直接映射成视图,而无需书写controller
    registry.addViewController("请求路径").setViewName("请求页面文件路径")
     */

public class MyMvcConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/toLogin").setViewName("login");
        
    }
}

搞定了,没有冲突了。

3.拦截器 Interceptor

3.1 概念:在AOP(Aspect-Oriented Programming,面向切面编程)中拦截器用于在某个方法或字段被访问之前进行拦截,然后在之前或之后加入某些操作。

3.2 原理

  拦截器 Interceptor 的拦截功能是基于 Java 的动态代理来实现的,只需要关心如何重写方法,而不需要关心其内部的实现原理。了解原理

3.3实现方法

  • 第一种是实现HandlerInterceptor接口,
  • 配置类里其实重写addInterceptors方法把自定义的拦截器类添加进来即可

3.3.1实现HandlerInterceptor接口,

HandlerInterceptor接口中,定义了 3 个方法,分别为preHandle()、postHandle()和afterCompletion(),咱们就是通过重写这 3 个方法来对用户的请求进行拦截处理的。因此,咱们可以通过直接实现HandlerInterceptor接口来实现拦截器的功能。不过在 Spring 框架之中,其还提供了另外一个接口和一个抽象类,实现了对HandlerInterceptor接口的功能扩展,分别为:AsyncHandlerInterceptor和HandlerInterceptorAdapter.

对于AsyncHandlerInterceptor接口,其在继承HandlerInterceptor接口的同时,又声明了一个新的方法afterConcurrentHandlingStarted();而HandlerInterceptorAdapter抽象类,则是更进一步,在其继承AsyncHandlerInterceptor接口的同时,又复写了preHandle方法。因此,AsyncHandlerInterceptor更像是一个过渡的接口。

在实际应用中,咱们一般都是通过实现HandlerInterceptor接口或者继承HandlerInterceptorAdapter抽象类重写preHandle()、postHandle()和afterCompletion()这 3 个方法来对用户的请求进行拦截处理的。下面,咱们就详细介绍这个 3 个方法。

  • preHandle(HttpServletRequest request, HttpServletResponse response, Object handle)方法,该方法在请求处理之前进行调用。SpringMVC 中的 Interceptor 是链式调用的,在一个应用中或者说是在一个请求中可以同时存在多个 Interceptor 。每个 Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是 Interceptor 中的 preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求做一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值 Boolean 类型的,当它返回为 false 时,表示请求结束,后续的 Interceptor 和 Controller 都不会再执行;当返回值为 true 时,就会继续调用下一个 Interceptor 的 preHandle 方法,如果已经是最后一个 Interceptor 的时候,就会是调用当前请求的 Controller 中的方法。
  • postHandle(HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView)方法,通过 preHandle 方法的解释咱们知道这个方法包括后面要说到的 afterCompletion 方法都只能在当前所属的 Interceptor 的 preHandle 方法的返回值为 true 的时候,才能被调用。postHandle 方法在当前请求进行处理之后,也就是在 Controller 中的方法调用之后执行,但是它会在 DispatcherServlet 进行视图返回渲染之前被调用,所以咱们可以在这个方法中对 Controller 处理之后的 ModelAndView 对象进行操作。postHandle 方法被调用的方向跟 preHandle 是相反的,也就是说,先声明的 Interceptor 的 postHandle 方法反而会后执行。
  • afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)方法,也是需要当前对应的 Interceptor 的 preHandle 方法的返回值为 true 时才会执行。因此,该方法将在整个请求结束之后,也就是在 DispatcherServlet 渲染了对应的视图之后执行,这个方法的主要作用是用于进行资源清理的工作。

拦截器在我们项目中经常使用的,这里就来介绍下最简单的判断是否登录的使用。

2个步骤:

  • 创建我们自己的拦截器类并实现 HandlerInterceptor 接口
  • 其实重写addInterceptors方法把自定义的拦截器类添加进来即可

首先,自定义拦截器代码:

package com.example.demo.config;


import com.example.demo.entity.User;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        boolean flag =true;
        User user=(User)request.getSession().getAttribute("user");
        if(null==user){
            response.sendRedirect("toLogin");
            flag = false;
        }else{
            flag = true;
        }
        return flag;
    }

    @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 {
    }
}

 MyMvcConfig重写addViewControllers

@Override
      public void addInterceptors(InterceptorRegistry registry){
            registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/toLogin","/login");
           
    }

这时,登录的样式没有了,spring boot 2.0则对静态资源也进行了拦截,当拦截器拦截到请求之后,但controller里并没有对应的请求时,该请求会被当成是对静态资源的请求。此时的handler就是 ResourceHttpRequestHandler,就会抛出上述错误。
解决方案:
解决办法就是,在拦截器那里排除静态资源的请求路径

registry.addInterceptor(myInterceptor).addPathPatterns("/**").excludePathPatterns("/toLogin","/login","/asserts/**","/test");

页面处理

//简单登录操作
$("#doLogin").click(function (e) {
    $.ajax({
    type : "POST",
    url : "/login",
    data : {
        "userName" : $("#userName").val(),
        "password" : $("#password").val()
    },
    dataType : "json",
    success : function(data) {
        if (data.result == "1") {
        window.location.href ="/learn";
        } else {
        alert("账号密码不能为空!");
        }
    }
    });
});

把判断代码放在Controller是不得已而为之,以后看看有没有别的好方法吧

/**
    *登录操作
    **/
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    @ResponseBody
    public Map<String,Object> login(HttpServletRequest request, HttpServletResponse response){
        Map<String,Object> map =new HashMap<String,Object>();
        String userName=request.getParameter("userName");
        String password=request.getParameter("password");
        if(!userName.equals("") && password!=""){
            User user =new User(userName,password);
            request.getSession().setAttribute("user",user);
            map.put("result","1");
        }else{
            map.put("result","0");
        }
        return map;
    }

猜你喜欢

转载自blog.csdn.net/qq_38930240/article/details/83543618