我使用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使用它。
Answer 1:
改变你的返回类型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);
Answer 2:
像这样的东西应该工作,我不知道是否有更简单的方法:
@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;
}
Answer 3:
不一定是这样做的最简洁的方式,但挺干净的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");
}
编辑您可以使用,如果春季3.1+在异常处理程序使用方法@ResponseBody,否则使用ModelAndView
什么的。
https://jira.springsource.org/browse/SPR-6902
Answer 4:
我会稍微改变实现:
首先,我创建了一个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序列化(这是默认添加,如果添加杰克逊到类路径,并添加任何@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);
}
注意,这是很常见的域对象从视图的对象或对象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);
}
Answer 5:
这里有一个不同的方法。 创建一个自定义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 。
Answer 6:
正如一些答案提到的,是创建要返回每个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/
Answer 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);
}
Answer 8:
随着春天的引导,我不完全知道为什么,这是必要的(我得到了/error
,即使后备@ResponseBody
是上定义@ExceptionHandler
),但在本身下面没有工作:
@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."}
Answer 9:
我觉得这个线程实际拥有的最简单,干净的解决方案,在不牺牲JSON martialing工具,Spring提供:
https://stackoverflow.com/a/16986372/1278921
文章来源: How to respond with HTTP 400 error in a Spring MVC @ResponseBody method returning String?