Spring GraphQL中@BatchMapping抛出异常报错及List返回类型下的错误处理咨询
嘿,针对你遇到的Spring GraphQL @BatchMapping异常处理问题,我来给你梳理下情况和解决方案~
首先还原下你的场景:你用Spring GraphQL写了这两段代码:
@QueryMapping public Client clientById(Long id) { return dao.findById(id).orElse(null); } @BatchMapping(typeName = "Client") public List<Optional<LegalEntity>> legalEntity(List<Client> clients) { throw new UnsupportedOperationException("Not implemented yet"); }
运行后抛出了这个错误:
java.lang.IllegalStateException: Unexpected return value: MonoError
at org.springframework.graphql.data.method.annotation.support.BatchLoaderHandlerMethod.toFlux(BatchLoaderHandlerMethod.java:196) ~[spring-graphql-1.2.0.jar:1.2.0]
at org.springframework.graphql.data.method.annotation.support.BatchLoaderHandlerMethod.invokeForIterable(BatchLoaderHandlerMethod.java:102) ~[spring-graphql-1.2.0.jar:1.2.0]
....
你发现框架会把异常包装成Mono.error(..)返回,但却无法解析这个类型,导致自定义异常处理器也抓不到原始错误。换成返回Flux<Optional<LegalEntity>>并返回Flux.error就能正常工作,但你想知道有没有办法在返回List类型的情况下处理错误。
问题根源
Spring GraphQL的@BatchMapping在处理List这类Iterable返回类型时,内部会尝试把结果适配成反应式类型(比如Flux)。但当你直接抛出异常时,框架会把这个异常包装成Mono.error,但这个Mono并不是Iterable的子类,所以在转换环节就会抛出那个IllegalStateException——因为它预期拿到的是一个可迭代的结果列表,而不是一个错误信号。
解决方案
方案一:使用反应式返回类型(推荐)
其实Spring GraphQL的@BatchMapping本身就更推荐用Flux/Mono这类反应式类型作为返回值,这样框架可以直接处理错误信号,你的全局异常处理器也能正常拦截到错误。就像你已经尝试的那样:
@BatchMapping(typeName = "Client") public Flux<Optional<LegalEntity>> legalEntity(List<Client> clients) { return Flux.error(new UnsupportedOperationException("Not implemented yet")); }
这种方式是框架设计时的适配模式,后续的错误处理、批量数据加载都会更顺畅,也是官方更建议的写法。
方案二:坚持用List返回类型的手动处理方式
如果你确实需要返回List,那不能直接抛出异常,而是要把错误信息封装到返回的List元素里。不过这种方式需要你自己处理错误的传播逻辑,比如:
import java.util.stream.Collectors; @BatchMapping(typeName = "Client") public List<Optional<LegalEntity>> legalEntity(List<Client> clients) { try { // 这里写你的实际业务逻辑 throw new UnsupportedOperationException("Not implemented yet"); } catch (Exception e) { // 根据你的业务需求,为每个Client返回对应的结果(比如空Optional) // 注意:这种方式需要和你的GraphQL Schema定义匹配,如果要传递错误信息,可能需要调整Schema结构 return clients.stream() .map(client -> Optional.empty()) .collect(Collectors.toList()); } }
不过这种方式有个明显的问题:你没法用全局异常处理器统一处理错误,因为异常已经被你捕获并转换成了正常返回值,GraphQL会把它当成正常数据返回给客户端。如果需要在响应中体现错误,可能还要调整你的Schema,比如定义一个Error类型,然后用Union类型(比如LegalEntityOrError)来返回结果,这样才能把错误信息传递给前端。
总结
如果想让异常处理器正常工作,最好还是采用反应式返回类型的写法,这是最贴合Spring GraphQL设计的方式。如果一定要用List返回类型,只能手动捕获异常并封装到结果中,但这种方式不够优雅,也没法利用全局异常处理机制。
备注:内容来源于stack exchange,提问作者Stanislau Listratsenka

