나는 함께 간단한 JSON API를위한 스프링 MVC를 사용하고 @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가 아닌 String이기 때문에 여기에서 사용할 수 없습니다.
답변
반환 유형을로 변경 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);
답변
이와 같은 것이 작동해야하지만 더 간단한 방법이 있는지 확실하지 않습니다.
@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;
}
답변
이 작업을 수행하는 가장 간단한 방법은 아니지만 상당히 깨끗한 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
답변
구현을 약간 변경합니다.
먼저 UnknownMatchException
:
@ResponseStatus(HttpStatus.NOT_FOUND)
public class UnknownMatchException extends RuntimeException {
public UnknownMatchException(String matchId) {
super("Unknown match: " + matchId);
}
}
@ResponseStatus 의 사용에 주목하십시오 ResponseStatusExceptionResolver
. Spring의에 의해 인식됩니다 . 예외가 발생하면 해당 응답 상태로 응답이 작성됩니다. (또한 404 - Not Found
이 사용 사례에 더 적합한 상태 코드를 자유롭게 변경할 수는 있지만 HttpStatus.BAD_REQUEST
원하는 경우 고수 할 수 있습니다 .)
다음으로 다음 MatchService
과 같은 서명을 갖도록 변경합니다 .
interface MatchService {
public Match findMatch(String matchId);
}
마지막으로 컨트롤러를 업데이트하고 Spring에 위임 MappingJackson2HttpMessageConverter
하여 JSON 직렬화를 자동으로 처리합니다 (클래스 패스에 Jackson을 추가하고 구성에 @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 객체와 분리하는 것이 매우 일반적입니다. 직렬화 가능한 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);
}
답변
다른 접근법이 있습니다. 다음과 같이로 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 .
답변
일부 답변에서 언급했듯이 반환하려는 각 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;
}
}
그런 다음 컨트롤러 조언 클래스를 만듭니다.
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/
답변
스프링 부트 응용 프로그램에서 이것을 사용하고 있습니다.
@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);
}