WebMvcConfigurer和WebMvcConfigurationSupport(MVC配置)

news2024/12/22 11:14:24

一:基本介绍

WebMvcConfigurer是接口,用于配置全局的SpringMVC的相关属性,采用JAVABean的方式来代替传统的XML配置文件,提供了跨域设置、静态资源处理器、类型转化器、自定义拦截器、页面跳转等能力。
WebMvcConfigurationSupport是webmvc的配置类,如果在springboot项目中,有配置类继承了WebMvcConfigurationSupport,那么webmvc的自动配置类WebMvcAutoConfiguration就会失效。

两者都是Spring MVC中的组件,具体的区别如下:

1.实现方式不同

WebMvcConfigurer:是一个接口,它提供了多个回调方法,可以用于自定义Spring MVC的配置(如消息转换器、拦截器等)。我们在使用时只需要实现该接口,重写其中的方法即可。
WebMvcConfigurationSupport:是一个抽象类,它也提供了多个回调方法,用于自定义Spring MVC的配置,但是需要继承该类并重写其中的方法。

2.作用不同

WebMvcConfigurer:
主要用于添加或修改Spring MVC的配置,如添加拦截器,自定义消息转换器等。
WebMvcConfigurationSupport:
主要用于完全自定义Spring MVC的配置,如果我们需要对Spring MVC的配置进行大量的自定义,可以选择继承该类并重写其中的方法。但是需要注意的是,继承该类会覆盖Spring MVC的部分默认配置。因此,当我们只需要对部分配置进行自定义时,应该使用WebMvcConfigurer。

3.继承关系不同

WebMvcConfigurer:
没有继承关系,我们只需要实现该接口即可使用。
WebMvcConfigurationSupport:
是一个抽象类,需要继承后才能使用。

4.总结

在日常开发中推荐优先使用WebMvcConfigurer的方式(官方推荐),因为简单方便,也没有特别复杂的定制需求;若我们项目中使用的MVC存在着更加复杂的配置需求推荐WebMvcConfigurationSupport,通过继承此类,我们可以说对官方的MVC代码进行重写操作,但是因为其配置量较大,实现比较复杂,因此在日常开发中使用WebMvcConfigurationSupport并不常见。

二:配置摘要

常用自定义配置如下:
1.addInterceptors(拦截器配置)
2.addCorsMappings(全局跨域处理)
3.addViewControllers(注册视图控制器)(可以让地址对应资源文件,如html文件)
4.addResourceHandlers(配置静态资源处理)(可以在客户端直接访问静态资源信息)

三:Spring和SpringBoot中配置步骤区别

1.Spring中配置WebMvcConfigurer方式:

①创建一个Java 类,实现WebMvcConfigurer接口

@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
    // 自定义配置代码
}

②注入到Bean容器里

@Configuration
public class AppConfig {
    @Bean
    public MyWebMvcConfig myWebMvcConfig() {
        return new MyWebMvcConfig();
    }
}
2.SpringBoot中配置WebMvcConfigurer方式:(这种方式简单)

创建一个 Java 类,并实现WebMvcConfigurer接口

@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
    // 自定义配置代码
}

四:拦截器(addInterceptors)

在SpringBoot中,可以使用拦截器来对请求进行统一的预处理或后处理。拦截器用于日志记录、权限检查、性能监控、事务控制等方面。SpringBoot中实现拦截器,首先要创建一个实现HandlerInterceptor接口的拦截器类。该接口定义了三个方法,分别是preHandle、postHandle和afterCompletion,用于在请求处理前、请求处理后和请求完成后进行处理。

1.HandlerInterceptor接口方法详解:

①preHandler
在请求处理之前被调用。该方法在Interceptor类中最先执行,用来进行一些前置初始化操作或是对当前请求做预处理,也可以进行一些判断来决定请求是否要继续进行下去。该方法的返回值是Boolean类型,当它返回false时,表示请求结束,后续的Interceptor和Controller都不会再执行;当它返回为true时会继续调用下一个Interceptor的preHandle方法,如果已经是最后一个Interceptor的时候就会调用当前请求的Controller方法。
②postHandler
在请求处理完成之后调用。也就是Controller方法调用之后执行,但是它会在DispatcherServlet进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView对象进行操作。
③afterCompletion
在整个请求结束后调用。就是对应的Interceptor类的postHandler方法返回true时才执行。就是说该方法将在整个请求结束之后,
也就是在DispatcherServlet渲染了对应的视图之后执行。此方法主要用来进行资源清理。
注:官方其实不建议我们非要把3个方法都重写,我们只要对需要的方法重写接口,就比如大部分项目只需要重写preHandler方法

