문자열을 반환하는 Spring MVC @ResponseBody 메서드에서 HTTP 400 오류가 발생하여 응답하는 방법
MVC를 Spring MVC는 Spring MVC로 되어 있습니다.@ResponseBody
직접 생산하는 레이어가 이미 .(JSON은 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);
Spring 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;
}
이것은 반드시 가장 간단한 방법은 아니지만, 제 생각에는 꽤 깨끗합니다.
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");
}
3. 를 는 예외 으로 @를 할 수 않으면 Spring 3.1+ 를 사용합니다.ModelAndView
아, 아, 아, 아, 아, 아, 아, 아, 아.
@ResponseBody는 @ExceptionHandler [SPR-6902]#11567에서 동작하지 않습니다.
구현을 약간 변경합니다.
'I'를 .UnknownMatchException
:
@ResponseStatus(HttpStatus.NOT_FOUND)
public class UnknownMatchException extends RuntimeException {
public UnknownMatchException(String matchId) {
super("Unknown match: " + matchId);
}
}
@ResponseStatus의 사용에 주의해 주십시오.이것은 스프링의ResponseStatusExceptionResolver
, 코드를 (로 도 실례했습니다404 - Not Found
이 사용 사례에 더 적합하다고 생각합니다만, 계속 사용하세요.HttpStatus.BAD_REQUEST
괜찮으시다면).
다음, 음음음 the the는 the the the the the the the next next next next?MatchService
하다
interface MatchService {
public Match findMatch(String matchId);
}
하고 Spring Spring's 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 오브젝트에서 분리하는 것은 매우 일반적입니다.이것은, 시리얼 가능한 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;
}
입니다.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;
}
일부 답변에서 설명한 바와 같이 반환하는 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/
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);
}
Spring Boot에서는 이것이 왜 필요한지는 잘 모르겠습니다(Spring Boot에서는/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;
}
Jackson Mapper는 그것을 "컨버터블"로 취급하지 않았기 때문에 나는 getters/setters를 추가해야 했고 나는 또한 추가했다.@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."}
또 다른 접근법은@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;
}
}
이것으로, 임의의 컨트롤러로부터 예외를 슬로우 할 수 있게 되어 어드바이스 클래스내에 다른 핸들러를 정의할 수 있게 되었습니다.
명시적으로 반환하지 않고 컨트롤러에서 예외를 처리하는 가장 심플하고 깨끗한 방법ResponseEntity
그냥 더하는 것이다.@ExceptionHandler
방법들.
Spring Boot 2.0.3을 사용한 스니펫의 예릴리즈:
// Prefer static import of HttpStatus constants as it's cleaner IMHO
// Handle with no content returned
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(BAD_REQUEST)
void onIllegalArgumentException() {}
// Return 404 when JdbcTemplate does not return a single row
@ExceptionHandler(IncorrectResultSizeDataAccessException.class)
@ResponseStatus(NOT_FOUND)
void onIncorrectResultSizeDataAccessException() {}
// Catch all handler with the exception as content
@ExceptionHandler(Exception.class)
@ResponseStatus(I_AM_A_TEAPOT)
@ResponseBody Exception onException(Exception e) {
return e;
}
여백으로서:
모든 컨텍스트/사용방법에서
matchService.getMatchJson(matchId) == null
무효입니다. 그러면 제 제안은getMatchJson
예를 들어, 예를 들어,IllegalArgumentException
돌아오는 대신null
컨트롤러에 거품이 일게 합니다.@ExceptionHandler
.한다면
null
다른 조건을 테스트하기 위해 사용됩니다.그러면 다음과 같은 구체적인 방법을 사용할 수 있습니다.matchService.hasMatchJson(matchId)
저는null
한 한 예기치 않은 「예상치 않은」을NullPointerException
.
또한 Spring의 기본 오류 처리 기능을 활용할 수도 있습니다.
그러나 이러한 기본 오류와 마찬가지로 응답 본문은 설정되지 않습니다.
이러한 요구는 보다 심층적인 커스텀 검증과 그 기준에 따라 요구가 거부되었다는 사실을 모호하게 하기 때문에 수작업으로 이루어졌을 뿐이며 잠재적으로 악의적인 의도를 나타낼 수 있는 요구를 거부할 때 유용합니다.
상태 코드와 함께 사용자 지정 응답을 사용합니다.
다음과 같이 합니다.
class Response<T>(
val timestamp: String = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS")
.withZone(ZoneOffset.UTC)
.format(Instant.now()),
val code: Int = ResultCode.SUCCESS.code,
val message: String? = ResultCode.SUCCESS.message,
val status: HttpStatus = HttpStatus.OK,
val error: String? = "",
val token: String? = null,
val data: T? = null
) : : ResponseEntity<Response.CustomResponseBody>(status) {
data class CustomResponseBody(
val timestamp: String = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS")
.withZone(ZoneOffset.UTC)
.format(Instant.now()),
val code: Int = ResultCode.SUCCESS.code,
val message: String? = ResultCode.SUCCESS.message,
val error: String? = "",
val token: String? = null,
val data: Any? = null
)
override fun getBody(): CustomResponseBody? = CustomResponseBody(timestamp, code, message, error, token, data)
언급URL : https://stackoverflow.com/questions/16232833/how-to-respond-with-an-http-400-error-in-a-spring-mvc-responsebody-method-retur
'programing' 카테고리의 다른 글
On Checked Changed 이벤트가 실행되지 않음 (0) | 2023.03.27 |
---|---|
리액트 후크 useEffect는 업데이트 시에만 적용됩니까? (0) | 2023.03.27 |
CF7 폼 값을 동적으로 변경하다 (0) | 2023.03.27 |
React Js에서 CSS/Jquery calc 함수 사용 (0) | 2023.03.27 |
Sonar 탐지 보고서에서 롬복 클래스 제외 (0) | 2023.03.27 |