likes
comments
collection
share

HTTP参数与响应处理:在SpringBoot中自定义参数解析器,反序列化器和类型转换器

作者站长头像
站长
· 阅读数 61

本文介绍了在Spring Boot中如何使用自定义参数解析器和自定义类型转换器来处理HTTP请求和响应中的自定义参数类型。

HTTP,GET,自定义参数解析器

在Java和Spring Boot中,可以通过自定义参数解析器(Argument Resolver)来处理请求中的自定义参数类型,从而实现更为灵活的参数解析。

实现自定义参数解析器(HandlerMethodArgumentResolver)

public class MyArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().equals(MyParam.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
        String value = request.getParameter("myParam");
        // 解析请求中的自定义参数,返回解析结果
        return new MyParam(value);
    }
}

首先,我们需要创建一个自定义参数解析器,实现HandlerMethodArgumentResolver接口。例如,我们创建一个名为MyArgumentResolver的类,用于解析请求中的自定义参数类型MyParam:

在supportsParameter()方法中,我们判断参数类型是否为自定义参数类型MyParam,如果是则返回true,表示该解析器支持这个参数类型。

在resolveArgument()方法中,我们首先通过NativeWebRequest获取HttpServletRequest对象,然后从请求中获取自定义参数myParam的值,并解析成MyParam对象,最后返回解析结果。

注册自定义参数解析器

将自定义参数解析器注册到Spring Boot中。我们可以在@Configuration类中使用addArgumentResolvers()方法将自定义参数解析器添加到Spring Boot中:

@Configuration
public class MyConfig implements WebMvcConfigurer {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new MyArgumentResolver());
    }
}

在上述配置中,我们实现了WebMvcConfigurer接口,重写了addArgumentResolvers()方法,并将MyArgumentResolver对象添加到了参数解析器列表中。

使用

最后,我们可以在Controller中使用自定义参数类型MyParam:

@RestController
@RequestMapping("/test")
public class MyController {

    @GetMapping("/myparam")
    public String testMyParam(@RequestParam("myParam") MyParam myParam) {
        // 处理自定义参数类型MyParam
        return "success";
    }
}

在上述代码中,我们使用了@RequestParam注解来绑定请求中的自定义参数myParam到Controller方法的参数MyParam中。

HTTP,POST,自定义参数解析器

在Java和SpringBoot中处理HTTP POST请求的请求体时,可以使用多种参数解析器。

在SpringBoot中,默认使用的是HttpMessageConverters,它会根据请求头中的Content-Type来选择合适的参数解析器。

Content-TypeParam Resolver
application/jsonMappingJackson2HttpMessageConverter
application/x-www-form-urlencodedFormHttpMessageConverter
multipart/form-dataMultipartResolver

接收和发送 snake_case 格式的 JSON 数据

配置高优级的ObjectMapper

@Bean
    @Primary
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        objectMapper.getFactory().configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true);
        objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
        return objectMapper;
    }

原理如下

  • 当您使用 @Primary 注解标记 ObjectMapper Bean 时,您告诉 Spring 在有多个 ObjectMapper Bean 可用时,优先使用这个 Bean。这样,您配置的 ObjectMapper 实例将成为 Spring Boot 应用中处理 JSON 序列化和反序列化任务的首选实例。
  • Spring Boot 使用 HttpMessageConverter 接口实现将 Java 对象转换为 HTTP 响应消息,以及将 HTTP 请求消息转换为 Java 对象。默认情况下,Spring Boot 配置了一组 HttpMessageConverter,包括处理 JSON 数据的 MappingJackson2HttpMessageConverter
  • MappingJackson2HttpMessageConverter 使用 Jackson 库中的 ObjectMapper 实例来处理 Java 对象与 JSON 数据之间的序列化和反序列化。在您的代码中,您已经配置了 ObjectMapper 以使用 snake_case 格式。
  • 当 Spring Boot 处理一个 HTTP 请求或响应时,它会使用 MappingJackson2HttpMessageConverter(如果请求或响应的内容类型为 JSON)。因为您已经配置了一个首选的 ObjectMapper Bean(使用 snake_case),MappingJackson2HttpMessageConverter 将使用这个 ObjectMapper 实例来处理 JSON 数据。

