Request data
=> HttpMessageConverter
=> FormHttpMessageConverter
=> MappingJackson2HttpMessageConverter && @RequestBody
=> Yml2HttpMessageConverter(自定义)
=> Validation验证
=> 数据持久化
=> 返回json
数据流转
HttpMessageConverter
采用RequestResponseBodyMethodProcessor
方法执行器,
前台提交数据,设置contentType
,spring-web根据contentType
,解析数据,并映射。
spring内置当HttpMessageConverter有
- ByteArrayHttpMessageConverter – converts byte arrays
- StringHttpMessageConverter – converts Strings
- ResourceHttpMessageConverter – converts org.springframework.core.io.Resource for any type of octet stream
- SourceHttpMessageConverter – converts javax.xml.transform.Source
- FormHttpMessageConverter – converts form data to/from a MultiValueMap<String, String>.
- Jaxb2RootElementHttpMessageConverter – converts Java objects to/from XML (added only if JAXB2 is present on the classpath)
- MappingJackson2HttpMessageConverter – converts JSON (added only if Jackson 2 is present on the classpath)
- MappingJacksonHttpMessageConverter – converts JSON (added only if Jackson is present on the classpath)
- AtomFeedHttpMessageConverter – converts Atom feeds (added only if Rome is present on the classpath)
- RssChannelHttpMessageConverter – converts RSS feeds (added only if Rome is present on the classpath)
平时我们用当最多当就是MappingJackson2HttpMessageConverter
和FormHttpMessageConverter
FormHttpMessageConverter
使用的contentTypeapplication/x-www-form-urlencoded
和multipart/form-data
MappingJackson2HttpMessageConverter
使用的contentTypeapplication/json
NOTED
如果使用GET请求数据,那么使用ServletModelAttributeMethodProcessor
,将不会使用转化器类。
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
类型转换
MappingJackson2HttpMessageConverter
对基本类型(包括枚举,List<> String[])支持都非常好。如果自定义类需要继承JsonDeserializer
。
@JsonComponent
public class PhoneJsonDeserializer extends JsonDeserializer<Phone> {
@Override
public Phone deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
String[] phonetArr = node.asText().split("-");
Phone phone = new Phone();
phone.setCode(phonetArr[0]);
phone.setNumber(phonetArr[1]);
return phone;
}
}
- GET提交和
multipart/form-data
实现Converter
@Component
public class PhoneConverter implements Converter<String, Phone> {
@Override
public Phone convert(String source) {
if (StringUtils.isEmpty(source)) {
return null;
}
String[] phonetArr = source.split("-");
Phone phone = new Phone();
phone.setCode(phonetArr[0]);
phone.setNumber(phonetArr[1]);
return phone;
}
}
自定义转换器
定义一个可以转换application/yml
的转换器
pom.xml
添加
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.19</version>
</dependency>
@Component
public class Yml2HttpMessageConverter extends AbstractHttpMessageConverter<Object> {
public Yml2HttpMessageConverter() {
super(new MediaType[]{new MediaType("application", "yml"), new MediaType("application", "ymal")});
}
@Override
protected boolean supports(Class<?> aClass) {
return true;
}
@Override
protected Object readInternal(Class<?> aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
Yaml yaml = new Yaml();
return yaml.loadAs(httpInputMessage.getBody(), aClass);
}
@Override
protected void writeInternal(Object o, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
Yaml yaml = new Yaml();
OutputStream outputStream = httpOutputMessage.getBody();
outputStream.write(yaml.dump(o).getBytes());
outputStream.close();
}
}
@Component
注解会放在在所有converter的第一个。优先于MappingJackson2HttpMessageConverter
。要改变顺序,WebMvcConfigurer
中去实现方法extendMessageConverters
。导致response的时候,都是yml格式,而不是json格式。
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* https://www.cnblogs.com/slankka/p/11437034.html
* 加到最后
* @param converters
*/
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new Yml2HttpMessageConverter());
}
}
这样response的时候,优先json格式,如果需要yml格式的返回可以手动指定
@GetMapping (produces = "application/yml")
public UserDTO getData(@RequestBody UserDTO userDTO) {
log.info(userDTO.toString());
return userDTO;
}
Validation
使用默认验证器
controller添加注解@Valid
,model添加验证注解如:@NotBlank
@GetMapping (produces = "application/yml")
public UserDTO getData(@RequestBody @Valid UserDTO userDTO, BindingResult result) throws Exception {
if (result.hasErrors()) {
throw new Exception("验证失败");
}
log.info(userDTO.toString());
return userDTO;
}
自定义验证器
@Documented
@Constraint(validatedBy = PhoneNumberValidator.class)
@Target( { ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface PhoneNumberConstraint {
String message() default "Invalid phone number";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class PhoneNumberValidator implements
ConstraintValidator<PhoneNumberConstraint, Phone> {
@Override
public void initialize(PhoneNumberConstraint constraintAnnotation) {
}
/**
* @param phone
* @param context
* @return
*/
@Override
public boolean isValid(Phone phone, ConstraintValidatorContext context) {
if (Objects.isNull(phone)) {
return true;
}
return (phone.getCode() + "-" + phone.getNumber()).matches("\\d+[-]\\d+");
}
}
使用注解
@PhoneNumberConstraint
private Phone phone;
持久化
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
application.xml
spring:
datasource:
name: druidDataSource
type: com.alibaba.druid.pool.DruidDataSource
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/iis?useUnicode=true&characterEncoding=UTF-8
username: root
password: jkxyx205
initialSize: 1
minIdle: 5
maxActive: 10
maxWait: 60000
jpa:
show-sql: true
properties:
hibernate:
format_sql: true
hibernate:
ddl-auto: update
public class ListJsonAttributeConverter<T> implements AttributeConverter<List<T>, String> {
@Override
public String convertToDatabaseColumn(List<T> list) {
if (CollectionUtils.isEmpty(list)) {
return null;
}
try {
return JsonUtils.toJson(list);
} catch (IOException e) {
return null;
}
}
@Override
public List<T> convertToEntityAttribute(String json) {
Class<?> clazz = ClassUtils.getActualTypeArgument(this.getClass())[0];
if (StringUtils.isBlank(json)) {
return Collections.emptyList();
}
try {
return (List<T>) JsonUtils.toList(json, clazz);
} catch (IOException e) {
e.printStackTrace();
return Collections.emptyList();
}
}
}
public class PojoJsonAttributeConverter<T> implements AttributeConverter<T, String> {
@Override
public String convertToDatabaseColumn(T t) {
if (Objects.isNull(t)) {
return null;
}
try {
return JsonUtils.toJson(t);
} catch (IOException e) {
return null;
}
}
@Override
public T convertToEntityAttribute(String json) {
Class<?> clazz = ClassUtils.getActualTypeArgument(this.getClass())[0];
if (StringUtils.isBlank(json)) {
return null;
}
try {
return (T) JsonUtils.toObject(json, clazz);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
将对象以 JSON
字符串的形式持久化到数据库中,使用继承指定范型类型
public class PriceConverter extends ListJsonAttributeConverter<List<QuotationExpression>> {}
public class OrderConverter extends PojoJsonAttributeConverter<QuotationExpression> {}
指定对应的convert
@Convert(converter = PriceConverter.class)
private List<QuotationExpression> quotationExpressionList;
@Convert(converter =OrderConverter.class)
private QuotationExpression quotationExpression;
https://vladmihalcea.com/jpa-attributeconverter/
https://zenidas.wordpress.com/recipes/jpa-converter-as-json/
response JSON
public class AddressJsonSerializer extends JsonSerializer<Address> {
@Override
public void serialize(Address address, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
address.setAddrDetail("China "+ address.getAddrDetail());
jsonGenerator.writeObject(address);
}
}
@Convert(converter = AddressConverter.class)
@JsonSerialize(using = AddressJsonSerializer.class)
private Address address;