调用SpringBoot Webflux接口删除MongoDB条目时报JSON错误
问题场景
客户端向SpringBoot WebFlux后端发起请求删除MongoDB文档时触发JSON解析报错,相关代码如下:
前端代码
fetch("http://localhost:8080/shifts/deleteShift", { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json", }, credentials: "include", body: JSON.stringify(UID), }) .then((response) => response.json()) .then((data) => { console.log(data); setDeleteShift(false); }) .catch((err) => console.log(err));
后端代码
Controller层
@ResponseStatus(HttpStatus.OK) @PostMapping("/deleteShift") private Mono<Void> deleteShift(@RequestBody String ID) { return shiftService.deleteShift(ID); }
Service层
public Mono<Void> deleteShift(String ID){ return this.shiftRepository.deleteById(ID); }
Repository层
@Repository public interface ShiftRepository extends ReactiveMongoRepository<Shift, String> { }
报错截图
问题根因
- 前端逻辑缺陷:接口返回
Mono<Void>时响应体为空,前端直接对空响应调用response.json()会触发JSON解析语法错误,这是报错的直接原因。 - 后端代码错误:Controller中处理请求的
deleteShift方法被定义为private私有方法,Spring无法正常代理映射该接口,会导致请求无法被正确处理。
修复方案
前端修改
调整响应处理逻辑,不要对空响应强行解析JSON,增加响应类型判断:
fetch("http://localhost:8080/shifts/deleteShift", { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json", }, credentials: "include", body: JSON.stringify(UID), }) .then(async (response) => { if (!response.ok) throw new Error(`请求异常,状态码:${response.status}`); const contentType = response.headers.get("content-type"); // 只有响应是JSON格式时才做解析 if (contentType?.includes("application/json")) { return response.json(); } return; }) .then(() => { setDeleteShift(false); }) .catch((err) => console.log(err));
后端修改
- 将Controller层请求处理方法的访问修饰符改为
public,保证Spring可以正常映射接口。 - 无返回值的删除接口使用
204 No Content状态码更符合HTTP语义,明确告知客户端无响应内容,避免解析异常。
修改后Controller代码:
@ResponseStatus(HttpStatus.NO_CONTENT) @PostMapping("/deleteShift") public Mono<Void> deleteShift(@RequestBody String ID) { return shiftService.deleteShift(ID); }
可选规范优化
如果希望参数格式更清晰,可以将单个ID封装为请求对象,避免纯字符串传参的格式歧义:
- 定义简单请求参数对象(Java 16+可直接用record):
public record DeleteShiftRequest(String id) {}
- 调整Controller方法参数接收:
@ResponseStatus(HttpStatus.NO_CONTENT) @PostMapping("/deleteShift") public Mono<Void> deleteShift(@RequestBody DeleteShiftRequest request) { return shiftService.deleteShift(request.id()); }
- 前端对应调整请求体格式:
body: JSON.stringify({ id: UID })
另外删除操作更符合RESTful规范的写法是使用@DeleteMapping,将ID放在路径参数中,比如@DeleteMapping("/{id}"),可以根据项目规范自行调整。
内容的提问来源于stack exchange,提问作者Hassan Raza
相关产品推荐
相关产品推荐