2.InterceptorRegistry类方法介绍:

①addInterceptor
该方法用于向拦截器链中添加一个拦截器。interceptor参数为待添加的拦截器对象,可以是自定义的拦截器类或Spring提供的预置拦截器。返回值为InterceptorRegistration对象,用于进一步配置该拦截器的属性。
②addWebRequestInterceptor
该方法用于向WebRequest拦截器链中添加一个拦截器。interceptor参数为待添加的拦截器对象,可以是自定义的WebRequestInterceptor类或者Spring提供的预置拦截器。也是返回值为InterceptorRegistration对象,用于进一步配置该拦截器的属性。
③getInterceptors
用于获取当前已经添加到拦截器链中的所有拦截器,返回值为List<HandlerInterceptor>对象,表示拦截器列表。

3.InterceptorRegistration类方法介绍:

①order
该方法用于设置拦截器的执行顺序,即在拦截器链中的位置。order参数为一个整数,值越小表示越先执行。
②addPathPatterns
该方法用于设置需要拦截的请求路径模式,即满足哪些请求路径时才会触发该拦截器。若"/**"则拦截全部;传入的参数是一个字符串数组,包含多个Ant风格的路径模式,例如"/api/**""/user/*"等。
③excludePathPatterns
该方法用于设置不需要拦截的请求路径模式,即满足哪些请求路径时不会触发该拦截器。一般不拦截,如登录或者Swagger等传入的参数是一个字符串数组,包含多个Ant风格的路径模式,例如 "/api/login""/user/login"等。
④pathMatcher
该方法用于设置该拦截器所使用的PathMatcher实例,从而可以自定义路径匹配逻辑。

注:拦截的路径或者放行的路径是以Controller开始的,如我们在application.yml配置的地址前缀则不包含

五:跨域(addCorsMappings)

参考:SpringBoot解决跨域的三种方式

1.CorsRegistry类方法介绍:

①addMapping
该方法用于添加允许跨域访问的路径,String类型,若存在多个路径则需要在CorsRegistry里配置多个

2.CorsRegistration类方法介绍:

CorsRegistration是CorsRegistry的辅助类,使用它可以对单个跨域请求进行更细粒度的配置。
①allowedOrigins(低版本使用,但是现在高版本也支持)
设置允许跨域请求的来源URL。该方法接受多个参数,每个参数为一个允许的来源URL。或者设置"*"
②allowedOriginPatterns(一般使用这种方式)
设置允许跨域请求的来源URL的模式。该方法接受多个参数,每个参数为一个允许的来源URL模式。或者设置"*"
③allowCredentials
设置是否允许跨域请求携带凭证信息。默认情况下,浏览器不会向跨域请求发送Cookie等凭证信息。如果希望携带凭证信息,则需要将allowCredentials方法设置为true。
④allowedMethods
设置允许跨域请求的HTTP方法。该方法接受多个参数,每个参数为一种允许的HTTP请求方式。
⑤allowedHeaders
设置允许请求携带的HTTP头信息。该方法接受多个参数,每个参数为一种允许的HTTP头信息。(放行哪些请求头部信息)
⑥exposedHeaders
设置响应头信息,这些信息允许客户端访问。该方法接受多个参数,每个参数为一种允许的响应头信息。(暴露哪些响应头信息)
⑦combine
将当前CorsRegistration对象与另一个CorsConfiguration对象合并,返回合并后的CorsConfiguration对象。可以使用该方法将多个CorsRegistration对象的配置合并到同一个CorsConfiguration对象中。
⑧maxAge
设置响应的缓存时间,单位为秒,默认30分钟。
例如,当设置maxAge为3600时,如果浏览器在一小时内再次向同一个目标URL发送跨域请求,就可以直接使用以前的预检请求结果,而不需要再次进行预检请求。maxAge属性只影响预检请求的缓存时间,而不会影响正常的跨域请求,因此不会对实际的业务逻辑产生影响。此外,maxAge属性的具体值需要根据实际情况进行调整,过小的缓存时间可能会导致频繁的预检请求,过大的缓存时间可能会使跨域请求的控制权得不到及时更新,从而增加安全风险。

