Spring Boot

Springboot解决header中的中文编码问题

2019-10-28  本文已影响0人  一曲畔上

1,http的header中不能直接传中文变量,我们添加到header中的中文,需要encoder转码;
我们就用常规的utf8编码转码;比如我们设定一个header中的参数name="小明",那我们只能是用
header.add("name", URLEncoder.encode("小明", "utf8"));
也就是header.add("name", "%E5%B0%8F%E6%98%8E");
2,在Springboot接收端,使用@RequestHeader(value = "name")接收到的是"%E5%B0%8F%E6%98%8E",而不是"小明"。当然了,也可以用传统方式,对于接收到的参数进行decoder,但是如果header参数比较多的情况下,这个方式就不那么友好了。
3,今天我要介绍的是基于Springboot的特性的一种解决方案,使用Springboot的转换器GenericConverter!
步骤如下:
3.1,实现接口GenericConverter

public class StringDecoderForHeaderConverter implements GenericConverter {
    private Logger logger = LoggerFactory.getLogger(StringDecoderForHeaderConverter.class);
    
    private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    private static final String NO_NAME = "NO_NAME";
    
    private Charset charset;
    
    public StringDecoderForHeaderConverter(@Nullable Charset charset) {
        this.charset = charset;
        if (this.charset == null) {
            this.charset = DEFAULT_CHARSET;
        }
    }
    
    /**
     * +返回编码值
     * @return charset
     */
    public Charset getCharset() {
        return charset;
    }
    
    /**
     * +设置编码值
     * @param charset 编码值
     */
    public void setCharset(Charset charset) {
        this.charset = charset;
        
        if (this.charset == null) {
            this.charset = DEFAULT_CHARSET;
        }
    }
    
    @Override
    public Set<ConvertiblePair> getConvertibleTypes() {
        return Collections.singleton(new ConvertiblePair(String.class, String.class));
    }

    @Override
    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        if (ObjectUtils.isEmpty(source)) {
            return source;
        }
        
        String name = needDecoder(source, targetType);
        if (name != null) {
            return convert(source.toString(), name);
        }
        
        return source;
    }
    
    /**
     * +是否需要解码
     * @param source 待处理的值
     * @param targetType 类型
     * @return 非null:需要解码;null:无需解码
     */
    private String needDecoder(Object source, TypeDescriptor targetType) {
        RequestHeader requestHeader = targetType.getAnnotation(RequestHeader.class);
        Class<?> type = targetType.getType();
        if (requestHeader != null && type == String.class) {
            if (source.toString().indexOf("%") >= 0) {
                String name = requestHeader.name();
                if (name == null || name.equals("")) {
                    name = requestHeader.value();
                }
                if (name == null || name.equals("")) {
                    name = NO_NAME;
                }
                
                return name;
            }
        }
        
        return null;
    }
    
    /**
     * +结果解码
     * @param source 待解码的结果
     * @param name 参数名称
     * @return 解码后的结果
     */
    private String convert(final String source, final String name) {
        if (logger.isDebugEnabled()) {
            logger.debug("Begin convert[" + source + "] for RequestHeader[" + name + "].");
        }
        String _result = null;
        try {
            _result = URLDecoder.decode(source, this.charset.name());
            if (logger.isDebugEnabled()) {
                logger.debug("Success convert[" + source + ", " + _result + "] for RequestHeader[" + name + "].");
            }
            
            return _result;
        } catch(Exception e) {
            logger.warn("Fail convert[" + source + "] for RequestHeader[" + name + "]!", e);
        }
        
        return source;
    }
}

代码不难理解,就是对应带有@RequestHeader注解的参数如果参数值中有"%",说明可能有被encoder的可能,那么我们需要decoder。
3.2,注册该bean

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({ HttpProperties.class })
@Import({ WebMvcAutoConfiguration.class })
@ComponentScan(
        value = "com.beyonds.phoenix.sun.web",
        includeFilters = {
                @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)
        })
public class WebMvcConfiguration implements WebMvcConfigurer {
    /**
     * +定义HttpClient
     * @return HttpClient
     */
    @Bean
    @ConfigurationProperties(prefix = "feign.httpclient")
    public HttpClientBuilder apacheHttpClientBuilder() {
        return HttpClientBuilder.create();
    }
    
    /**
     * +自定义输入的日期格式
     * +覆盖spring.mvc.date-format
     * @return 日期格式转换器
     */
    @Bean
    public StringDateConverter dateConverter() {
        return new StringDateConverter();
    }
    
    /**
     * +对于header中的中文字进行解码
     * @return 转换结果
     */
    @Bean
    public StringDecoderForHeaderConverter stringHeaderConverter(HttpProperties httpProperties) {
        return new StringDecoderForHeaderConverter(httpProperties.getEncoding().getCharset());
    }
}

3.3,配置编码参数,利用HttpProperties

spring: 
  application: 
    name: phoenix-sun
  http: 
    converters: 
      preferred-json-mapper: jackson
    encoding:
      enabled: true
      charset: UTF-8
      force: true

3.4,这样就ok了,无需对某个header去单独处理了。
4,完整的代码示例请参照代码示例或者代码示例

上一篇下一篇

猜你喜欢

热点阅读