Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
cc.shacocloud.mirage.web.bind.converter.json.AbstractJackson2HttpMessageConverter Maven / Gradle / Ivy
package cc.shacocloud.mirage.web.bind.converter.json;
import cc.shacocloud.mirage.web.AbstractHandlerMethodMapping;
import cc.shacocloud.mirage.web.HttpRequest;
import cc.shacocloud.mirage.web.HttpResponse;
import cc.shacocloud.mirage.web.exception.HttpMessageConversionException;
import cc.shacocloud.mirage.web.exception.HttpMessageNotReadableException;
import cc.shacocloud.mirage.web.exception.HttpMessageNotWritableException;
import cc.shacocloud.mirage.web.http.HttpMessageConverter;
import cc.shacocloud.mirage.web.http.MediaType;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.type.TypeFactory;
import cc.shacocloud.mirage.web.bind.converter.AbstractGenericHttpMessageConverter;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled;
import io.vertx.core.Future;
import io.vertx.core.buffer.Buffer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.jetbrains.annotations.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.TypeUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
/**
* 基于Jackson的和独立于内容类型的 {@link HttpMessageConverter}实现的抽象基类。
*
*
* 兼容Jackson 2.9 和更高版本
*
* @see Jackson2HttpMessageConverter
*/
public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter {
protected final Log logger = LogFactory.getLog(AbstractHandlerMethodMapping.class);
private static final Map ENCODINGS;
static {
ENCODINGS = new HashMap<>(JsonEncoding.values().length + 1);
for (JsonEncoding encoding : JsonEncoding.values()) {
ENCODINGS.put(encoding.getJavaName(), encoding);
}
ENCODINGS.put("US-ASCII", JsonEncoding.UTF8);
}
/**
* 转换器使用的默认字符集
*/
@Nullable
@Deprecated
public static final Charset DEFAULT_CHARSET = null;
protected ObjectMapper objectMapper;
@Nullable
private Boolean prettyPrint;
@Nullable
private final PrettyPrinter ssePrettyPrinter;
protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
prettyPrinter.indentObjectsWith(new DefaultIndenter(" ", "\ndata:"));
this.ssePrettyPrinter = prettyPrinter;
}
protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper, MediaType supportedMediaType) {
this(objectMapper);
setSupportedMediaTypes(Collections.singletonList(supportedMediaType));
}
protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper, MediaType... supportedMediaTypes) {
this(objectMapper);
setSupportedMediaTypes(Arrays.asList(supportedMediaTypes));
}
/**
* 为这个视图设置{@code ObjectMapper}。
* 如果没有设置,则使用默认的{@link ObjectMapper#ObjectMapper() }。
* 设置一个自定义配置的{@code ObjectMapper}是进一步控制JSON序列化过程的一种方法。
* 例如,扩展的 {@link com.fasterxml.jackson.databind.ser。可以配置为特定类型提供自定义序列化器的SerializerFactory}.
* 改进序列化过程的另一个选择是使用Jackson的提供的对要序列化的类型的注解,在这种情况下,一个自定义配置的ObjectMapper是不必要的。
*/
public void setObjectMapper(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "ObjectMapper 不能为空");
this.objectMapper = objectMapper;
configurePrettyPrint();
}
/**
* 返回此视图的底层{@code ObjectMapper}。
*/
public ObjectMapper getObjectMapper() {
return this.objectMapper;
}
/**
* 写JSON时是否使用{@link DefaultPrettyPrinter}。这是设置{@code ObjectMapper}的快捷方式,如下所示:
*
* ObjectMapper mapper = new ObjectMapper();
* mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
* converter.setObjectMapper(mapper);
*
*/
public void setPrettyPrint(boolean prettyPrint) {
this.prettyPrint = prettyPrint;
configurePrettyPrint();
}
private void configurePrettyPrint() {
if (this.prettyPrint != null) {
this.objectMapper.configure(SerializationFeature.INDENT_OUTPUT, this.prettyPrint);
}
}
@Override
public boolean canRead(MethodParameter parameter, Class> clazz, @Nullable MediaType mediaType) {
return canRead(parameter, clazz, null, mediaType);
}
@Override
public boolean canRead(MethodParameter parameter, Type type, @Nullable Class> contextClass, @Nullable MediaType mediaType) {
if (!canRead(mediaType)) {
return false;
}
JavaType javaType = getJavaType(type, contextClass);
AtomicReference causeRef = new AtomicReference<>();
if (this.objectMapper.canDeserialize(javaType, causeRef)) {
return true;
}
logWarningIfNecessary(javaType, causeRef.get());
return false;
}
@Override
public boolean canWrite(Class> clazz, @Nullable MediaType mediaType) {
if (!canWrite(mediaType)) {
return false;
}
if (mediaType != null && mediaType.getCharset() != null) {
Charset charset = mediaType.getCharset();
if (!ENCODINGS.containsKey(charset.name())) {
return false;
}
}
AtomicReference causeRef = new AtomicReference<>();
if (this.objectMapper.canSerialize(clazz, causeRef)) {
return true;
}
logWarningIfNecessary(clazz, causeRef.get());
return false;
}
/**
* 确定是否记录来自 {@link ObjectMapper#canDeserialize} 或 {@link ObjectMapper#canSerialize} 检查.
*
* @param type Jackson测试的(反)序列化性的类
* @param cause jackson抛出的异常,(通常是{@link JsonMappingException})
*/
protected void logWarningIfNecessary(Type type, @Nullable Throwable cause) {
if (cause == null) {
return;
}
// 不要记录未找到序列化器的警告(注意:Jackson 2.9上不同的消息措辞)
boolean debugLevel = (cause instanceof JsonMappingException && cause.getMessage().startsWith("Cannot find"));
if (debugLevel ? logger.isDebugEnabled() : logger.isWarnEnabled()) {
String msg = "未能评估 Jackson " + (type instanceof JavaType ? "的" : "") + "序列化类型 [" + type + "]";
if (debugLevel) {
logger.debug(msg, cause);
} else if (logger.isDebugEnabled()) {
logger.warn(msg, cause);
} else {
logger.warn(msg + ": " + cause);
}
}
}
@Override
protected Future readInternal(Class> clazz, HttpRequest request) {
try {
JavaType javaType = getJavaType(clazz, null);
Object arg = readJavaType(javaType, request);
return Future.succeededFuture(arg);
} catch (Exception e) {
return Future.failedFuture(e);
}
}
@Override
public Future read(MethodParameter parameter, Type type, @Nullable Class> contextClass, HttpRequest request) {
try {
JavaType javaType = getJavaType(type, contextClass);
Object arg = readJavaType(javaType, request);
return Future.succeededFuture(arg);
} catch (Exception e) {
return Future.failedFuture(e);
}
}
private Object readJavaType(JavaType javaType, HttpRequest request) throws IOException {
MediaType contentType = request.headers().getContentType();
Charset charset = getCharset(contentType);
boolean isUnicode = ENCODINGS.containsKey(charset.name());
try {
Buffer body = request.getBody();
if (body == null || body.length() == 0) return null;
JsonParser parser;
if (isUnicode) {
parser = objectMapper.getFactory()
.createParser((InputStream) new ByteBufInputStream(body.getByteBuf()));
} else {
parser = objectMapper.getFactory()
.createParser(new InputStreamReader(new ByteBufInputStream(body.getByteBuf()), charset));
}
return this.objectMapper.readValue(parser, javaType);
} catch (InvalidDefinitionException ex) {
throw new HttpMessageConversionException("类型定义错误: " + ex.getType(), ex);
} catch (JsonProcessingException ex) {
throw new HttpMessageNotReadableException("JSON解析错误: " + ex.getOriginalMessage(), ex, request);
}
}
private static Charset getCharset(@Nullable MediaType contentType) {
if (contentType != null && contentType.getCharset() != null) {
return contentType.getCharset();
} else {
return StandardCharsets.UTF_8;
}
}
@Override
protected Future writeInternal(Object object, @Nullable Type type, HttpResponse response) {
try {
MediaType contentType = response.request().headers().getContentType();
JsonEncoding encoding = getJsonEncoding(contentType);
ByteBuf byteBuf = Unpooled.unreleasableBuffer(Unpooled.buffer(0, Integer.MAX_VALUE));
try (ByteBufOutputStream output = new ByteBufOutputStream(byteBuf);
JsonGenerator generator = this.objectMapper.getFactory().createGenerator((OutputStream) output, encoding)) {
writeInternal(generator, type, object, contentType);
return Future.succeededFuture(Buffer.buffer(byteBuf));
} finally {
byteBuf.release();
}
} catch (Exception e) {
return Future.failedFuture(e);
}
}
protected void writeInternal(JsonGenerator generator, @Nullable Type type, Object object, MediaType contentType)
throws IOException, HttpMessageNotWritableException {
try {
writePrefix(generator, object);
Object value = object;
Class> serializationView = null;
FilterProvider filters = null;
JavaType javaType = null;
if (object instanceof MappingJacksonValue) {
MappingJacksonValue container = (MappingJacksonValue) object;
value = container.getValue();
serializationView = container.getSerializationView();
filters = container.getFilters();
}
if (type != null && TypeUtils.isAssignable(type, value.getClass())) {
javaType = getJavaType(type, null);
}
ObjectWriter objectWriter = (serializationView != null ? this.objectMapper.writerWithView(serializationView) : this.objectMapper.writer());
if (filters != null) {
objectWriter = objectWriter.with(filters);
}
if (javaType != null && javaType.isContainerType()) {
objectWriter = objectWriter.forType(javaType);
}
SerializationConfig config = objectWriter.getConfig();
if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) &&
config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
objectWriter = objectWriter.with(this.ssePrettyPrinter);
}
objectWriter.writeValue(generator, value);
writeSuffix(generator, object);
generator.flush();
} catch (InvalidDefinitionException ex) {
throw new HttpMessageConversionException("类型定义错误: " + ex.getType(), ex);
} catch (JsonProcessingException ex) {
throw new HttpMessageNotWritableException("不能写JSON: " + ex.getOriginalMessage(), ex);
}
}
/**
* 在主要内容之前写一个前缀
*
* @param generator 用于编写内容的生成器。
* @param object 要写入输出消息的对象。
*/
protected void writePrefix(JsonGenerator generator, Object object) throws IOException {
}
/**
* 在主要内容后面写一个后缀。
*
* @param generator 用于编写内容的生成器。
* @param object 要写入输出消息的对象。
*/
protected void writeSuffix(JsonGenerator generator, Object object) throws IOException {
}
/**
* 返回指定类型和上下文类的Jackson {@link JavaType}。
*
* @param type 要为其返回Jackson JavaType的泛型
* @param contextClass 目标类型的上下文类,例如目标类型出现在方法签名中的类(可以是{@code null})
* @return {@link JavaType}
*/
protected JavaType getJavaType(Type type, @Nullable Class> contextClass) {
TypeFactory typeFactory = this.objectMapper.getTypeFactory();
return typeFactory.constructType(GenericTypeResolver.resolveType(type, contextClass));
}
/**
* 确定用于给定内容类型的JSON编码。
*
* @param contentType 调用者所请求的媒体类型
* @return 要使用的JSON编码
*/
protected JsonEncoding getJsonEncoding(@Nullable MediaType contentType) {
if (contentType != null && contentType.getCharset() != null) {
Charset charset = contentType.getCharset();
JsonEncoding encoding = ENCODINGS.get(charset.name());
if (encoding != null) {
return encoding;
}
}
return JsonEncoding.UTF8;
}
@Override
@Nullable
protected MediaType getDefaultContentType(Object object) {
if (object instanceof MappingJacksonValue) {
object = ((MappingJacksonValue) object).getValue();
}
return super.getDefaultContentType(object);
}
@Override
protected Long getContentLength(Object object, @Nullable MediaType contentType) {
if (object instanceof MappingJacksonValue) {
object = ((MappingJacksonValue) object).getValue();
}
return super.getContentLength(object, contentType);
}
}