Jackson2ObjectMapperBuilderCustomizer

import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JacksonConfig {

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> builder.propertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
    }
}

自定义JSON反序列化器(JsonDeserializer)

我们从一个Long过长导致js自动截断的case处理出出发,学习一下JsonDeserializer.

JS内置有32位整数,而number类型的安全整数是53位,但是java的long类型却是64位,如果数值过大的直接传递会有问题.

一般做法是把long型字段转成string进行处理。(可以使用json注解转换或者springmvc统一拦截处理)

定义JsonDeserializer

public class StringToLongDeserializer extends JsonDeserializer<Long> {
    @Override
    public Long deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        String val = p.getText();
        if(StringUtils.isBlank(val)){
            return null;
        }else {
            return Long.valueOf(val);
        }
    }
}
public class LongToStingSerializer extends JsonSerializer<Long> {
    @Override
    public void serialize(Long value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if(value == null){
            gen.writeString("");
        }else {
            gen.writeString(String.valueOf(value));
        }
    }
}

使用JsonDeserializer

前端传过来的请求是string 我们需要转成Long

public class Request {
    @JsonDeserialize(using = StringToLongDeserializer.class)
    private Long pddGoodsId;
}

后端数据类型为Long ,前端需要String 类型

public class VO implements Serializable {
    @JsonSerialize(using = LongToStingSerializer.class)
    private Long goodsId;
}

自定义类型转换器

实现自定义类型转换器(Converter)

public class StringToDateConverter implements Converter<String, Date> {
    private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

    @Override
    public Date convert(String source) {
        try {
            return dateFormat.parse(source);
        } catch (ParseException e) {
            throw new IllegalArgumentException("无效的日期格式:" + source);
        }
    }
}

上述代码中,定义了一个将字符串转换为日期类型的转换器,通过实现Converter接口的convert方法来实现类型转换。在convert方法中,使用SimpleDateFormat类将字符串解析为日期类型,并处理解析异常。

注册自定义类型转换器

为了让Spring框架能够自动地使用这个转换器,需要将其注册到Spring框架中

通过 DefaultConversionService 注册

@Configuration
public class AppConfig {
    @Bean
    public StringToDateConverter stringToDateConverter() {
        return new StringToDateConverter();
    }

    @Bean
    public ConversionService conversionService() {
        DefaultConversionService conversionService = new DefaultConversionService();
        conversionService.addConverter(stringToDateConverter());
        return conversionService;
    }
}

上述代码中,使用@Configuration注解定义了一个配置类,并在该类中定义了一个StringToDateConverter类型的Bean和一个ConversionService类型的Bean。

在ConversionService类型的Bean中,调用addConverter方法将StringToDateConverter类型的转换器注册到ConversionService中。

现在,当 Spring 需要将 String 类型转换为 LocalDate 类型时,将使用我们的自定义 StringToLocalDateConverter 转换器。

通过 FormattingConversionServiceFactoryBean 注册

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.support.FormattingConversionServiceFactoryBean;

@Configuration
public class ConversionConfiguration {

    @Bean
    public FormattingConversionServiceFactoryBean conversionService() {
        FormattingConversionServiceFactoryBean factoryBean = new FormattingConversionServiceFactoryBean();
        factoryBean.setConverters(Set.of(new StringToLocalDateConverter("yyyy-MM-dd")));
        return factoryBean;
    }
}

通过为 FormattingConversionServiceFactoryBean 提供一组转换器,我们可以在 Spring 应用上下文中注册自定义转换器。

现在,当 Spring 需要将 String 类型转换为 LocalDate 类型时,将使用我们的自定义 StringToLocalDateConverter 转换器。

转载自:https://juejin.cn/post/7243592123801960507
评论
请登录