【发布时间】:2021-01-01 14:04:58
【问题描述】:
我正在尝试使用 WebClient 将贷款对象发布到另一个微服务,该微服务将该对象保存在数据库中。所以理论上body(JSON贷款对象)应该只是传递给DB服务的API。不知何故,我无法弄清楚如何做到这一点。
这是接受 JSON 贷款对象的 API:
映射:localhost:8081/loans
@PostMapping
public <T extends Loan> void addLoan(@Valid @NonNull @RequestBody T loan) {
loanService.createLoan(loan);
}
然后它调用loanService,它应该将贷款对象传递给DB-service API
public <T extends Loan> T createLoan(T loan) {
ParameterizedTypeReference<T> typeReference = new ParameterizedTypeReference<T>(){};
T a = client.post().uri("/loans").body(BodyInserters.fromValue(loan)).retrieve().bodyToMono(typeReference).block();
return a;
}
这是该数据库服务的 API: 映射:localhost:8080/api/v1/loans
@PostMapping
@ResponseBody
public <T extends Loan> T createLoan(@RequestBody T loan) {
return loanService.createLoan(loan);
}
这是它的服务:
public <T extends Loan> T createLoan(T Loan) {
return (T) loanRepository.save(Loan);
}
如果我只是将贷款对象直接传递给 DB 服务 API,那么一切正常。但如果我将它传递给其他 API,我会收到以下错误:
"status": 500,
"error": "Internal Server Error",
"trace": "org.springframework.web.reactive.function.client.WebClientResponseException$InternalServerError: 500 Internal Server Error from POST http://localhost:8080/api/v1/loans/\n\tat org.springframework.web.reactive.function.client.WebClientResponseException.create(WebClientResponseException.java:201)\n\tSuppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: \nError has been observed at the following site(s):\n\t|_ checkpoint ⇢ 500 from POST http://localhost:8080/api/v1/loans/ [DefaultWebClient]\nStack trace:\n\t\tat org.springframework.web.reactive.function.client.WebClientResponseException.create(WebClientResponseException.java:201)\n\t\tat org.springframework.web.reactive.function.client.DefaultClientResponse.lambda$createException$1(DefaultClientResponse.java:216)\n\t\tat reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:106)\n\t\tat reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)\n\t\tat reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:99)\n\t\tat reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:127)\n\t\tat reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107)\n\t\tat reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:295)\n\t\tat reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:337)\n\t\tat reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1784)\n\t\tat reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:159)\n\t\tat reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142)\n\t\tat reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:259)\n\t\tat reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142)\n\t\tat reactor.netty.channel.FluxReceive.onInboundComplete(FluxReceive.java:383)\n\t\tat reactor.netty.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:396)\n\t\tat reactor.netty.channel.ChannelOperations.terminate(ChannelOperations.java:452)\n\t\tat reactor.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:664)\n\t\tat reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:94)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\t\tat io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\t\tat io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)\n\t\tat io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324)\n\t\tat io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296)\n\t\tat io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)\n\t\tat io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)\n\t\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)\n\t\tat io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)\n\t\tat io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:795)\n\t\tat io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:480)\n\t\tat io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:378)\n\t\tat io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)\n\t\tat io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)\n\t\tat io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)\n\t\tat java.base/java.lang.Thread.run(Thread.java:832)\n\tSuppressed: java.lang.Exception: #block terminated with an error\n\t\tat reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:99)\n\t\tat reactor.core.publisher.Mono.block(Mono.java:1679)\n\t\tat de.rwth.swc.lab.ws2021.daifu.businesslogic.services.LoanService.createLoan(LoanService.java:39)\n\t\tat de.rwth.swc.lab.ws2021.daifu.businesslogic.api.LoanController.addLoan(LoanController.java:28)\n\t\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\t\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)\n\t\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n\t\tat java.base/java.lang.reflect.Method.invoke(Method.java:564)\n\t\tat org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)\n\t\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141)\n\t\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)\n\t\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:893)\n\t\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:807)\n\t\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\n\t\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1061)\n\t\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:961)\n\t\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\n\t\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)\n\t\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:652)\n\t\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\n\t\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:733)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\t\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\t\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\n\t\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\t\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\n\t\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\t\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\n\t\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\t\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\t\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)\n\t\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)\n\t\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)\n\t\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)\n\t\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\n\t\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)\n\t\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\n\t\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)\n\t\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\n\t\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)\n\t\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)\n\t\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n\t\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)\n\t\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)\n\t\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\n\t\tat java.base/java.lang.Thread.run(Thread.java:832)\n",
"message": "500 Internal Server Error from POST http://localhost:8080/api/v1/loans/",
"path": "/loans/"
这是服务器端错误:
Servlet.service() for servlet [dispatcherServlet] in context with path [/api/v1] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value : de.rwth.swc.lab.ws2021.daifu.dataservice.data.models.loans.PrivateLoan.customer; nested exception is org.hibernate.PropertyValueException: not-null property references a null or transient value : de.rwth.swc.lab.ws2021.daifu.dataservice.data.models.loans.PrivateLoan.customer] with root cause org.hibernate.PropertyValueException: not-null property references a null or transient value : de.rwth.swc.lab.ws2021.daifu.dataservice.data.models.loans.PrivateLoan.customer
最后,这是 POST 正文:
{
"amount": 10000.00,
"balance": -2000.00,
"customer": {"id": 1},
"interest": 0.06,
"status": "TIMELY",
"reason": "Some reaseon",
"type": "privateLoan"
}
错误提示“非空属性引用空值或瞬态值”,但完全相同的请求适用于对第二个 API 的直接 POST 请求,这对我来说没有意义。
这是贷款类别:
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@Getter
@Setter
@NoArgsConstructor
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = CarLoan.class, name = "carLoan"),
@JsonSubTypes.Type(value = ConstructionLoan.class, name = "constructionLoan"),
@JsonSubTypes.Type(value = Mortgage.class, name = "mortgage"),
@JsonSubTypes.Type(value = PrivateLoan.class, name = "privateLoan"),
@JsonSubTypes.Type(value = PropertyLoan.class, name = "propertyLoan")
})
@ApiModel(
discriminator = "type",
subTypes = {CarLoan.class, ConstructionLoan.class, Mortgage.class, PrivateLoan.class, PropertyLoan.class}
)
public abstract class Loan {
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
@Column(name = "id")
@ApiModelProperty(required = false, hidden = true)
protected Integer id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "customer_id", nullable = false)
@JsonBackReference(value = "customer-loans")
protected Customer customer;
@OneToMany(mappedBy = "loan", cascade = CascadeType.ALL)
@JsonManagedReference(value = "loan-loanRates")
private Set<LoanRate> loanRates;
@NonNull
protected Double amount;
@NonNull
protected Double interest;
@NonNull
protected Double balance;
@NonNull
protected LoanStatus status;
public enum LoanStatus {
TIMELY("timely"),
GRACE_PERIOD("grace period"),
DEFAULT("default"),
DEFICIT("deficit"),
IRRECOVERABLE_DEBT("irrecoverable debt"),
CLOSED("closed");
@Getter
private String stringRepresentation;
private LoanStatus(String s) {
this.stringRepresentation = s;
}
}
public <T extends Loan> boolean isOfSameInstance(T otherLoan) {
return (this.getClass().equals(otherLoan.getClass()));
}
}
如果我应该发布其他任何内容,请告诉我。 提前致谢。
【问题讨论】:
-
这个例子很混乱。根据您的解释,似乎有两个单独的 Web 应用程序。您能否为@PostMapping 添加路径,以便我们可以查看控制器方法绑定到哪些路径。两个控制器(单独的应用程序)都有一个名为“loanService”的依赖项,这使得阅读起来很混乱。报错是服务端的500错误,能不能从发生内部错误的服务器端打印日志?
-
@rewolf 感谢您的评论。我刚刚添加了您的建议。希望这会有所帮助。
-
谢谢!我认为查看模型类会有所帮助。唯一有意义的是它在重新序列化时删除了该属性。看到模型类会有所帮助。这两个服务是否使用与 DTO 相同的模型类?
-
您能添加您使用的模型类吗? rewolf 是对的,问题可能是由于两个项目中都使用了模型类。
标签: spring spring-boot post webclient spring-webclient