Spring MVC中返回字符串的@ResponseBody方法如何响应HTTP 400错误
技术背景
在使用Spring MVC构建简单的JSON API时,我们常常会使用@ResponseBody
注解来直接返回JSON字符串。然而,当遇到异常情况,如请求参数无效时,需要返回HTTP 400错误(Bad Request)。但由于方法返回类型是String
,不能直接使用ResponseEntity
来设置响应状态码,这就需要寻找合适的解决方案。
实现步骤
方法一:更改返回类型为ResponseEntity<>
这是一种简单且有效的方法,直接更改方法的返回类型,从而可以方便地设置响应状态码。
1 2 3 4 5 6 7 8
| @RequestMapping(value = "/matches/{matchId}", produces = "application/json") public ResponseEntity<String> match(@PathVariable String matchId) { String json = matchService.getMatchJson(matchId); if (json == null) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } return new ResponseEntity<>(json, HttpStatus.OK); }
|
从Spring 4.1开始,还可以使用ResponseEntity
的辅助方法:
1 2 3 4
| if (json == null) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null); } return ResponseEntity.ok(json);
|
方法二:使用HttpServletResponse
通过HttpServletResponse
对象手动设置响应状态码。
1 2 3 4 5 6 7 8 9
| @RequestMapping(value = "/matches/{matchId}", produces = "application/json") @ResponseBody public String match(@PathVariable String matchId, HttpServletRequest request, HttpServletResponse response) { String json = matchService.getMatchJson(matchId); if (json == null) { response.setStatus(HttpServletResponse.SC_BAD_REQUEST); } return json; }
|
方法三:抛出异常并使用@ExceptionHandler
自定义异常类,并使用@ExceptionHandler
注解来处理异常,设置响应状态码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @ResponseStatus(value = HttpStatus.BAD_REQUEST) public class BadThingException extends RuntimeException { }
@RequestMapping(value = "/matches/{matchId}", produces = "application/json") @ResponseBody public String match(@PathVariable String matchId) { String json = matchService.getMatchJson(matchId); if (json == null) { throw new BadThingException(); } return json; }
@ExceptionHandler(BadThingException.class) @ResponseStatus(value = HttpStatus.BAD_REQUEST) public @ResponseBody MyError handleException(BadThingException e) { return new MyError("That doesn’t work"); }
|
方法四:使用ResponseStatusException
(Spring 5+)
直接抛出ResponseStatusException
来设置响应状态码。
1 2 3 4 5 6 7 8 9
| @RequestMapping(value = "/matches/{matchId}", produces = "application/json") @ResponseBody public String match(@PathVariable String matchId) { String json = matchService.getMatchJson(matchId); if (json == null) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST); } return json; }
|
核心代码
以下是使用ResponseEntity
的完整示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController public class MatchController {
private MatchService matchService;
public MatchController(MatchService matchService) { this.matchService = matchService; }
@RequestMapping(value = "/matches/{matchId}", produces = "application/json") public ResponseEntity<String> match(@PathVariable String matchId) { String json = matchService.getMatchJson(matchId); if (json == null) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } return new ResponseEntity<>(json, HttpStatus.OK); } }
|
最佳实践
- 使用
ResponseEntity
:如果项目允许更改方法返回类型,使用ResponseEntity
是最简单和直接的方式,代码简洁且易于维护。 - 异常处理:对于复杂的业务逻辑,将错误处理逻辑封装在异常处理类中,使用
@ExceptionHandler
注解可以使代码更加清晰,分离正常业务逻辑和错误处理逻辑。 - 使用
ResponseStatusException
:在Spring 5及以上版本中,使用ResponseStatusException
可以避免自定义异常类,减少代码量。
常见问题
1. 使用@ResponseStatus
注解返回HTML响应
@ResponseStatus
注解可能会导致服务器返回HTML响应,这对于RESTful服务不太合适。可以使用ResponseStatusException
来替代,它不会有这个问题。
这可能是因为没有正确配置可接受的媒体类型。确保在控制器方法或配置中设置了正确的produces
属性。
3. ResponseStatusException
不可用
ResponseStatusException
仅在Spring 5及以上版本中可用。如果使用的是较旧的Spring版本,需要采用其他方法。