六:注册页面跳转(addViewControllers)

addViewControllers方法是SpringMVC中WebMvcConfigurer接口定义的一个方法,用于注册一个简单的视图控制器,以便将请求路径映射到一个具体的视图页面。在一些简单的场景下,我们可能只需要将某个请求路径直接映射到一个固定的视图页面,而不需要进行额外的逻辑处理。此时,可以使用addViewControllers方法来快速注册一个视图控制器,并指定对应的请求路径和视图名称即可。但是我们需要额外注意的是,我们在SpringBoot中处理视图跳转时最好集成Thymeleaf,因为它是SpringBoot指定认可的并且,Thymeleaf与SpringMVC协同工作,可以方便地实现快速开发Web应用程序。

1.配置

在SpringBoot配置Thymeleaf的一些信息:
导入Thymeleaf坐标:

<!--导入SpringBoot集成Thymeleaf启动器-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

在application.yml配置Thymeleaf配置信息:

    spring:
       # 配置Thymeleaf模板(默认启动会请求/templates/index.html)
      thymeleaf:
        cache: false                      # 是否有模板缓存
        prefix: classpath:/templates/     # 模板放置的位置
        suffix: .html                     # 模板后缀
        mode: HTML                        # 模板类型
        encoding: UTF-8                   # 模板编码
2.ViewControllerRegistry类说明:

