Spring WebFlux文件下载API如何兼容空请求体?
Spring Boot WebFlux 文件下载API支持空请求体的解决方案
问题背景
我使用Spring Boot WebFlux构建了一个文件下载API,相关代码如下:
public Mono<ServerResponse> downloadAttachment(final ServerRequest request) { final var idPercorso = request.queryParam("idPercorso") .map(Integer::parseInt).orElse(null); } Mono<ServerResponse> additionalCalcualtions(MapFile mapFile, ServerRequest request){ return Mono.zip(parseUserInfo(request), request.bodyToMono(ObjectNode.class)) .flatMap(t -> { //var dto = t.getT2(); return silverMountainService(t.getT1()).downloadMappaPercorso(StorageType.parse(mapFile.storageType),mapFile.omniaClass, mapFile.getFileName() ) .flatMap(fileResult -> ServerResponse.ok() .headers(h -> h.setContentDisposition( Optional.ofNullable(mapFile.getFileName()) .filter(StringUtils::isNotBlank) .map(filename -> ContentDisposition.attachment().filename(mapFile.getFileName()).build()) .orElseGet(() -> ContentDisposition.inline().build() ) )) .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(BodyInserters.fromValue(fileResult.getContent())) ); }) .onErrorResume(CustomHttpException.class, assEx -> { log.error("Error({}): {}", assEx.getErrorId(), assEx.getMessage()); return ServerResponse.status(assEx.getHttpStatus()).bodyValue(String.format("ErrorID: %s", assEx.getErrorId())); }) .onErrorResume(Exception.class, ex -> { var errId = UUID.randomUUID().toString(); log.error(String.format("Error(%s) %s", errId, ex.getMessage()), ex); return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR).bodyValue(String.format("ErrorID: %s", errId)); }); }
接口调用地址为:myUrl/downloadMappa?idPercorso=2
当前问题:当请求携带空JSON体{}时接口可正常工作,但不带请求体时会返回空响应,需要修改代码支持空请求体的场景。
解决方案
核心问题在于request.bodyToMono(ObjectNode.class)在无请求体时会生成一个空的Mono,导致Mono.zip因缺少其中一个输入值而无法触发后续逻辑,最终返回空响应。只需给获取请求体的逻辑添加空值兜底即可:
修改后的additionalCalcualtions方法代码:
Mono<ServerResponse> additionalCalcualtions(MapFile mapFile, ServerRequest request){ // 给bodyToMono添加空值兜底,无请求体时返回null return Mono.zip(parseUserInfo(request), request.bodyToMono(ObjectNode.class).defaultIfEmpty(null)) .flatMap(t -> { // var dto = t.getT2(); // 代码未用到请求体内容,null不影响逻辑 return silverMountainService(t.getT1()).downloadMappaPercorso(StorageType.parse(mapFile.storageType),mapFile.omniaClass, mapFile.getFileName()) .flatMap(fileResult -> ServerResponse.ok() .headers(h -> h.setContentDisposition( Optional.ofNullable(mapFile.getFileName()) .filter(StringUtils::isNotBlank) .map(filename -> ContentDisposition.attachment().filename(mapFile.getFileName()).build()) .orElseGet(() -> ContentDisposition.inline().build() ) )) .contentType(MediaType.APPLICATION_OCTET_STREAM) .body(BodyInserters.fromValue(fileResult.getContent())) ); }) .onErrorResume(CustomHttpException.class, assEx -> { log.error("Error({}): {}", assEx.getErrorId(), assEx.getMessage()); return ServerResponse.status(assEx.getHttpStatus()).bodyValue(String.format("ErrorID: %s", assEx.getErrorId())); }) .onErrorResume(Exception.class, ex -> { var errId = UUID.randomUUID().toString(); log.error(String.format("Error(%s) %s", errId, ex.getMessage()), ex); return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR).bodyValue(String.format("ErrorID: %s", errId)); }); }
修改说明
- 使用
.defaultIfEmpty(null)给请求体Mono添加兜底值,当无请求体时会发出null而非空Mono,确保Mono.zip能获取到两个输入值并触发后续逻辑 - 由于代码中并未实际使用请求体内容(已注释
var dto = t.getT2()),传入null不会影响业务逻辑 - 也可以用
.switchIfEmpty(Mono.just(null))替代.defaultIfEmpty(null),效果完全一致
内容的提问来源于stack exchange,提问作者bircastri
相关产品推荐
相关产品推荐

