WebMvcConfigurer 和 WebMvcConfigurationSupport

一、前言 

WebMvcConfigurer 是一个接口,用于配置全局的SpringMVC的相关属性,采用JAVABean的方式来代替传统的XML配置文件,提供了跨域设置、静态资源处理器、类型转化器、自定义拦截器、页面跳转等能力。

WebMvcConfigurationSupport是webmvc的配置类,如果在springboot项目中,有配置类继承了WebMvcConfigurationSupport,那么webmvc的自动配置类WebMvcAutoConfiguration就会失效。

官方推荐直接实现WebMvcConfigurer 或者 继承WebMvcConfigurationSupport,

方式一:实现WebMvcConfigurer接口(推荐),方式二:继承WebMvcConfigurationSupport(/səˈpɔːt/)类。 

二、WebMvcConfigurer 

package com.syh.pdd.config.web;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.*;

import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    // 用户头像
    @Value("${file.userImage.writePath}")
    private String userImageWritePath;
    @Value("${file.userImage.readPath}")
    private String userImageReadPath;

    /**
     * 映射文件路径配置
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");

        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");

        registry.addResourceHandler("BJTPReadpath")
                .addResourceLocations("file:"+ "BJTPSavepath");


                // 图片回显路径,“/**”是当前文件夹以及子文件夹
        registry.addResourceHandler(userImageReadPath)
                // 图片存放路径
                .addResourceLocations("file:" + userImageWritePath);
    }

    /**
     * 跨域配置添加
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 设置允许跨域的路径
        registry.addMapping("/**")
                // 设置允许跨域请求的域名
              //  .allowedOrigins("*")
                .allowedOriginPatterns("*")
                // 是否允许证书 不再默认开启
                .allowCredentials(true)
                // 设置允许的方法
                .allowedMethods("*")
                // 跨域允许时间
                .maxAge(3600);
    }

    //解决中文乱码问题
    @Override
    public void configureMessageConverters(List> converters) {
        //解决中文乱码
        converters.add(responseBodyConverter());
        //解决 添加解决中文乱码后 上述配置之后,返回json数据直接报错 500:no convertter for return value of type
        converters.add(messageConverter());
    }
    @Bean
    public HttpMessageConverter responseBodyConverter(){
        StringHttpMessageConverter converter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
        return converter;
    }
    @Bean
    public MappingJackson2HttpMessageConverter messageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setObjectMapper(getObjectMapper());
        return converter;
    }
    @Bean
    public ObjectMapper getObjectMapper() {
        return new ObjectMapper();
    }

    /**
     *  格式化返回的内容
     *  https://my.oschina.net/u/3681868/blog/3075150
     * */
    @Override
    public void extendMessageConverters(List> converters) {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = converter.getObjectMapper();
        // 时间格式化
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        // 设置格式化内容
        converter.setObjectMapper(objectMapper);
        converters.add(0, converter);
    }

    /**
     * 添加Web项目的拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //放行路径
        List jwtExcludePatterns = new ArrayList();
        // 登录接口放行
        jwtExcludePatterns.add("/api/user/login");
        // 验证码放行
        jwtExcludePatterns.add("/api/user/getVerify/**");
        // 前端更换头像请求,没有走拦截器,此处放行
        jwtExcludePatterns.add("/api/user/updatePicture");
        // 对所有api开头的访问路径,都通过MyInterceptor拦截器进行拦截,MyInterceptor代码在下面
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/api/**")
                .excludePathPatterns(jwtExcludePatterns);
    }

    /*解析器  使用方法在文章末尾*/
    @Override
    public void addArgumentResolvers(List resolvers) {
        WebMvcConfigurer.super.addArgumentResolvers(resolvers);
    }
}

1、自定义拦截器

什么是拦截器:在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。拦截是AOP的一种实现策略

为什么需要拦截器:在做身份认证或者是进行日志的记录时,我们需要通过拦截器达到我们的目的。最常用的登录拦截、或是权限校验、或是防重复提交、或是根据业务像12306去校验购票时间,总之可以去做很多的事情

        
            net.minidev
            json-smart
        
        
            com.vaadin.external.google
            android-json
            0.0.20131108.vaadin1
            compile
        
package com.syh.pdd.config.web;

import com.syh.pdd.Utils.token.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import net.minidev.json.JSONObject;
import org.springframework.http.HttpMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 自定义拦截器类token认证
 * 注意:可以实现多个拦截器,只需要继续实现HandlerInterceptor即可            
 */