①addViewController(String urlPath)
通过urlPath参数指定的请求URL路径(例如"/home")注册一个简单的视图控制器,该方法返回一个ViewControllerRegistration对象,通过该对象可以设置相关属性,如视图名称、请求方式等。如:registry.addViewController(“/login”);
②setOrder(int order)
设置当前视图控制器的执行顺序,当有多个视图控制器针对同一请求路径时,可以使用该方法进行优先级排序。默认情况下,不同的视图控制器按照它们被注册的顺序执行。
③addRedirectViewController(String urlPath, String redirectUrl)
注册一个重定向视图控制器,将urlPath请求路径重定向到指定的重定向地址redirectUrl。
如:registry.addRedirectViewController(“/toBaidu”,“https://www.baidu.com”);

3.ViewControllerRegistration类说明:

①setViewName(String viewName)
资源路径的前缀
②setStatusCode(HttpStatus statusCode)
配置访问不存在资源的响应码,如下常见的:
HttpStatus.BAD_REQUEST:请求参数错误或格式不正确,例如缺少必需参数、参数类型错误等。
HttpStatus.UNAUTHORIZED:未经授权访问,需要用户登录或提供凭证。
HttpStatus.FORBIDDEN:已经授权但访问被禁止,通常意味着权限不足或需要进行进一步身份验证。
HttpStatus.NOT_FOUND:请求的资源不存在,通常使用自定义的404错误页面进行提示。
HttpStatus.METHOD_NOT_ALLOWED:请求方式不支持,例如GET请求访问只支持POST的接口时会返回405错误。
HttpStatus.INTERNAL_SERVER_ERROR:服务器内部错误,需要在后台进行排查和修复。
示例:registry.addViewController(“/**”).setStatusCode(HttpStatus.NOT_FOUND);
说明:访问不存在的页面我一律按照404处理,但是我们templates/error/404.html页面需要存在

注:按照之前配置的thymeleaf配置来说,默认根路径为resources/templates,跳转的资源文件都是.html文件

七:静态资源处理(addResourceHandlers)

addResourceHandlers方法是SpringMVC框架提供的一种配置静态资源的方式,它可以将指定的资源路径映射到一个或多个URL路径上,并指定资源的缓存策略、版本号以及是否允许目录列表等选项。具体来说,addResourceHandlers方法需要传入一个ResourceHandlerRegistry对象作为参数,然后在这个对象上调用addResourceHandler方法,来添加一个或多个处理器。

1.ResourceHandlerRegistry类方法介绍:

①addResourceHandler
该方法用于指定静态资源的URL路径,支持Ant风格的通配符,如"/resources/**"表示匹配所有以"/resources/"开头的请求。

2.ResourceHandlerRegistration类方法介绍:

①addResourceLocations
该方法为静态资源所在的物理路径或URL。可以使用多个addResourceLocations方法指定多个路径,如下例:

registry.addResourceHandler("/resources/**")
	.addResourceLocations("classpath:/static/", "file:/opt/files/")

说明:
classpath:/static/表示在项目的Classpath下(即src/main/resources文件夹下)查找static文件夹,file:/opt/files/表示在系统中的/opt/files/目录下查找文件。

②setCacheControl
此方法用于设置缓存控制头(cache-control header),CacheControl是一个封装了缓存策略的类。例如:

CacheControl cc = CacheControl.maxAge(30, TimeUnit.DAYS).cachePublic();
registry.addResourceHandler("/resources/**").addResourceLocations("classpath:/static/")
	.setCacheControl(cc);

说明:这将指示浏览器缓存静态资源30天,并且它们是public缓存,意味着中间代理服务器也可以缓存资源。
③setCachePeriod
该方法用于设置静态资源缓存时间,参数类型为Duration类型。如:

registry.addResourceHandler("/resources/**").addResourceLocations("classpath:/static/")
	.setCachePeriod(Duration.ofMinutes(5));

说明:静态资源缓存的过期时间为5分钟
④setOptimizeLocations
此方法用于启用或禁用位置优化。如果启用位置优化,则将优化静态资源的位置,以便并发访问静态资源时可以获得更好的性能。默认情况下,位置优化是禁用的。
⑤setUseLastModified
此方法用于启用或禁用上次修改时间检查(last-modified check)。如果启用上次修改时间检查,则在每个请求中发送一个if-modified-since头,以检查是否需要返回新内容。默认情况下,上次修改时间检查是启用的。
⑥resourceChain
用于开启或关闭ResourceChain模式。当开启ResourceChain模式时,每个资源文件都会自动添加版本号,避免浏览器缓存问题。
例如:

registry.addResourceHandler("/resources/**").addResourceLocations("classpath:/static/")
	.resourceChain(true);

注意,要使ResourceChain生效,还需要设置addResolver如:

.resourceChain(false)
	// 添加VersionResourceResolver,且指定版本号
	.addResolver(new VersionResourceResolver()
	.addFixedVersionStrategy("1.0.0", "/**")); // 下次访问地址:http://localhost:8881/resources/1.0.0/xxx.css

八:代码示例(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("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
 
        registry.addResourceHandler("BJTPReadpath")
                .addResourceLocations("file:"+ "BJTPSavepath");
 
        // 浏览器测试图片回显路径: http://IP:8080/file/ewm/Img/2023/10/12/图片名称.jpg
  	    registry
                // 图片回显路径
                .addResourceHandler("file/ewm/Img/**")
                // 图片存放路径
                .addResourceLocations("file:D:/file/ewm/Img/");
        }
    }
 
    /**
     * 跨域配置添加
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 设置允许跨域的路径
        registry.addMapping("/**")
                // 设置允许跨域请求的域名
              //  .allowedOrigins("*")
                .allowedOriginPatterns("*")
                // 是否允许证书 不再默认开启
                .allowCredentials(true)
                // 设置允许的方法
                .allowedMethods("*")
                // 跨域允许时间
                .maxAge(3600);
    }
 
    //解决中文乱码问题
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        //解决中文乱码
        converters.add(responseBodyConverter());
        //解决 添加解决中文乱码后 上述配置之后,返回json数据直接报错 500:no convertter for return value of type
        converters.add(messageConverter());
    }
    @Bean
    public HttpMessageConverter<String> 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<HttpMessageConverter<?>> 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<String> 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<HandlerMethodArgumentResolver> resolvers) {
        WebMvcConfigurer.super.addArgumentResolvers(resolvers);
    }
}

九:代码示例(拦截器)

<dependency>
	<groupId>net.minidev</groupId>
	<artifactId>json-smart</artifactId>
</dependency>
<dependency>
	<groupId>com.vaadin.external.google</groupId>
	<artifactId>android-json</artifactId>
	<version>0.0.20131108.vaadin1</version>
	<scope>compile</scope>
</dependency>


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<String> 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<HttpMessageConverter<?>> converters) {
        super.configureMessageConverters(converters);
        //解决中文乱码
        converters.add(responseBodyConverter());
        //解决 添加解决中文乱码后 上述配置之后,返回json数据直接报错 500:no convertter for return value of type
        converters.add(messageConverter());
    }
 
 
    @Bean
    public HttpMessageConverter<String> 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<HttpMessageConverter<?>> 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<HandlerMethodArgumentResolver> resolvers) {
        // userArgumentResolver该类对Controller传入的参数做了具体处理
        resolvers.add(userArgumentResolver);
    }
 
 
}

自定义解析器

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


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2262950.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【机器学习】机器学习的基本分类-强化学习(Reinforcement Learning, RL)

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是一种基于试错的方法&#xff0c;旨在通过智能体与环境的交互&#xff0c;学习能够最大化累积奖励的策略。以下是强化学习的详细介绍。 强化学习的核心概念 智能体&#xff08;Agent&#xff09; 执行动作并与环境…

博世智驾新动力:Apache DolphinScheduler如何征服数据处理挑战

视频及PPT等相关资料&#xff1a;点击查看 讲师介绍 陶超权&#xff0c;博世智驾&#xff08;中国&#xff09;后端工程师&#xff0c;负责数据处理和数据调度方面工作&#xff0c;在智能驾驶数据处理领域具有丰富的实践经验。在2024年12月Apache DolphinScheduler社区线上交流…

令牌(token)+加密(加盐)

目录 一,令牌技术 1,不使用session的原因: 2,有两种解决方案: (1)服务器层面的 (2)客户端层面的(JWT令牌) 生成签名: 生成jwt令牌: 验证令牌是否合法: (3)令牌实际运用 二,加密加盐: 进行加密: 进行验证: 一,令牌技术 1,不使用session的原因: 登录页面,用户会将密…

Flask入门:打造简易投票系统

目录 准备工作 创建项目结构 编写HTML模板 编写Flask应用 代码解读 进一步优化 结语 Flask,这个轻量级的Python Web框架,因其简洁和易用性,成为很多开发者入门Web开发的首选。今天,我们就用Flask来做一个简单的投票系统,让你快速上手Web开发,同时理解Flask的核心概…

阿里巴巴前端面试经验

阿里巴巴面经&#xff08;新零售事业群-CBU技术部&#xff09; 笔试 /*** 1. 查找落单的数字* 描述&#xff1a;给定一个非空的数字数组&#xff0c;数组有且只有一个非重复项&#xff0c;实现一个方法获取落单项* 示例:* getSingleNumber([1, 2, 1, 2, 0]); // 0* getSingle…

指针的深入讲解

本章重点&#xff1a; 字符指针数组指针指针数组数组传参和指针传参函数指针函数指针数组指向函数指针数组的指针回调函数 我们在指针的初阶的时候主要讲了&#xff1a; 1.指针就是变量&#xff0c;用来存放地址&#xff0c;地址唯一标识一块内存空间 2.指针的大小是固定4个…

网络多层的协议详述

网络层 1&#xff09;地址管理&#xff1a;制定一系列的规则&#xff0c;通过地址&#xff0c;在网络上描述出一个设备的位置 2&#xff09;路由选择&#xff1a;网络环境比较复杂&#xff0c;从一个节点到另一个节点&#xff0c;存在很多条不同的路径&#xff0c;需要规划出…

Zabbix6.0升级为6.4

为了体验一些新的功能&#xff0c;比如 Webhook 和问题抑制等&#xff0c;升级个小版本。 一、环境信息 1. 版本要求 一定要事先查看官方文档&#xff0c;确认组件要求的版本&#xff0c;否则版本过高或者过低都会出现问题。 2. 升级前后信息 环境升级前升级后操作系统CentOS…

UML复习题

用例与用户的4种关系对象图和类图有什么关系:对象图是类图某一时刻的快照组件图&#xff0c;体现的是静态图部署图&#xff0c;涉及到硬件的结点&#xff0c;实线链接 以上都是静态图 时序图&#xff0c;消息先后协作图 &#xff0c;谁和谁交互&#xff0c;对象之间的交互某一…

【MFC】多工具栏如何保存状态

MFC中的工具栏本来只有一个&#xff0c;如果想增加几个工具栏是比较简单&#xff0c;但现在一个重要的问题是&#xff0c;状态无法保存&#xff0c;导致每次打开&#xff0c;工具栏就会出现问题&#xff0c;要么偏移位置要么显示不出。 经过研究&#xff0c;发现是MFC框架中的…

Buck开关电源闭环控制的仿真研究15V/5V[Matlab/simulink源码+Word文档]

课题设计要求 ⑴输入直流电压(VIN)&#xff1a;15V ⑵输出电压(VO)&#xff1a;5.0V ⑶负载电阻&#xff1a;R2欧 ⑷输出电压纹波峰-峰值 Vpp≤50mV &#xff0c;电感电流脉动&#xff1a;输出电流的10% ⑸开关频率(fs)&#xff1a;100kHz ⑹BUCK主电路二极管的通态压降VD0.5V…

鸿蒙项目云捐助第十八讲云捐助我的页面下半部分的实现

鸿蒙项目云捐助第十八讲云捐助我的页面下半部分的实现 在一般的应用app中都会有一个“我的”页面&#xff0c;在“我的”页面中可以完成某些设置&#xff0c;也可以完成某些附加功能&#xff0c;如“修改密码”等相关功能。这里的鸿蒙云捐助也有一个“我的”功能页面。这里对“…

Flink2.0未来趋势中需要注意的一些问题

手机打字&#xff0c;篇幅不长&#xff0c;主要讲一下FFA中关于Flink2.0的未来趋势&#xff0c;直接看重点。 Flink Forward Asia 2024主会场有一场关于Flink2.0的演讲&#xff0c;很精彩&#xff0c;官方也发布了一些关于Flink2.0的展望和要解决的问题。 1.0时代和2.0时代避免…

《深入浅出Apache Spark》系列⑤:Spark SQL的表达式优化

导读&#xff1a;随着数据量的快速增长&#xff0c;传统的数据处理方法难以满足对计算速度、资源利用率以及查询响应时间的要求。为了应对这些挑战&#xff0c;Spark SQL 引入了多种优化技术&#xff0c;以提高查询效率&#xff0c;降低计算开销。本文从表达式层面探讨了 Spark…

在Tomcat中部署应用时,如何通过域名访问而不加端口号

--江上往来人&#xff0c;但爱鲈鱼美。 --君看一叶舟&#xff0c;出没风波里。 在Tomcat中部署应用时&#xff0c;如果你希望通过域名访问而不加端口号&#xff08;默认HTTP端口80或HTTPS端口443&#xff09;&#xff0c;你通常需要在前端使用一个反向代理服务器&#xff08;如…

如何测量分辨率

一、什么是分辨率&#xff1f; 分辨率指的是分清物体细节的能力。分辨率是一个成像系统还原空间频率的能力。一些人只是简单的用分辨率去描述极限分辨率&#xff0c;但是相机在在不同的对比度的情况下还原低&#xff0c;中和高频率的能力&#xff0c;也可以显示全面综合的信息。…

Leetcode分隔链表

java 实现 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val val; this.next next; }* }*/ class …

maui开发成生安卓apk,运行提示该应用与此设备的CPU不兼容

在生成.NET MAUI安卓应用时遇到“该应用与此设备的CPU不兼容”的问题&#xff0c;确保你的.NET MAUI应用支持的Android目标框架与设备CPU架构相匹配。例如&#xff0c;如果你的应用是为ARM64架构编译的&#xff0c;而你的设备是x86架构&#xff0c;就会出现不兼容的问题。 一、…

在 Unity 6 中使用APV为您的世界创建全局照明的新方法(一)

Unity 6 中推出的新照明功能让您能够更快速、更高效的完成对烘焙场景的照明工作&#xff0c;在本文中我们将与大家详细分享在 Unity 6 中应用自适应探针卷创建快速全局光照的更多细节与具体应用方法。由于内容比较丰富&#xff0c;我们将把内容分为三篇文章&#xff0c;以便大家…

深度学习之超分辨率算法——FRCNN

– 对之前SRCNN算法的改进 输出层采用转置卷积层放大尺寸&#xff0c;这样可以直接将低分辨率图片输入模型中&#xff0c;解决了输入尺度问题。改变特征维数&#xff0c;使用更小的卷积核和使用更多的映射层。卷积核更小&#xff0c;加入了更多的激活层。共享其中的映射层&…