一、问题:
前端代码:
// 新建form表单
// this.lessonName是个字符串名字,this.upath是一份文档
// 请求头的编码格式为:Content-Type: multipart/form-data
const fd = new FormData()
fd.append('lessonName', this.lessonName)
fd.append('file', this.upath)
this.$http.post('/simulation/lesson/add1', fd).then(res => {
if (res) {
this.$message.success(res.data.msg)
this.load();
this.dialogFormVisible = false;
this.clearFile();
} else {
this.$message.error(res.data.msg)
}
})
后端代码:
// 接收函数编写。此时接收的文件file可以正常使用,但lessonName显示为乱码
// 原因是:formdata传递参数,参数会被二进制化。后端接收的时候默认格式为iso_8859_1
// 这是因为StringHttpMessageConverter读和写的编码方式是:ISO-8859-1
@PostMapping( "/add")
public ResultVO add(@RequestPart(value = "lessonName") String lessonName,
@RequestParam(value = "file", required = false) MultipartFile file)
{
if (simulationLessonService.valLessonName(lessonName)){
return new ResultVO(ResStatus.NO,"课程名已存在",false);
}
SimulationLessonDTO lessonDTO = new SimulationLessonDTO();
lessonDTO.setLessonName(lessonName);
……
解决思路
1,前端传参时写明编码格式;
2,前端传参时先进行编码再传递;
3,后端接收时采用全局设置参数;
4,后端修改tomcat配置;
5,后端修改Servlet配置;
6,后端编写全局过滤器,设置request请求编码格式;
7,局部修改,硬解码。
根据上面的思路,做了一些测试。最后有两条成功办法。有些思路没有测,毕竟只是做项目,不是做科研。先说说失败的方法,后附两条成功的方法。
失败方法:
1,springboot配置文件(失败)
# 服务器配置
server:
servlet:
encoding:
charset: utf-8
enabled: true
force: true
tomcat:
uri-encoding: UTF-8
2,声明接收格式(失败)
@PostMapping(value = "/add",produces = "text/plain;charset=UTF-8")
3,在jwtfilter中修改request接收参数格式(失败)
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
httpServletRequest.setCharacterEncoding("UTF-8");
// 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}
return super.preHandle(request, response);
}
4,编写全局过滤器(失败)
//字符编码过滤器
@WebFilter(urlPatterns = "/*",filterName = "CharacterEncodingFilter")
public class CharacterEncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
filterChain.doFilter(request , response);
}
@Override
public void destroy() {
}
}
成功方法:
1,硬改接收参数编码(成功,但不是全局的,仅能解决燃眉之急)
@PostMapping( "/add1")
public ResultVO add1(@RequestPart(value = "lessonName") String lessonName1,
@RequestParam(value = "file", required = false) MultipartFile file)
{
String lessonName = new String(lessonName1.getBytes(StandardCharsets.ISO_8859_1));
if (simulationLessonService.valLessonName(lessonName)){
return new ResultVO(ResStatus.NO,"课程名已存在",false);
}
SimulationLessonDTO lessonDTO = new SimulationLessonDTO();
lessonDTO.setLessonName(lessonName);
……
2,编写全局编码转换器(成功,最终采用的也是这个)
/**
* 中文乱码解决
*/
@Configuration
public class CharsetConfig extends WebMvcConfigurerAdapter {
@Bean
public HttpMessageConverter<String> responseBodyConverter() {
StringHttpMessageConverter converter = new StringHttpMessageConverter(
Charset.forName("UTF-8"));
return converter;
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
super.configureMessageConverters(converters);
converters.add(responseBodyConverter());
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false);
}
}
因为WebMvcConfigurerAdapter已被springboot弃用,根据网上查找,将WebMvcConfigurerAdapter更改为WebMvcConfigurationSupport,修改后代码为:
/**
* 中文乱码解决
*/
@Configuration
public class CharsetConfig extends WebMvcConfigurationSupport {
@Bean
public HttpMessageConverter<String> responseBodyConverter() {
StringHttpMessageConverter converter = new StringHttpMessageConverter(
Charset.forName("UTF-8"));
return converter;
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
super.configureMessageConverters(converters);
converters.add(responseBodyConverter());
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false);
}
}
其它办法,未测试
1(思路应该是一样的),
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
for(HttpMessageConverter httpMessageConverter:converters){
if(StringHttpMessageConverter.class.isAssignableFrom(httpMessageConverter.getClass())){
((StringHttpMessageConverter)httpMessageConverter).setDefaultCharset(Charset.forName("UTF-8"));
}
}
}
}
2,前端转换下格式(没测试,就不具体写了)
原因解析:
子类StringHttpMessageConverter
public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> {
//转成字符串的默认编码为ISO-8859-1
public static final Charset DEFAULT_CHARSET = StandardCharsets.ISO_8859_1;
public StringHttpMessageConverter() {
this(DEFAULT_CHARSET);
}
//默认支持的MediaType是text/plain,”*/*“
// 因为它支持MediaType.TEXT_PLAIN, MediaType.ALL所有类型,所以你的contentType无所谓~~~ 它都能够处理
public StringHttpMessageConverter(Charset defaultCharset) {
super(defaultCharset, MediaType.TEXT_PLAIN, MediaType.ALL);
}
//如果是string类型,就进入
@Override
public boolean supports(Class<?> clazz) {
return String.class == clazz;
}
@Override
protected String readInternal(Class<? extends String> clazz, HttpInputMessage inputMessage) throws IOException {
Charset charset = getContentTypeCharset(inputMessage.getHeaders().getContentType());
/// 根据编码把字符串读进来~
return StreamUtils.copyToString(inputMessage.getBody(), charset);
}
@Override
protected Long getContentLength(String str, @Nullable MediaType contentType) {
Charset charset = getContentTypeCharset(contentType);
return (long) str.getBytes(charset).length;
}
@Override
protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {
HttpHeaders headers = outputMessage.getHeaders();
if (this.writeAcceptCharset && headers.get(HttpHeaders.ACCEPT_CHARSET) == null) {
headers.setAcceptCharset(getAcceptedCharsets());
}
// 根据编码把字符串写进去~
Charset charset = getContentTypeCharset(headers.getContentType());
StreamUtils.copy(str, charset, outputMessage.getBody());
}
Content-Type:text/plain(其实Content-Type为任何值都一样),请求体是:飒飒说,控制台输出:?????????,乱码?
这是因为StringHttpMessageConverter读和写的编码方式是:ISO-8859-1
后续
发现单独写的CharsetConfig与SwaggerConfig继承冲突了。最后合并到SwaggerConfig类里了。
@Configuration
@EnableSwagger2
@ComponentScan("com.hz.simulation")
public class SwaggerConfig implements WebMvcConfigurer {
/**
* 解决中文乱码问题
* @return
*/
@Bean
public HttpMessageConverter<String> responseBodyConverter() {
StringHttpMessageConverter converter = new StringHttpMessageConverter(
Charset.forName("UTF-8"));
return converter;
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
WebMvcConfigurer.super.configureMessageConverters(converters);
converters.add(responseBodyConverter());
}