Spring WebClient调用微服务时@PATCH局部更新致非更新字段置空问题
我有Node.js和Java两个服务,想用PATCH方法仅更新请求体中的单个字段。直接向Node.js服务(http://localhost:8004/products/xxx)发送如下请求体时,只会更新size字段,其余字段保持不变:
{ "size": 42 }
但调用Java服务(http://localhost:8005/products/xxx),由其转发请求至Node.js服务时,虽更新了size字段,但其余字段均被置为null,结果如下:
Updated product { title: null, desc: null, img: null, categories: null, size: '42', price: null }
相关代码
Java代码
public Mono<Products> updateProduct(String id, Products editProduct){ return webClient .patch() .uri("/products/{id}", id) .body(Mono.just(editProduct), Products.class) .retrieve() .bodyToMono(Products.class); } @PatchMapping("/products/{id}") @ResponseStatus(code = HttpStatus.OK) public Mono<Products> updateProduct(@PathVariable("id") String id, @Validated @RequestBody Products editProduct){ return basketService.updateProduct(id, editProduct); }
Node.js代码
router.patch('/:id', async (req, res) => { try{ const productId = req.params.id const updates = req.body console.log("Updated product", updates) const result = await Product.findByIdAndUpdate(productId, updates) res.status(200).json(result); }catch(err) { res.status(500).json(err); } })
原因
Java的@RequestBody会将请求体序列化为完整的Products对象,未传入的字段会被默认赋值为null。当WebClient把这个包含null字段的完整对象发送给Node.js时,Node.js的findByIdAndUpdate默认会用传入的对象覆盖数据库记录——null值会直接把数据库中原有的字段值改成null。
而直接请求Node.js时,请求体只有size字段,所以只会更新该字段,其余字段不受影响。
解决办法
方案1:修改Java端,仅传递非null字段
方法A:用Map接收请求体
修改Java接口方法,用Map<String, Object>代替Products类接收请求体,这样只会保留传入的字段:
@PatchMapping("/products/{id}") @ResponseStatus(code = HttpStatus.OK) public Mono<Products> updateProduct(@PathVariable("id") String id, @RequestBody Map<String, Object> editProduct){ return basketService.updateProduct(id, editProduct); }
同时调整WebClient的调用逻辑:
public Mono<Products> updateProduct(String id, Map<String, Object> editProduct){ return webClient .patch() .uri("/products/{id}", id) .body(Mono.just(editProduct), Map.class) .retrieve() .bodyToMono(Products.class); }
方法B:给Products类添加Jackson注解忽略null字段
在Products类上添加@JsonInclude注解,让序列化时自动忽略null值:
@JsonInclude(JsonInclude.Include.NON_NULL) public class Products { // 类字段、getter/setter方法 }
这样WebClient发送请求时,只会携带非null的字段,和直接请求Node.js的请求体一致。
方案2:修改Node.js端,明确仅更新传入字段
在findByIdAndUpdate中使用$set操作符,确保只更新传入的字段(避免null值覆盖原有数据),同时添加new: true返回更新后的文档:
const result = await Product.findByIdAndUpdate( productId, { $set: updates }, { new: true, runValidators: true } )
注意:这个方案只能治标,如果Java端仍然传递null字段,还是会将对应数据库字段置为null,因此优先推荐修改Java端的方案。
内容的提问来源于stack exchange,提问作者semural

