cc.shacocloud.mirage.web.bind.converter.AbstractHttpMessageConverter Maven / Gradle / Ivy
package cc.shacocloud.mirage.web.bind.converter;
import cc.shacocloud.mirage.web.HttpRequest;
import cc.shacocloud.mirage.web.HttpResponse;
import cc.shacocloud.mirage.web.http.HttpHeaderMap;
import cc.shacocloud.mirage.web.http.HttpMessageConverter;
import cc.shacocloud.mirage.web.http.MediaType;
import io.vertx.core.Future;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpHeaders;
import org.springframework.core.MethodParameter;
import org.jetbrains.annotations.Nullable;
import org.springframework.util.Assert;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* {@link HttpMessageConverter}实现的抽象基类。
*
*
* 这个基类通过 {@link #setSupportedMediaTypes(List) supportedMediaTypes} bean属性添加了对设置{@code MediaTypes}的支持。
* 它还在写入输出消息时增加了对{@link HttpHeaders#CONTENT_TYPE}和{@link HttpHeaders#CONTENT_LENGTH}的支持。
*
* @param 转换后的对象类型
*/
public abstract class AbstractHttpMessageConverter implements HttpMessageConverter {
private List supportedMediaTypes = Collections.emptyList();
@Nullable
private Charset defaultCharset;
/**
* 构造一个不支持媒体类型的{@code AbstractHttpMessageConverter}。
*
* @see #setSupportedMediaTypes
*/
protected AbstractHttpMessageConverter() {
}
/**
* 构造具有一种支持的媒体类型的{@code AbstractHttpMessageConverter}。
*
* @param supportedMediaType 支持的媒体类型
*/
protected AbstractHttpMessageConverter(MediaType supportedMediaType) {
setSupportedMediaTypes(Collections.singletonList(supportedMediaType));
}
/**
* 构造具有多种支持媒体类型的{@code AbstractHttpMessageConverter}。
*
* @param supportedMediaTypes 支持的媒体类型
*/
protected AbstractHttpMessageConverter(MediaType... supportedMediaTypes) {
setSupportedMediaTypes(Arrays.asList(supportedMediaTypes));
}
/**
* 构造带有默认字符集和多种支持的媒体类型的{@code AbstractHttpMessageConverter}。
*
* @param defaultCharset 默认字符集
* @param supportedMediaTypes 支持的媒体类型
*/
protected AbstractHttpMessageConverter(Charset defaultCharset, MediaType... supportedMediaTypes) {
this.defaultCharset = defaultCharset;
setSupportedMediaTypes(Arrays.asList(supportedMediaTypes));
}
/**
* 设置此转换器支持的{@link MediaType}对象列表
*/
public void setSupportedMediaTypes(List supportedMediaTypes) {
Assert.notEmpty(supportedMediaTypes, "MediaType 列表不能为空");
this.supportedMediaTypes = new ArrayList<>(supportedMediaTypes);
}
@Override
public List getSupportedMediaTypes() {
return Collections.unmodifiableList(this.supportedMediaTypes);
}
/**
* 设置默认字符集(如果有的话)。
*/
public void setDefaultCharset(@Nullable Charset defaultCharset) {
this.defaultCharset = defaultCharset;
}
/**
* 如果有的话,返回默认字符集。
*/
@Nullable
public Charset getDefaultCharset() {
return this.defaultCharset;
}
/**
* 这个实现检查给定的类是否为{@linkplain #supports },
* 以及{@linkplain #getSupportedMediaTypes } {@linkplain MediaType#includes }给定的媒体类型。
*/
@Override
public boolean canRead(MethodParameter parameter, Class> clazz, @Nullable MediaType mediaType) {
return supports(clazz) && canRead(mediaType);
}
/**
* 如果任何{@linkplain #setSupportedMediaTypes }媒体类型包括{@link MediaType#includes}给定的媒体类型,返回{@code true}
*
* @param mediaType 要读取的媒体类型,如果没有指定,可以是{@code null}。通常是{@code Content-Type}头的值。
* @return 支持的媒体类型与媒体类型兼容或媒体类型为{@code null}则为{@code true}
*/
protected boolean canRead(@Nullable MediaType mediaType) {
if (mediaType == null) {
return true;
}
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.includes(mediaType)) {
return true;
}
}
return false;
}
/**
* 这个实现检查给定的类是否 {@linkplain #supports },
* 以及 {@linkplain #getSupportedMediaTypes}媒体类型 {@linkplain MediaType#includes}包含给定的媒体类型。
*/
@Override
public boolean canWrite(Class> clazz, @Nullable MediaType mediaType) {
return supports(clazz) && canWrite(mediaType);
}
/**
* 如果给定的媒体类型包括任何{@linkplain #setSupportedMediaTypes(List) },则返回{@code true}。
*
* @param mediaType 要写入的媒体类型,如果没有指定,可以是{@code null}。通常是{@code Accept}头的值。
* @return 支持的媒体类型与媒体类型兼容或媒体类型为{@code null}则为{@code true}
*/
protected boolean canWrite(@Nullable MediaType mediaType) {
if (mediaType == null || MediaType.ALL.equalsTypeAndSubtype(mediaType)) {
return true;
}
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
if (supportedMediaType.isCompatibleWith(mediaType)) {
return true;
}
}
return false;
}
/**
* 这个实现简单地委托给{@link #readInternal(Class, HttpRequest)}。以后的实现可能会添加一些默认行为。
*/
@Override
public final Future read(MethodParameter parameter, Class extends T> clazz, HttpRequest request) {
return readInternal(clazz, request);
}
/**
* 这个实现通过调用{@link #addDefaultHeaders}来设置默认的头,然后调用{@link #writeInternal}。
*/
@Override
public final Future write(final T t, @Nullable MediaType contentType, HttpResponse response) {
final HttpHeaderMap headers = response.headers();
// 设置默认头
addDefaultHeaders(headers, t, contentType);
return writeInternal(t, response);
}
/**
* 向输出消息添加默认标题。
*
* 如果没有提供内容类型,此实现将委托给{@link #getDefaultContentType(Object)},必要时设置默认字符集。
* 调用{@link #getContentLength},并设置相应的头部。
*
* @since 4.2
*/
protected void addDefaultHeaders(HttpHeaderMap headers, T t, @Nullable MediaType contentType) {
if (headers.getContentType() == null) {
MediaType contentTypeToUse = contentType;
if (contentType == null || !contentType.isConcrete()) {
contentTypeToUse = getDefaultContentType(t);
} else if (MediaType.APPLICATION_OCTET_STREAM.equals(contentType)) {
MediaType mediaType = getDefaultContentType(t);
contentTypeToUse = (mediaType != null ? mediaType : contentTypeToUse);
}
if (contentTypeToUse != null) {
if (contentTypeToUse.getCharset() == null) {
Charset defaultCharset = getDefaultCharset();
if (defaultCharset != null) {
contentTypeToUse = new MediaType(contentTypeToUse, defaultCharset);
}
}
headers.setContentType(contentTypeToUse);
}
}
if (headers.getContentLength() < 0 && !headers.contains(HttpHeaders.TRANSFER_ENCODING)) {
Long contentLength = getContentLength(t, headers.getContentType());
if (contentLength != null) {
headers.setContentLength(contentLength);
}
}
}
/**
* 返回给定类型的默认内容类型。
*
*
* 默认情况下,它返回{@link #setSupportedMediaTypes(List)}属性的第一个元素(如果有的话)。
* 可以在子类中被重写。
*
* @param t the type to return the content type for
* @return the content type, or {@code null} if not known
*/
@Nullable
protected MediaType getDefaultContentType(T t) {
List mediaTypes = getSupportedMediaTypes();
return (!mediaTypes.isEmpty() ? mediaTypes.get(0) : null);
}
/**
* 返回给定类型的内容长度
*
* 默认情况下,这会返回{@code null},这意味着内容长度是未知的。可以在子类中被重写。
*
* @param t 返回内容长度的类型
* @return 内容长度, (如果不知道{@code null})
*/
@Nullable
protected Long getContentLength(T t, @Nullable MediaType contentType) {
return null;
}
/**
* 指示此转换器是否支持给定的类。
*
* @param clazz 要测试以获得支持的类
* @return 如果支持则为{@code true}反之为{@code false}
*/
protected abstract boolean supports(Class> clazz);
/**
* 读取实际对象的抽象模板方法。从{@link HttpMessageConverter#read}调用。
*
* @param clazz 要返回的对象类型
* @param request 要从中读取的HTTP输入消息
* @return 转换对象
*/
protected abstract Future readInternal(Class extends T> clazz, HttpRequest request);
/**
* 编写实际主体的抽象模板方法。从{@link HttpMessageConverter#write}调用。
*
* @param t 要写入输出消息的对象
* @param response 要写入的HTTP输出消息
* @return {@link } 解析后的结果
*/
protected abstract Future writeInternal(T t, HttpResponse response);
}