我使用Spring MVC为一个简单的JSON API,使用@ResponseBody的方法,如下所示。(我已经有了一个直接生成JSON的服务层。)

@RequestMapping(value = "/matches/{matchId}", produces = "application/json")
@ResponseBody
public String match(@PathVariable String matchId) {
    String json = matchService.getMatchJson(matchId);
    if (json == null) {
        // TODO: how to respond with e.g. 400 "bad request"?
    }
    return json;
}

在给定的场景中,响应HTTP 400错误的最简单、最干净的方法是什么?

我确实遇到过这样的方法:

return new ResponseEntity(HttpStatus.BAD_REQUEST);

...但我不能在这里使用它,因为我的方法的返回类型是字符串,而不是ResponseEntity。


当前回答

我在Spring Boot应用程序中使用这个:

@RequestMapping(value = "/matches/{matchId}", produces = "application/json")
@ResponseBody
public ResponseEntity<?> match(@PathVariable String matchId, @RequestBody String body,
            HttpServletRequest request, HttpServletResponse response) {

    Product p;
    try {
      p = service.getProduct(request.getProductId());
    } catch(Exception ex) {
       return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
    }

    return new ResponseEntity(p, HttpStatus.OK);
}

其他回答

这不是最紧凑的方式,但在我看来很干净:

if(json == null) {
    throw new BadThingException();
}
...

@ExceptionHandler(BadThingException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public @ResponseBody MyError handleException(BadThingException e) {
    return new MyError("That doesn’t work");
}

如果使用Spring 3.1+,您可以在异常处理方法中使用@ResponseBody,否则使用ModelAndView或其他东西。

@ResponseBody不能与@ExceptionHandler [sp -6902] #11567一起工作

我将稍微改变一下实现:

首先,我创建了一个UnknownMatchException:

@ResponseStatus(HttpStatus.NOT_FOUND)
public class UnknownMatchException extends RuntimeException {
    public UnknownMatchException(String matchId) {
        super("Unknown match: " + matchId);
    }
}

注意@ResponseStatus的使用,它将被Spring的ResponseStatusExceptionResolver识别。如果抛出异常,它将创建具有相应响应状态的响应。(我还擅自将状态代码更改为404 - Not Found,我认为这更适合这个用例,但您可以坚持使用HttpStatus。BAD_REQUEST如果你喜欢。)


接下来,我将更改MatchService具有以下签名:

interface MatchService {
    public Match findMatch(String matchId);
}

最后,我将更新控制器并委托给Spring的MappingJackson2HttpMessageConverter来自动处理JSON序列化(如果您将Jackson添加到类路径中,并将@EnableWebMvc或<mvc:annotation-driven />添加到配置中,则默认添加JSON序列化。请参阅参考文档):

@RequestMapping(value = "/matches/{matchId}", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Match match(@PathVariable String matchId) {
    // Throws an UnknownMatchException if the matchId is not known
    return matchService.findMatch(matchId);
}

注意,将域对象与视图对象或DTO对象分离是非常常见的。这可以通过添加一个小型DTO工厂来轻松实现,该工厂返回可序列化的JSON对象:

@RequestMapping(value = "/matches/{matchId}", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public MatchDTO match(@PathVariable String matchId) {
    Match match = matchService.findMatch(matchId);
    return MatchDtoFactory.createDTO(match);
}

这里有一个不同的方法。创建一个带有@ResponseStatus注解的自定义Exception,如下所示。

@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Not Found")
public class NotFoundException extends Exception {

    public NotFoundException() {
    }
}

需要的时候扔出去。

@RequestMapping(value = "/matches/{matchId}", produces = "application/json")
@ResponseBody
public String match(@PathVariable String matchId) {
    String json = matchService.getMatchJson(matchId);
    if (json == null) {
        throw new NotFoundException();
    }
    return json;
}

最简单的方法是抛出一个ResponseStatusException:

@RequestMapping(value = "/matches/{matchId}", produces = "application/json")
@ResponseBody
public String match(@PathVariable String matchId, @RequestBody String body) {
    String json = matchService.getMatchJson(matchId);
    if (json == null) {
        throw new ResponseStatusException(HttpStatus.NOT_FOUND);
    }
    return json;
}

另一种方法是使用@ExceptionHandler和@ControllerAdvice将所有处理程序集中在同一个类中。如果不是,则必须将处理程序方法放入希望为其管理异常的每个控制器中。

你的处理程序类:

@ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {

  @ExceptionHandler(MyBadRequestException.class)
  public ResponseEntity<MyError> handleException(MyBadRequestException e) {
    return ResponseEntity
        .badRequest()
        .body(new MyError(HttpStatus.BAD_REQUEST, e.getDescription()));
  }
}

您的自定义异常:

public class MyBadRequestException extends RuntimeException {

  private String description;

  public MyBadRequestException(String description) {
    this.description = description;
  }

  public String getDescription() {
    return this.description;
  }
}

现在,您可以从任何控制器抛出异常,并且可以在通知类中定义其他处理程序。