相关博文:
SpringBoot学习(3)-SpringBoot添加支持CORS跨域访问:https://blog.csdn.net/yft_android/article/details/80307672
SpringBoot(六):SpringBoot使用CROS解决跨域问题:https://blog.csdn.net/saytime/article/details/74937204
No converter found for return value of type: class java.util.HashMap异常解决方案:https://blog.csdn.net/fxbin123/article/details/83221986
CORS(Cross-Origin Resource Sharing)”跨域资源共享”,是一个W3C标准,它允许浏览器向跨域服务器发送Ajax请求,打破了Ajax只能访问本站内的资源限制。我们在开发中都会遇到前端请求后台服务器出现跨域错误,下面我就讲一下如何让你的SpringBoot项目支持CORS跨域。
核心:配置CorsConfiguration
WebMvcConfigurerAdapter已经过时,新的实现是:
@Configuration
public class WebMvcConfg implements WebMvcConfigurer {
//省略
}
@Configuration
public class WebMvcConfg extends WebMvcConfigurationSupport {
//省略
}
网上推荐使用第二种方案,WebMvcConfigurationSupport是WebMvcConfigurerAdapter替代方案;
package com.wisely.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class CustomCorsConfiguration {
@Bean
public WebMvcConfigurationSupport corsConfigurer() {
return new WebMvcConfigurationSupport() {
@Override
public void addCorsMappings(CorsRegistry registry) {
//允许访问的接口
registry.addMapping("/api/*")
//允许站外访问的路径
.allowedOrigins("http://localhost:8888");
//.allowedMethods("GET", "POST", "DELETE", "PUT", "OPTIONS")
//.allowCredentials(false).maxAge(3600);
}
};
}
}
但项目运行时出现异常
org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class java.util.HashMap
网上给出的异常原因有:
1、缺失Jackson 依赖,如果有 spring-boot-starter-web
依赖可排除该问题
2、是否是 POJO (get set 方法是否有)
3、注解是否是 @RestController
若以上可能性完全排除:
具体原因:
extends WebMvcConfigurationSupport
,这种方式会屏蔽springboot @EnableAutoConfiguration
中的设置
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class),只有当容器中没有WebMvcConfigurationSupport这个类型的组件的时候,才会启动自动配置。
所以当我们继承WebMvcConfigurationSupport之后,除非在继承类中重写了一系列有关WebMVC的配置,否则可能就会遇到静态资源访问不到,返回数据不成功这些一系列问题了。
鉴于SpringBoot2.X 中 WebMvcConfigurerAdapter 已经过时,所以可以实现 WebMvcConfigurer 接口来达到相同的目的:
package com.wisely.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CustomCorsConfiguration implements WebMvcConfigurer{
@Override
public void addCorsMappings(CorsRegistry registry) {
//允许访问的接口
registry.addMapping("/api/*")
//允许站外访问的路径
.allowedOrigins("http://localhost:8888");
//.allowedMethods("GET", "POST", "DELETE", "PUT", "OPTIONS")
//.allowCredentials(false).maxAge(3600);
}
}
至此,项目中出现的问题得到解决。
别人的总结:
1、@EnableWebMvc=WebMvcConfigurationSupport,使用了@EnableWebMvc注解等于扩展了WebMvcConfigurationSupport但是没有重写任何方法。@EnableWebMvc 和WebMvcConfigurationSupport 二者只能选一个,如果同时实现,则会失效 (感谢一位网友热心帮助,开始忽略了这个问题)
2、继承WebMvcConfigurationSupport, 需要重写一系列有关WebMVC的配置,否则可能就会遇到静态资源访问不到,返回数据不成功这些一系列问题了(我就是掉在此坑),
因为springboot已经实例化了WebMvcConfigurationSupport, 如果使用@EnableWebMvc 注解或者继承WebMvcConfigurationSupport会覆盖默认实现, 一般建议还是不覆盖默认的好
3、实现WebMvcConfigurer 接口
示例:
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
/**
* 实现WebMvcConfigurer接口的部分示例...
*
* @author fxbin
* @version v1.0
* @since 2018/11/7 1:21
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
/**
* 添加类型转换器和格式化器
* @author fxbin
* @param registry
*/
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatterForFieldType(LocalDate.class, new DateFormatter());
}
/**
* 跨域支持
* @author fxbin
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "DELETE", "PUT")
.maxAge(3600 * 24);
}
/**
* 添加静态资源--过滤swagger-api (开源的在线API文档)
* @author fxbin
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//过滤swagger
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
registry.addResourceHandler("/swagger-resources/**")
.addResourceLocations("classpath:/META-INF/resources/swagger-resources/");
registry.addResourceHandler("/swagger/**")
.addResourceLocations("classpath:/META-INF/resources/swagger*");
registry.addResourceHandler("/v2/api-docs/**")
.addResourceLocations("classpath:/META-INF/resources/v2/api-docs/");
}
/**
* 配置消息转换器--这里我用的是alibaba 开源的 fastjson
* @author fxbin
* @param converters
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//1.需要定义一个convert转换消息的对象;
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
//2.添加fastJson的配置信息,比如:是否要格式化返回的json数据;
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat,
SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteNullStringAsEmpty,
SerializerFeature.DisableCircularReferenceDetect,
SerializerFeature.WriteNullListAsEmpty,
SerializerFeature.WriteDateUseDateFormat);
//3处理中文乱码问题
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
//4.在convert中添加配置信息.
fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);
fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
//5.将convert添加到converters当中.
converters.add(fastJsonHttpMessageConverter);
}
/**
* 添加自定义的拦截器
* @author fxbin
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
}
}
/**
* 拦截器
* @author fxbin
* @version v1.0
* @since 2018/11/7 1:25
*/
class MyInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Interceptor preHandler method is running !");
return super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Interceptor postHandler method is running !");
super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("Interceptor afterCompletion method is running !");
super.afterCompletion(request, response, handler, ex);
}
@Override
public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Interceptor afterConcurrentHandlingStarted method is running !");
super.afterConcurrentHandlingStarted(request, response, handler);
}
}