package org.springblade.core.tool.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springblade.core.tool.utils.Charsets;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.StreamUtils;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
/**
* 针对 api 服务对 android 和 ios 处理的 分读写的 jackson 处理
*
*
* 1. app 端上报数据是 使用 readObjectMapper
* 2. 返回给 app 端的数据使用 writeObjectMapper
* 3. 如果是返回字符串,直接相应,不做 json 处理
*
*
* @author L.cm
*/
public class MappingApiJackson2HttpMessageConverter extends AbstractReadWriteJackson2HttpMessageConverter {
@Nullable
private String jsonPrefix;
public MappingApiJackson2HttpMessageConverter(ObjectMapper objectMapper, BladeJacksonProperties properties) {
super(objectMapper, initWriteObjectMapper(objectMapper, properties), initMediaType(properties));
}
private static List initMediaType(BladeJacksonProperties properties) {
List supportedMediaTypes = new ArrayList<>();
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
supportedMediaTypes.add(new MediaType("application", "*+json"));
// 支持 text 文本,用于报文签名
if (Boolean.TRUE.equals(properties.getSupportTextPlain())) {
supportedMediaTypes.add(MediaType.TEXT_PLAIN);
}
return supportedMediaTypes;
}
private static ObjectMapper initWriteObjectMapper(ObjectMapper readObjectMapper, BladeJacksonProperties properties) {
// 拷贝 readObjectMapper
ObjectMapper writeObjectMapper = readObjectMapper.copy();
// 大数字 转 字符串
if (Boolean.TRUE.equals(properties.getBigNumToString())) {
writeObjectMapper.registerModules(BladeNumberModule.INSTANCE);
}
// null 处理
if (Boolean.TRUE.equals(properties.getNullToEmpty())) {
writeObjectMapper.setSerializerFactory(writeObjectMapper.getSerializerFactory().withSerializerModifier(new BladeBeanSerializerModifier()));
writeObjectMapper.getSerializerProvider().setNullValueSerializer(BladeBeanSerializerModifier.NullJsonSerializers.STRING_JSON_SERIALIZER);
}
return writeObjectMapper;
}
@Override
protected void writeInternal(@NonNull Object object, @Nullable Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
// 如果是字符串,直接写出
if (object instanceof String) {
Charset defaultCharset = this.getDefaultCharset();
Charset charset = defaultCharset == null ? Charsets.UTF_8 : defaultCharset;
StreamUtils.copy((String) object, charset, outputMessage.getBody());
} else {
super.writeInternal(object, type, outputMessage);
}
}
/**
* Specify a custom prefix to use for this view's JSON output.
* Default is none.
*
* @param jsonPrefix jsonPrefix
* @see #setPrefixJson
*/
public void setJsonPrefix(@Nullable String jsonPrefix) {
this.jsonPrefix = jsonPrefix;
}
/**
* Indicate whether the JSON output by this view should be prefixed with ")]}', ". Default is false.
* Prefixing the JSON string in this manner is used to help prevent JSON Hijacking.
* The prefix renders the string syntactically invalid as a script so that it cannot be hijacked.
* This prefix should be stripped before parsing the string as JSON.
*
* @param prefixJson prefixJson
* @see #setJsonPrefix
*/
public void setPrefixJson(boolean prefixJson) {
this.jsonPrefix = (prefixJson ? ")]}', " : null);
}
@Override
protected void writePrefix(@NonNull JsonGenerator generator, @NonNull Object object) throws IOException {
if (this.jsonPrefix != null) {
generator.writeRaw(this.jsonPrefix);
}
}
}