All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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); } }