@Slf4j
public class MyInterceptor implements HandlerInterceptor {
    /**
     * 访问控制器方法前执行
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        // ==========!!!!!!注意注意注意   注意注意   注意注意   ========================
        /*
         * 前端在请求的时候会发送一个OPTION请求来验证本次请求是否安全,
         * 但是springboot的拦截器会拦截所有请求。因为第一次是OPTION没有携带JWT,所以验证失败
         * */
        if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
            return true;
        }
        // 获取token
        String token = request.getHeader("token");
        // 校验token
        if (JwtUtil.checkToken(token)) {
            log.info(request.getRequestURL() + ">>>" +
                    new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "通过token验证");
            return true; // 放行
        } else{
            //设置response状态
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            //设置失败响应数据
            JSONObject res = new JSONObject();
            res.put("status","101010");
            res.put("msg","登录过期,请重新登录");
            PrintWriter out = null ;
            out = response.getWriter();
            out.write(res.toString());
            out.flush();
            out.close();
            return false; // 拦截
        }
    }

    /**
     * 访问控制器方法后执行
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
    }

    /**
     * postHandle方法执行完成后执行,一般用于释放资源
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    }
}

/**
 * Web配置类
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {
    /**
     * 添加Web项目的拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //放行路径
        List jwtExcludePatterns = new ArrayList();
        // 登录接口放行
        jwtExcludePatterns.add("/system/user/login");
        // 验证码放行
        jwtExcludePatterns.add("/system/user/getVerify/**");
        // 对所有图片资源放行
        jwtExcludePatterns.add("/Project/saveFile/**");

        // 拦截路径:对所有访问路径,都通过MyInterceptor类型的拦截器进行拦截
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**")
                .excludePathPatterns(jwtExcludePatterns);

    }
}

三、WebMvcConfigurationSupport

package com.hssmart.config.web;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.List;

@Configuration
public class WebConfig extends WebMvcConfigurationSupport{

    // 自定义一个拦截器
    @Autowired
    UserArgumentResolver userArgumentResolver;


    /**
     * 映射文件路径配置
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {

        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");

        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");


        /**
         * 说明:增加虚拟路径(经过本人测试:在此处配置的虚拟路径,用springboot内置的tomcat时有效,
         用外部的tomcat也有效;所以用到外部的tomcat时不需在tomcat/config下的相应文件配置虚拟路径了,阿里云linux也没问题)
         */
        //registry.addResourceHandler("/pic/**").addResourceLocations("file:E:/pic/");



        registry.addResourceHandler("BJTPReadpath")
                .addResourceLocations("file:"+ "BJTPSavepath");


        //阿里云(映射路径去除盘符)
        //registry.addResourceHandler("/ueditor/image/**").addResourceLocations("/upload/image/");
        //registry.addResourceHandler("/ueditor/video/**").addResourceLocations("/upload/video/");

        //用户图片路径
        registry.addResourceHandler("/Project/saveFile/user/userImg/**"(网络路径,其实可以任意定义))
                .addResourceLocations("file:D:/JAVA/Project/saveFile/user/userImg/"(储存路径));

        super.addResourceHandlers(registry);
    }


    /**
     * 跨域配置添加
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 设置允许跨域的路径
        registry.addMapping("/**")
                // 设置允许跨域请求的域名
                .allowedOrigins("*")
                // 是否允许证书 不再默认开启
                .allowCredentials(true)
                // 设置允许的方法 springboot较高版本不能使用*号
                // .allowedMethods("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH")
                .allowedMethods("*")
                // 跨域允许时间
                .maxAge(3600);
    }


    //解决中文乱码问题
    @Override
    public void configureMessageConverters(List> converters) {
        super.configureMessageConverters(converters);
        //解决中文乱码
        converters.add(responseBodyConverter());
        //解决 添加解决中文乱码后 上述配置之后,返回json数据直接报错 500:no convertter for return value of type
        converters.add(messageConverter());
    }


    @Bean
    public HttpMessageConverter responseBodyConverter(){
        StringHttpMessageConverter converter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
        return converter;
    }
    @Bean
    public MappingJackson2HttpMessageConverter messageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setObjectMapper(getObjectMapper());
        return converter;
    }
    @Bean
    public ObjectMapper getObjectMapper() {
        return new ObjectMapper();
    }


    /**
     *  格式化返回的内容(格式转换器)
     *  https://my.oschina.net/u/3681868/blog/3075150
     * */
    @Override
    public void extendMessageConverters(List> converters) {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = converter.getObjectMapper();
        // 生成JSON时,将所有Long转换成String
        //SimpleModule simpleModule = new SimpleModule();
        //simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        //simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        //objectMapper.registerModule(simpleModule);
        // 时间格式化
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        // 设置格式化内容
        converter.setObjectMapper(objectMapper);
        converters.add(0, converter);
    }

    /**
     * 解析器,该方法可实现可不实现,需要自定义。作用在调用Controller方法的参数传入之前,有返回值
     */
    @Override
    public void addArgumentResolvers(List resolvers) {
        // userArgumentResolver该类对Controller传入的参数做了具体处理
        resolvers.add(userArgumentResolver);
    }


}

1、解析器

(1)自定义解析器

@Configuration
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
 
    @Autowired
    Userservice userservice;
 
    // 此方法返回true,下面的参数才会执行
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        // 获取传入参数的类型
        Class type = methodParameter.getParameterType();
        // 如果参数类型有为User类的则符合,进入resolveArgument方法
        if (UserPojo.class == type) {
            return true;
        }
        return false;
    }
 
    // 该方法为拦截方法,将结果返回给controller
    @Override
    public Object resolveArgument(MethodParameter methodParameter,
                                  ModelAndViewContainer modelAndViewContainer,
                                  NativeWebRequest nativeWebRequest,
                                  WebDataBinderFactory webDataBinderFactory) throws Exception {
 
        HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        HttpServletResponse response = nativeWebRequest.getNativeResponse(HttpServletResponse.class);
        
        String userTick = CookieUtil.getCookieValue(request, "userTicket");
        if (StringUtils.isBlank(userTick)) {
            return null;
        }
        UserPojo userPojo = userservice.getUserByCookie(userTick, request, response);
        if (userPojo == null) {
            return null;
        }
        return userPojo;
    }
}

(2)在controller中使用

@GetMapping("goods")
public Result showGoods(UserPojo user){
    // 注意:这里的User参数不是由前端传入的,而是由addArgumentResolvers方法处理之后传进来的
    log.info(user.toString());
    // 根据处理之后传入的参数判断是否登录
    if (user == null)
        return Result.error();
    return Result.ok();
}

你可能感兴趣的:(SSM框架,spring,boot,spring,java)