如何在返回String的Spring MVC @ResponseBody方法中响应HTTP 400错误?

Jon*_*nik 366 java spring spring-mvc http-error

我使用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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

return new ResponseEntity(HttpStatus.BAD_REQUEST);
Run Code Online (Sandbox Code Playgroud)

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

Bas*_*hdy 588

将您的退货类型更改为ResponseEntity<>,然后您可以使用以下400

return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
Run Code Online (Sandbox Code Playgroud)

并提出正确的要求

return new ResponseEntity<>(json,HttpStatus.OK);
Run Code Online (Sandbox Code Playgroud)

更新1

在4.1之后,ResponseEntity中有辅助方法可以用作

return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
Run Code Online (Sandbox Code Playgroud)

return ResponseEntity.ok(json);
Run Code Online (Sandbox Code Playgroud)

  • 它将是'ResponseEntity <YourClass>' (10认同)
  • 如果您传递的不是字符串,该怎么办?如在POJO或其他对象中? (6认同)
  • 使用此方法,您不再需要@ResponseBody注释 (5认同)

sta*_*ker 99

这样的事情应该有效,我不确定是否有更简单的方法:

@RequestMapping(value = "/matches/{matchId}", produces = "application/json")
@ResponseBody
public String match(@PathVariable String matchId, @RequestBody String body,
            HttpServletRequest request, HttpServletResponse response) {
    String json = matchService.getMatchJson(matchId);
    if (json == null) {
        response.setStatus( HttpServletResponse.SC_BAD_REQUEST  );
    }
    return json;
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢!这很有效,也很简单.(在这种情况下,可以通过删除未使用的`body`和`request`参数来进一步简化.) (3认同)

Zut*_*tty 53

不一定是最紧凑的方式,但相当干净的IMO

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

@ExceptionHandler(BadThingException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public @ResponseBody MyError handleException(BadThingException e) {
    return new MyError("That doesnt work");
}
Run Code Online (Sandbox Code Playgroud)

如果使用Spring 3.1+,编辑你可以在异常处理程序方法中使用@ResponseBody,否则使用a ModelAndView或者其他东西.

https://jira.springsource.org/browse/SPR-6902

  • 抱歉,这似乎不起作用。它会在日志中生成带有长堆栈跟踪的 HTTP 500“服务器错误”:`ERROR org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver - 无法调用 @ExceptionHandler 方法:publiccontroller.TestController$MyErrorcontroller.TestController。 handleException(controller.TestController$BadThingException) org.springframework.web.HttpMediaTypeNotAcceptableException: 找不到可接受的表示` 答案中是否缺少某些内容? (2认同)

mat*_*sev 47

我会稍微改变一下实现:

首先,我创建一个UnknownMatchException:

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

注意使用@ResponseStatus,它将被Spring认可ResponseStatusExceptionResolver.如果抛出异常,它将创建具有相应响应状态的响应.(我也冒昧地改变了404 - Not Found我觉得更适合这个用例的状态代码,但HttpStatus.BAD_REQUEST如果你愿意,你可以坚持下去.)


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

interface MatchService {
    public Match findMatch(String matchId);
}
Run Code Online (Sandbox Code Playgroud)

最后,我会更新控制器和委托给Spring的MappingJackson2HttpMessageConverter自动处理JSON序列化(这是默认添加如果添加杰克逊到类路径,并添加或者@EnableWebMvc<mvc:annotation-driven />到你的配置,请参阅参考文档):

@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);
}
Run Code Online (Sandbox Code Playgroud)

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

@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);
}
Run Code Online (Sandbox Code Playgroud)


dan*_*emi 31

这是一种不同的方法.创建一个Exception带有注释的自定义@ResponseStatus,如下所示.

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

    public NotFoundException() {
    }
}
Run Code Online (Sandbox Code Playgroud)

并在需要时抛出它.

@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;
}
Run Code Online (Sandbox Code Playgroud)

查看Spring文档:http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-ann-annotated-exceptions.


Mev*_*mir 20

最简单的方法是扔一个 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;
    }
Run Code Online (Sandbox Code Playgroud)

  • 最佳答案:不需要更改返回类型,也不需要创建自己的异常。此外,如果需要,ResponseStatusException 允许添加原因消息。 (6认同)
  • 需要注意的是,ResponseStatusException 仅在 Spring 版本 5+ 中可用 (3认同)

Nor*_*ris 18

如某些答案中所述,可以为要返回的每个HTTP状态创建一个异常类.我不喜欢为每个项目创建每个状态的类的想法.这是我想出来的.

  • 创建一个接受HTTP状态的通用异常
  • 创建一个Controller Advice异常处理程序

我们来看看代码

package com.javaninja.cam.exception;

import org.springframework.http.HttpStatus;


/**
 * The exception used to return a status and a message to the calling system.
 * @author norrisshelton
 */
@SuppressWarnings("ClassWithoutNoArgConstructor")
public class ResourceException extends RuntimeException {

    private HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;

    /**
     * Gets the HTTP status code to be returned to the calling system.
     * @return http status code.  Defaults to HttpStatus.INTERNAL_SERVER_ERROR (500).
     * @see HttpStatus
     */
    public HttpStatus getHttpStatus() {
        return httpStatus;
    }

    /**
     * Constructs a new runtime exception with the specified HttpStatus code and detail message.
     * The cause is not initialized, and may subsequently be initialized by a call to {@link #initCause}.
     * @param httpStatus the http status.  The detail message is saved for later retrieval by the {@link
     *                   #getHttpStatus()} method.
     * @param message    the detail message. The detail message is saved for later retrieval by the {@link
     *                   #getMessage()} method.
     * @see HttpStatus
     */
    public ResourceException(HttpStatus httpStatus, String message) {
        super(message);
        this.httpStatus = httpStatus;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我创建一个控制器建议类

package com.javaninja.cam.spring;


import com.javaninja.cam.exception.ResourceException;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;


/**
 * Exception handler advice class for all SpringMVC controllers.
 * @author norrisshelton
 * @see org.springframework.web.bind.annotation.ControllerAdvice
 */
@org.springframework.web.bind.annotation.ControllerAdvice
public class ControllerAdvice {

    /**
     * Handles ResourceExceptions for the SpringMVC controllers.
     * @param e SpringMVC controller exception.
     * @return http response entity
     * @see ExceptionHandler
     */
    @ExceptionHandler(ResourceException.class)
    public ResponseEntity handleException(ResourceException e) {
        return ResponseEntity.status(e.getHttpStatus()).body(e.getMessage());
    }
}
Run Code Online (Sandbox Code Playgroud)

使用它

throw new ResourceException(HttpStatus.BAD_REQUEST, "My message");
Run Code Online (Sandbox Code Playgroud)

http://javaninja.net/2016/06/throwing-exceptions-messages-spring-mvc-controller/

  • 非常好的方法..我更喜欢返回带有 errorCode 和消息字段的 jSON,而不是简单的字符串.. (2认同)
  • 这应该是正确的答案,具有自定义状态代码和消息的通用和全局异常处理程序:D (2认同)

小智 10

我在我的春季启动应用程序中使用它

@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);
}
Run Code Online (Sandbox Code Playgroud)