栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

webflux 响应统一返回

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

webflux 响应统一返回

主要原理是在序列化之前把接口返回值放入自定义结构体,然后按照继续按照框架的逻辑继续执行.
这个方法相比于网上搜到的方法,少了一次反序列化和一次序列化

@Configuration
public class CodecConfig {
    @Bean
    @Primary
    @Order(Integer.MIN_VALUE)
    CodecConfigurer codecConfigurer() {
        // 配置json序列化策略
        ObjectMapper objectMapper = new ObjectMapper();
        JavaTimeModule timeModule = new JavaTimeModule();
        timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

        objectMapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.registerModule(timeModule)
                .registerModule(new ParameterNamesModule()).registerModules(ObjectMapper.findModules());

        // 配置统一响应
        BaseResultHandler baseResultHandler = new BaseResultHandler(objectMapper);
        ServerCodecConfigurer serverCodecConfigurer = ServerCodecConfigurer.create();
        ServerCodecConfigurer.ServerDefaultCodecs serverDefaultCodecs = serverCodecConfigurer.defaultCodecs();
        serverDefaultCodecs.jackson2JsonEncoder(baseResultHandler);
        return serverCodecConfigurer;
    }

    @Bean
    @Primary
    ResponseBodyResultHandler resultHandler(@Qualifier("webFluxAdapterRegistry") ReactiveAdapterRegistry reactiveAdapterRegistry,
                                            ServerCodecConfigurer serverCodecConfigurer,
                                            @Qualifier("webFluxContentTypeResolver") RequestedContentTypeResolver contentTypeResolver) {
        return new ResultHandler(serverCodecConfigurer.getWriters(), contentTypeResolver, reactiveAdapterRegistry);
    }

    static class BaseResultHandler extends Jackson2JsonEncoder {
        public BaseResultHandler(ObjectMapper mapper, MimeType... mimeTypes) {
            super(mapper, mimeTypes);
        }

        @Override
        public Flux encode(Publisher inputStream, DataBufferFactory bufferFactory, ResolvableType elementType, MimeType mimeType, Map hints) {
            if (inputStream instanceof Mono) {
                // 处理单一对象类型数据
                inputStream = Mono.from(inputStream)
                        .map(body -> body instanceof Response ? body : Response.ok(body))
                        .defaultIfEmpty(Response.ok(null));
            } else {
                // 处理集合类型数据
                if (getStreamingMediaTypeSeparator(mimeType) == null) {
                    inputStream = Flux.from(inputStream)
                            .collectList()
                            // 处理分页
                            .zipWith(PageUtil.get(), (body, count) -> body instanceof Response ? body : MultiResponse.ok(count, body))
                            .defaultIfEmpty(MultiResponse.ok(0, Collections.emptyList()));
                }
            }
            return super.encode(inputStream, bufferFactory, elementType, mimeType, hints);
        }
    }
}

到此,除了流式返回的数据外,自己创建的类型都能统一返回了.
如果需要把框架内部定义的数据类型(比如:String,void)也放在自己的响应结构中的话,需要继续往上追溯

public abstract class AbstractMessageWriterResultHandler extends HandlerResultHandlerSupport {
	protected Mono writeBody(@Nullable Object body, MethodParameter bodyParameter,
			@Nullable MethodParameter actualParam, ServerWebExchange exchange) {
		if (adapter != null) {
			publisher = adapter.toPublisher(body);
			boolean isUnwrapped = KotlinDetector.isSuspendingFunction(bodyParameter.getMethod()) &&
					!COROUTINES_FLOW_CLASS_NAME.equals(bodyType.toClass().getName());
			ResolvableType genericType = isUnwrapped ? bodyType : bodyType.getGeneric();
			elementType = getElementType(adapter, genericType);
			actualElementType = elementType;
		}	
		...
		if (elementType.resolve() == void.class || elementType.resolve() == Void.class) {
			return Mono.from((Publisher) publisher);
		}
		...
		if (bestMediaType != null) {
			String logPrefix = exchange.getLogPrefix();
			if (logger.isDebugEnabled()) {
				logger.debug(logPrefix +
						(publisher instanceof Mono ? "0..1" : "0..N") + " [" + elementType + "]");
			}
			for (HttpMessageWriter writer : getMessageWriters()) {
				if (writer.canWrite(actualElementType, bestMediaType)) {
					return writer.write((Publisher) publisher, actualType, elementType,
							bestMediaType, exchange.getRequest(), exchange.getResponse(),
							Hints.from(Hints.LOG_PREFIX_HINT, logPrefix));
				}
			}
		}
		...
	}
}

其中有两个关键的方法 getElementType(), getMessageWriters()
getElementType(): 接口返回空值会返回Void,在下一个if中会直接返回空的Mono,后续的操作都会被跳过

