If you use the ID automatically numbered in the database as it is for the ID displayed on the screen, the user will guess various things. (It is called the number of users)
For such a special field, I think that it is common to prepare a dedicated value object and write the process of ID conversion internally. However, when the type of the request or response model cannot be changed due to complicated circumstances, I investigated whether it can be managed by annotation.
The working environment is Java9
,SpringBoot (2.0.0.RELEASE)
. (I haven't done anything special, so I think other versions are fine)
The requirements for this time are as follows.
--The controller request model and response model types cannot be changed.
--However, annotations can be added
--I want to be able to handle both the client and the server without worrying about obfuscation.
--Obfuscation is conversion from Long
to Long
Based on the above requirements, I would like to make the specifications as follows.
--Annotation is ʻObfuscate` --Only fields are targeted --This time, add 1 for obfuscation and subtract 1 for simplification (it should be more complicated)
This class is obfuscated. (Where to leave the server)
Inherit and implement StdSerializer
.
If it is null
, it will end as it is. (Because Not Null etc. should be expressed by other annotations)
ObfuscateSerializer.java
public class ObfuscateSerializer extends StdSerializer<Long> {
private static final long serialVersionUID = -6910413385029615313L;
protected ObfuscateSerializer() {
super(Long.class);
}
@Override
public void serialize(Long value, JsonGenerator gen, SerializerProvider provider) throws IOException {
//Obfuscation logic
if (value == null) {
return;
}
gen.writeNumber(value + 1);
}
}
This is a class that simplifies. (Where entering the server)
Like the serializer, use StdDeserializer
.
ObfuscateDeserializer.java
public class ObfuscateDeserializer extends StdDeserializer<Long> {
private static final long serialVersionUID = 4303388430635458159L;
protected ObfuscateDeserializer() {
super(Long.class);
}
@Override
public Long deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
//Simplified logic
return Long.parseLong(p.getValueAsString()) - 1;
}
}
{
"timestamp": 1521424385168,
"status": 400,
"error": "Bad Request",
"message": "JSON parse error: For input string: \"hoge\"; nested exception is com.fasterxml.jackson.databind.JsonMappingException: For input string: \"hoge\" (through reference chain: com.example.Hoge[\"value\"])",
"path": "/api/sample/obfuscate"
}
Since it is a marker annotation, it does not have any properties. The target is set only to ʻElementType.FIELD` from the specifications.
Obfuscate.java
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
public @interface Obfuscate {
}
It is a class that associates the defined annotation with the serializer / deserializer.
Inherit and implement JacksonAnnotationIntrospector
.
This class is called for each item when Jackson in Spring Boot converts json to the specified class and the target class, field, or method is annotated.
This time we'll make the serializer and deserializer unique, so we'll override the findSerializer
and findDeserializer
.
MyAnnotationInterceptor.java
public class MyAnnotationInterceptor extends JacksonAnnotationIntrospector {
private static final long serialVersionUID = -7722925738908936096L;
@Override
public Object findSerializer(Annotated a) {
if (a.hasAnnotation(Obfuscate.class)) {
return ObfuscateSerializer.class;
}
return super.findSerializer(a);
}
@Override
public Object findDeserializer(Annotated a) {
if (a.hasAnnotation(Obfuscate.class)) {
return ObfuscateDeserializer.class;
}
return super.findDeserializer(a);
}
}
Register the above annotation interceptor with Spring Boot.
JacksonConfig.java
@Configuration
public class JacksonConfig {
@Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.annotationIntrospector(new MyAnnotationInterceptor());
return builder;
}
}
Now that you're ready, you can add @Obfuscate
to the request or response model fields and they will be converted automatically.
ObfuscateRequest.java
@lombok.Value
public class ObfuscateRequest {
@Obfuscate
Long id;
}
This time, I defined annotations to obfuscate and simplify the fields. I think that there are situations where it is useful when it has already been released and it is difficult to change the class, or when the model is reused and it can not be changed. Also, in the case of value objects, there are some inconveniences (although it can be avoided) such as deepening the hierarchy when using json, so I thought that it was a good choice.
https://qiita.com/nijuya_o/items/0f512792c4db27913c7a https://qiita.com/k_ui/items/b6e1763d023da96ea46f
Recommended Posts