3. Web 组件

空~2022年9月6日
  • SpringBoot
大约 4 分钟

3. Web 组件

拦截器, Servlet , Filter

拦截器

拦截器是 SpringMVC 中一种对象, 能拦截器对 Controller 的请求.

拦截器框架中有系统的拦截器, 还可以自定义拦截器, 实现对请求预先处理.

SpringMVC 拦截器:

  1. 创建类实现 SpringMVC 框架的 HandlerInterceptor 接口

    public interface HandlerInterceptor {
        default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
         return true;
        }
    
        default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        }
    
        default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        }
    }
    
  2. 需在 SpringMVC 的配置文件中, 声明拦截器

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:path="url" />
            <bean class="拦截器类全限定名称"/>
        </mvc:interceptor>
    </mvc:interceptors>
    

SpringBoot 中注册拦截器:

需要实现 WebMvcConfigurer 接口, 该接口集成了 MVC 中 xml 配置文件的相关配置

WebMvcConfigurer 源码

public interface WebMvcConfigurer {
    default void configurePathMatch(PathMatchConfigurer configurer) {}
    default void configureContentNegotiation(ContentNegotiationConfigurer configurer) {}
    default void configureAsyncSupport(AsyncSupportConfigurer configurer) {}
    default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {}
    default void addFormatters(FormatterRegistry registry) {}
    default void addInterceptors(InterceptorRegistry registry) {}
    default void addResourceHandlers(ResourceHandlerRegistry registry) {}
    default void addCorsMappings(CorsRegistry registry) {}
    default void addViewControllers(ViewControllerRegistry registry) {}
    default void configureViewResolvers(ViewResolverRegistry registry) {}
    default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {}
    default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) {}
    default void configureMessageConverters(List<HttpMessageConverter<?>> converters) {}
    default void extendMessageConverters(List<HttpMessageConverter<?>> converters) {}
    default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {}
    default void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {}
    @Nullable
    default Validator getValidator() {
        return null;
    }
    @Nullable
    default MessageCodesResolver getMessageCodesResolver() {
        return null;
    }
}

编写一个拦截器类

public class LoginHandler implements HandlerInterceptor {
    /**
     * @param request
     * @param response
     * @param handler  被拦截的控制器对象
     * @return boolean
     * true: 请求能被controller处理
     * false: 请求被截断
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("LoginHandler.preHandle");
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
}

在配置类实现 WebMvcConfigurer 接口

@Configuration
public class MyAppConfig implements WebMvcConfigurer {

    //添加拦截器对象,  注入到容器中
    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        //创建拦截器对象, 也可以使用注解将该类交给 spring 容器管理
        HandlerInterceptor interceptor = new LoginHandler();

        //指定拦截的请求uri地址
        String path []= {"/user/**"};
        //指定不拦截的地址
        String excludePath  [] = {"/user/login"};
        registry.addInterceptor(interceptor)
                .addPathPatterns(path)
                .excludePathPatterns(excludePath);
    }
}

在 controller 层测试访问

@Controller
public class HelloController {
    @RequestMapping("/user/account")
    @ResponseBody
    public String userAccount() {
        return "userAccount"; // 无打印输出
    }

    @RequestMapping("/user/login")
    @ResponseBody
    public String userLogin() {
        return "userLogin"; // 打印输出 "LoginHandler.preHandle"
    }
}

Servlet

在 SpringBoot 框架中使用 Servlet 对象.

使用步骤:

  1. 创建 Servlet 类, 创建类继承 HttpServlet
  2. 注册 Servlet, 让框架能找到 Servlet
  1. 创建自定义 Servlet

    //创建Servlet类
    public class MyServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // 在doGet() 方法中调用了doPost() 方法, get和post操作结果一样, 仅方便测试
            doPost(req,resp);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //使用HttpServletResponse输出数据, 应答结果
            resp.setContentType("text/html;charset=utf-8");
            PrintWriter out  = resp.getWriter();
            out.println("===执行的是Servlet==");
            out.flush();
            out.close();
        }
    }
    
  2. 注册 Servlet

    @Configuration
    public class WebApplictionConfig {
    
        // 定义方法,  注册Servlet对象
        @Bean
        public ServletRegistrationBean<MyServlet> servletRegistrationBean() {
            /*
            public ServletRegistrationBean(T servlet, String... urlMappings)
            第一个参数是 Servlet对象,  第二个是url地址
    
            ServletRegistrationBean bean =
            new ServletRegistrationBean( new MyServlet(),"/myservlet");
            */
    