getMessageWriters(): 包含了一个字符处理器

代表遇上字符的数据类型都会应用这种特殊处理方式,而text/plain;charset=UTF-8不属于json序列化的范畴,所以上边写的自定义响应处理类失效了

所以小机灵干脆直接覆盖ResponseBodyResultHandler,把待处理类型偷偷替换了,直接走对象的处理方式

public class ResultHandler extends ResponseBodyResultHandler {
    
    private static final List> IGNORE = new ArrayList<>();
    static {
        IGNORE.add(String.class);
    }

    public ResultHandler(List> writers, RequestedContentTypeResolver resolver, ReactiveAdapterRegistry registry) {
        super(writers, resolver, registry);
    }

    @Override
    @SuppressWarnings({"unchecked", "rawtypes", "ConstantConditions"})
    protected Mono writeBody(@Nullable Object body, MethodParameter bodyParameter,
                                   @Nullable MethodParameter actualParam, ServerWebExchange exchange) {

        ResolvableType bodyType = ResolvableType.forMethodParameter(bodyParameter);
        ResolvableType actualType = (actualParam != null ? ResolvableType.forMethodParameter(actualParam) : bodyType);
        ReactiveAdapter adapter = getAdapterRegistry().getAdapter(bodyType.resolve(), body);

        Publisher publisher;
        ResolvableType elementType;
        ResolvableType actualElementType;
        if (adapter != null) {
            publisher = adapter.toPublisher(body);
            boolean isUnwrapped = KotlinDetector.isSuspendingFunction(bodyParameter.getMethod()) &&
                    !COROUTINES_FLOW_CLASS_NAME.equals(bodyType.toClass().getName());
            ResolvableType genericType = isUnwrapped ? bodyType : bodyType.getGeneric();
            elementType = getElementType(genericType);
            actualElementType = elementType;
        } else {
            publisher = Mono.justOrEmpty(body);
            actualElementType = body != null ? ResolvableType.forInstance(body) : bodyType;
            elementType = (bodyType.toClass() == Object.class && body != null ? actualElementType : bodyType);
        }

        MediaType bestMediaType;
        try {
            bestMediaType = selectMediaType(exchange, () -> getMediaTypesFor(elementType));
        } catch (NotAcceptableStatusException ex) {
            HttpStatus statusCode = exchange.getResponse().getStatusCode();
            if (statusCode != null && statusCode.isError()) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Ignoring error response content (if any). " + ex.getReason());
                }
                return Mono.empty();
            }
            throw ex;
        }
        if (bestMediaType != null) {
            String logPrefix = exchange.getLogPrefix();
            if (logger.isDebugEnabled()) {
                logger.debug(logPrefix +
                        (publisher instanceof Mono ? "0..1" : "0..N") + " [" + elementType + "]");
            }
            for (HttpMessageWriter writer : getMessageWriters()) {
                if (writer.canWrite(actualElementType, bestMediaType)) {
                    return writer.write((Publisher) publisher, actualType, elementType,
                            bestMediaType, exchange.getRequest(), exchange.getResponse(),
                            Hints.from(Hints.LOG_PREFIX_HINT, logPrefix));
                }
            }
        }

        MediaType contentType = exchange.getResponse().getHeaders().getContentType();
        boolean isPresentMediaType = (contentType != null && contentType.equals(bestMediaType));
        Set producibleTypes = exchange.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
        if (isPresentMediaType || !CollectionUtils.isEmpty(producibleTypes)) {
            return Mono.error(new HttpMessageNotWritableException(
                    "No Encoder for [" + elementType + "] with preset Content-Type '" + contentType + "'"));
        }

        List mediaTypes = getMediaTypesFor(elementType);
        if (bestMediaType == null && mediaTypes.isEmpty()) {
            return Mono.error(new IllegalStateException("No HttpMessageWriter for " + elementType));
        }

        return Mono.error(new NotAcceptableStatusException(mediaTypes));
    }

    private ResolvableType getElementType(ResolvableType genericType) {
        // 去除了空值和String
        if (genericType == ResolvableType.NONE || IGNORE.contains(genericType.resolve())) {
            return ResolvableType.forClass(Object.class);
        }
        return genericType;
    }

    private List getMediaTypesFor(ResolvableType elementType) {
        List writableMediaTypes = new ArrayList<>();
        for (HttpMessageWriter converter : getMessageWriters()) {
            if (converter.canWrite(elementType, null)) {
                writableMediaTypes.addAll(converter.getWritableMediaTypes(elementType));
            }
        }
        return writableMediaTypes;
    }
}
暑期编程PK赛 得CSDN机械键盘等精美礼品!
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1017047.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号