2013-04-26 109 views
248

我正在使用Spring MVC做简单的JSON API,基于@ResponseBody的方法如下。 (我已经有直接生产JSON服务层)。如何在Spring MVC @ResponseBody方法返回String时响应HTTP 400错误?

@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我不能在这里使用它。

回答

433

改变你的返回类型ResponseEntity<>,那么你可以在下面使用400

return new ResponseEntity<>(HttpStatus.BAD_REQUEST); 

和正确的请求

return new ResponseEntity<>(json,HttpStatus.OK); 

更新1

春天4.1后有在ResponseEntity辅助方法可以作为

return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null); 

return ResponseEntity.ok(json); 
+0

啊,所以你也可以使用'ResponseEntity'。这很好用,对原始代码只是一个简单的改变 - 谢谢! – Jonik 2013-04-27 16:22:52

+0

欢迎您随时添加自定义标题,同时检查ResponseEntity – 2013-04-27 22:27:32

+0

的所有构造函数,除了HttpStatus.OK之外,您还可以选择提供一个响应代码。有用。 – 2014-11-14 04:06:19

44

不一定这样做的最简洁的方式,但很干净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"); 
} 

编辑,你可以,如果使用Spring 3.1+的异常处理程序使用方法@ResponseBody,否则使用ModelAndView什么的。

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

+1

对不起,这似乎不起作用。它会在日志中产生具有长堆栈跟踪的HTTP 500“服务器错误”: 错误org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver - 无法调用@ExceptionHandler方法:public controller.TestController $ MyError controller.TestController .handleException(controller.TestController $ BadThingException) org.springframework.web.HttpMediaTypeNotAcceptableException:无法找到可接受的表示法<答案是否有缺失? – Jonik 2013-04-27 08:52:13

+0

另外,我并没有完全理解定义另一个自定义类型(M​​yError)的意义。这是必要的吗?我正在使用最新的Spring(3.2.2)。 – Jonik 2013-04-27 08:54:54

+1

它适合我。相反,我使用'javax.validation.ValidationException'。 (Spring 3.1.4) – 2013-10-01 01:48:13

76

像这样的事情应该工作,我不知道是否有更简单的方法:

@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; 
} 
+3

谢谢!这工作,也很简单。 (在这种情况下,可以通过删除未使用的'body'和'request'参数进一步简化。) – Jonik 2013-04-27 09:18:02

36

我会稍微改变执行:

首先,我创建了一个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 />添加到您的配置中,则默认添加它,请参阅reference docs):

@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分离。

@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); 
} 
+0

我有500 我日志: AY 28日,2015年下午5点23分31秒org.apache.cxf。拦截器。摘要损坏链接观察者onMessage 严重:错误处理期间发生错误,放弃! org.apache.cxf.interceptor.Fault – razor 2015-05-28 16:25:37

-2

我觉得这个主题却有着最简单,干净的解决方案,在不牺牲JSON martialing工具,Spring提供了:这可以很容易地通过增加一个小DTO工厂,返回的序列化JSON对象来实现

https://stackoverflow.com/a/16986372/1278921

7

我M在我的春天启动的应用程序

@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); 
} 
20

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

@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; 
} 

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

+0

该方法允许您在堆栈跟踪的任何位置终止执行,而无需返回应指定您希望返回的HTTP状态代码的“特殊值”。 – 2017-11-28 05:34:23

9

正如在某些答案中提到的那样,您可以为每个要返回的HTTP状态创建一个异常类。我不喜欢为每个项目创建每个状态类的想法。这是我想出来的。

  • 创建接受一个HTTP状态
  • 创建一个控制器忠告异常处理程序一般异常

让我们的代码

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; 
    } 
} 

然后我创建一个控制器咨询类

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()); 
    } 
} 

要使用它

throw new ResourceException(HttpStatus.BAD_REQUEST, "My message"); 

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

+0

非常好的方法..而不是一个简单的字符串我宁愿返回一个jSON与errorCode和消息字段.. – 2017-12-13 14:29:49

0

随着春天开机,我不完全知道为什么,这是必要的(我即使@ResponseBody是上@ExceptionHandler定义的/error回退),但以下本身没有工作:

@ResponseBody 
@ResponseStatus(HttpStatus.BAD_REQUEST) 
@ExceptionHandler(IllegalArgumentException.class) 
public ErrorMessage handleIllegalArguments(HttpServletRequest httpServletRequest, IllegalArgumentException e) { 
    log.error("Illegal arguments received.", e); 
    ErrorMessage errorMessage = new ErrorMessage(); 
    errorMessage.code = 400; 
    errorMessage.message = e.getMessage(); 
    return errorMessage; 
} 

它仍然抛出一个异常,显然是因为没有可生产的媒体类型被定义为一个请求属性:

// AbstractMessageConverterMethodProcessor 
@SuppressWarnings("unchecked") 
protected <T> void writeWithMessageConverters(T value, MethodParameter returnType, 
     ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) 
     throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { 

    Class<?> valueType = getReturnValueType(value, returnType); 
    Type declaredType = getGenericType(returnType); 
    HttpServletRequest request = inputMessage.getServletRequest(); 
    List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request); 
    List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType); 
if (value != null && producibleMediaTypes.isEmpty()) { 
     throw new IllegalArgumentException("No converter found for return value of type: " + valueType); // <-- throws 
    } 

// .... 

@SuppressWarnings("unchecked") 
protected List<MediaType> getProducibleMediaTypes(HttpServletRequest request, Class<?> valueClass, Type declaredType) { 
    Set<MediaType> mediaTypes = (Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); 
    if (!CollectionUtils.isEmpty(mediaTypes)) { 
     return new ArrayList<MediaType>(mediaTypes); 

所以我加了他们。

@ResponseBody 
@ResponseStatus(HttpStatus.BAD_REQUEST) 
@ExceptionHandler(IllegalArgumentException.class) 
public ErrorMessage handleIllegalArguments(HttpServletRequest httpServletRequest, IllegalArgumentException e) { 
    Set<MediaType> mediaTypes = new HashSet<>(); 
    mediaTypes.add(MediaType.APPLICATION_JSON_UTF8); 
    httpServletRequest.setAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes); 
    log.error("Illegal arguments received.", e); 
    ErrorMessage errorMessage = new ErrorMessage(); 
    errorMessage.code = 400; 
    errorMessage.message = e.getMessage(); 
    return errorMessage; 
} 

这让我通过具有“兼容支持的媒体类型”,但随后仍然没有工作,因为我ErrorMessage是错误的:

public class ErrorMessage { 
    int code; 

    String message; 
} 

JacksonMapper没有把它处理“可转换”,所以我不得不添加getter/setter方法,我也加入@JsonProperty注释

public class ErrorMessage { 
    @JsonProperty("code") 
    private int code; 

    @JsonProperty("message") 
    private String message; 

    public int getCode() { 
     return code; 
    } 

    public void setCode(int code) { 
     this.code = code; 
    } 

    public String getMessage() { 
     return message; 
    } 

    public void setMessage(String message) { 
     this.message = message; 
    } 
} 

然后我收到我的消息预期

{"code":400,"message":"An \"url\" parameter must be defined."} 
相关问题