            ServletRegistrationBean<MyServlet> bean = new ServletRegistrationBean<>();
            bean.setServlet(new MyServlet());
            bean.addUrlMappings("/login", "/test"); // <url-pattern>
    
            return bean;
        }
    
  3. 访问测试

    image-20220919175244076

过滤器 Filter

Filter 是 Servlet 规范中的过滤器, 可以处理请求, 对请求的参数, 属性进行调整, 常常在过滤器中处理字符编码.

在框架中使用过滤器:

  1. 创建自定义过滤器类
  2. 注册 Filter 过滤器对象

例子:

// 自定义过滤器
public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("执行了MyFilter, doFilter");
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

注册 Filter

@Configuration
public class WebApplicationConfig {

    @Bean
    public FilterRegistrationBean<MyFilter> filterRegistrationBean(){
        FilterRegistrationBean<MyFilter> bean  = new FilterRegistrationBean<>();
        bean.setFilter( new MyFilter());
        bean.addUrlPatterns("/user/*");
        return bean;
    }
}

在 controller 中访问测试

@Controller
public class HelloController {
    @RequestMapping("/user/account")
    @ResponseBody
    public String userAccount() {
        return "user account";
    }
}

运行结果

image-20220919175933378

过滤器和之前的拦截器都生效了, 且过滤器在前

image-20220919180042987

字符集过滤器

CharacterEncodingFilter: 解决 post 请求中乱码的问题

如果在 servlet 中不指定 HttpServletResponse 的响应的字符集, 则默认为 ISO-8859-1.

public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 在doGet() 方法中调用了doPost() 方法, get和post操作结果一样, 仅方便测试
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 使用HttpServletResponse输出数据, 应答结果
       resp.setContentType("text/html"); //  不指定字符集  ;charset=utf-8
        PrintWriter out = resp.getWriter();
        out.println("===执行的是Servlet==");
        out.flush();
        out.close();
    }
}

image-20220919182317629

image-20220919182358521

在 SpringMVC 框架, 在 web.xml 注册过滤器. 配置他的属性.

第一种方式:

  1. 配置字符集过滤器

    @Configuration
    public class WebSystemConfig {
    
        // 注册Servlet
        @Bean
        public ServletRegistrationBean servletRegistrationBean(){
            MyServlet myServlet = new MyServlet();
            ServletRegistrationBean reg = new ServletRegistrationBean(myServlet,"/myservlet");
            return reg;
        }
    
        // 注册Filter
        @Bean
        public FilterRegistrationBean<CharacterEncodingFilter> filterRegistrationBean() {
            FilterRegistrationBean<CharacterEncodingFilter> reg = new FilterRegistrationBean<>();
    
            // 使用框架中的过滤器类
            CharacterEncodingFilter filter = new CharacterEncodingFilter();
            // 指定使用的编码方式
            filter.setEncoding("utf-8");
            // 指定request,  response都使用encoding的值
            filter.setForceEncoding(true);
    
            reg.setFilter(filter);
            // 指定 过滤的url地址
            reg.addUrlPatterns("/*");
            return reg;
        }
    }
    
  2. 修改 application.properties 文件, 让自定义的过滤器起作用

    #SpringBoot中默认已经配置了CharacterEncodingFilter. 编码默认ISO-8859-1
    #设置enabled=false 作用是关闭系统中配置好的过滤器, 使用自定义的CharacterEncodingFilter
    server.servlet.encoding.enabled=false
    

第二种方式:

修改 application.properties 文件

#让系统的CharacterEncodingFilter生效
server.servlet.encoding.enabled=true
#指定使用的编码方式
server.servlet.encoding.charset=utf-8
#强制request, response都使用charset属性的值
server.servlet.encoding.force=true