-
Notifications
You must be signed in to change notification settings - Fork 0
feat: apiPayload 세팅 #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
bf77f8c
3768f6f
8e2c9ae
ff2f31e
736b5ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package com.example.phrasebe.apiPayload; | ||
|
|
||
| import com.example.phrasebe.apiPayload.code.BaseCode; | ||
| import com.example.phrasebe.apiPayload.code.status.SuccessStatus; | ||
| import com.fasterxml.jackson.annotation.JsonInclude; | ||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||
| import com.fasterxml.jackson.annotation.JsonPropertyOrder; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Getter; | ||
|
|
||
| @Getter | ||
| @AllArgsConstructor | ||
| @JsonPropertyOrder({"isSuccess", "code", "message", "result"}) | ||
| public class ApiResponse<T> { | ||
|
|
||
| @JsonProperty("isSuccess") | ||
| private final Boolean isSuccess; | ||
| private final String code; | ||
| private final String message; | ||
| @JsonInclude(JsonInclude.Include.NON_NULL) | ||
| private T result; | ||
|
||
|
|
||
|
|
||
| // 성공한 경우 응답 생성 | ||
|
|
||
| public static <T> ApiResponse<T> onSuccess(T result){ | ||
| return new ApiResponse<>(true, SuccessStatus._OK.getCode() , SuccessStatus._OK.getMessage(), result); | ||
| } | ||
|
||
|
|
||
| public static <T> ApiResponse<T> of(BaseCode code, T result){ | ||
| return new ApiResponse<>(true, code.getReasonHttpStatus().getCode() , code.getReasonHttpStatus().getMessage(), result); | ||
| } | ||
|
||
|
|
||
|
|
||
| // 실패한 경우 응답 생성 | ||
| public static <T> ApiResponse<T> onFailure(String code, String message, T data){ | ||
| return new ApiResponse<>(false, code, message, data); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package com.example.phrasebe.apiPayload.code; | ||
|
|
||
| public interface BaseCode { | ||
|
|
||
| ReasonDTO getReason(); | ||
|
||
|
|
||
| ReasonDTO getReasonHttpStatus(); | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package com.example.phrasebe.apiPayload.code; | ||
|
|
||
| public interface BaseErrorCode { | ||
|
|
||
| ErrorReasonDTO getReason(); | ||
|
|
||
| ErrorReasonDTO getReasonHttpStatus(); | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package com.example.phrasebe.apiPayload.code; | ||
|
|
||
| import lombok.Builder; | ||
| import lombok.Getter; | ||
| import org.springframework.http.HttpStatus; | ||
|
|
||
| @Getter | ||
| @Builder | ||
| public class ErrorReasonDTO { | ||
|
|
||
| private HttpStatus httpStatus; | ||
|
|
||
| private final boolean isSuccess; | ||
| private final String code; | ||
| private final String message; | ||
|
|
||
| public boolean getIsSuccess(){return isSuccess;} | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package com.example.phrasebe.apiPayload.code; | ||
|
|
||
| import lombok.Builder; | ||
| import lombok.Getter; | ||
| import org.springframework.http.HttpStatus; | ||
|
|
||
| @Getter | ||
| @Builder | ||
| public class ReasonDTO { | ||
|
|
||
| private HttpStatus httpStatus; | ||
|
|
||
| private final boolean isSuccess; | ||
| private final String code; | ||
| private final String message; | ||
|
|
||
| public boolean getIsSuccess(){return isSuccess;} | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| package com.example.phrasebe.apiPayload.code.status; | ||
|
|
||
| import com.example.phrasebe.apiPayload.code.BaseErrorCode; | ||
| import com.example.phrasebe.apiPayload.code.ErrorReasonDTO; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Getter; | ||
| import org.springframework.http.HttpStatus; | ||
|
|
||
| @Getter | ||
| @AllArgsConstructor | ||
| public enum ErrorStatus implements BaseErrorCode { | ||
|
|
||
| // 가장 일반적인 응답 | ||
| _INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON500", "서버 에러, 관리자에게 문의 바랍니다."), | ||
| _BAD_REQUEST(HttpStatus.BAD_REQUEST,"COMMON400","잘못된 요청입니다."), | ||
| _UNAUTHORIZED(HttpStatus.UNAUTHORIZED,"COMMON401","인증이 필요합니다."), | ||
| _FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON403", "금지된 요청입니다."), | ||
|
|
||
|
|
||
| // 멤버 관려 에러 | ||
| MEMBER_NOT_FOUND(HttpStatus.BAD_REQUEST, "MEMBER4001", "사용자가 없습니다."), | ||
| NICKNAME_NOT_EXIST(HttpStatus.BAD_REQUEST, "MEMBER4002", "닉네임은 필수 입니다."); | ||
|
|
||
| private final HttpStatus httpStatus; | ||
| private final String code; | ||
| private final String message; | ||
|
|
||
| @Override | ||
| public ErrorReasonDTO getReason() { | ||
| return ErrorReasonDTO.builder() | ||
| .message(message) | ||
| .code(code) | ||
| .isSuccess(false) | ||
| .build(); | ||
| } | ||
|
|
||
| @Override | ||
| public ErrorReasonDTO getReasonHttpStatus() { | ||
| return ErrorReasonDTO.builder() | ||
| .message(message) | ||
| .code(code) | ||
| .isSuccess(false) | ||
| .httpStatus(httpStatus) | ||
| .build() | ||
| ; | ||
| } | ||
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package com.example.phrasebe.apiPayload.code.status; | ||
|
|
||
| import com.example.phrasebe.apiPayload.code.BaseCode; | ||
| import com.example.phrasebe.apiPayload.code.ReasonDTO; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Getter; | ||
| import org.springframework.http.HttpStatus; | ||
|
|
||
| @Getter | ||
| @AllArgsConstructor | ||
| public enum SuccessStatus implements BaseCode { | ||
|
|
||
| // 일반적인 응답 | ||
| _OK(HttpStatus.OK, "COMMON200", "성공입니다."); | ||
|
|
||
| private final HttpStatus httpStatus; | ||
| private final String code; | ||
| private final String message; | ||
|
|
||
|
|
||
| @Override | ||
| public ReasonDTO getReason() { | ||
| return ReasonDTO.builder() | ||
| .message(message) | ||
| .code(code) | ||
| .isSuccess(true) | ||
| .build(); | ||
| } | ||
|
|
||
| @Override | ||
| public ReasonDTO getReasonHttpStatus() { | ||
| return ReasonDTO.builder() | ||
| .message(message) | ||
| .code(code) | ||
| .isSuccess(true) | ||
| .httpStatus(httpStatus) | ||
| .build() | ||
| ; | ||
|
|
||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| package com.example.phrasebe.apiPayload.exception; | ||
|
|
||
| import com.example.phrasebe.apiPayload.ApiResponse; | ||
| import com.example.phrasebe.apiPayload.code.ErrorReasonDTO; | ||
| import com.example.phrasebe.apiPayload.code.status.ErrorStatus; | ||
| import jakarta.servlet.http.HttpServletRequest; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import jakarta.validation.ConstraintViolationException; | ||
| import org.springframework.http.HttpHeaders; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.http.HttpStatusCode; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.MethodArgumentNotValidException; | ||
| import org.springframework.web.bind.annotation.ExceptionHandler; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
| import org.springframework.web.bind.annotation.RestControllerAdvice; | ||
| import org.springframework.web.context.request.ServletWebRequest; | ||
| import org.springframework.web.context.request.WebRequest; | ||
| import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; | ||
|
|
||
| import java.util.LinkedHashMap; | ||
| import java.util.Map; | ||
| import java.util.Optional; | ||
|
|
||
| @Slf4j | ||
| @RestControllerAdvice(annotations = {RestController.class}) | ||
| public class ExceptionAdvice extends ResponseEntityExceptionHandler { | ||
|
|
||
|
|
||
| @ExceptionHandler | ||
| public ResponseEntity<Object> validation(ConstraintViolationException e, WebRequest request) { | ||
| String errorMessage = e.getConstraintViolations().stream() | ||
| .map(constraintViolation -> constraintViolation.getMessage()) | ||
| .findFirst() | ||
| .orElseThrow(() -> new RuntimeException("ConstraintViolationException 추출 도중 에러 발생")); | ||
|
|
||
| return handleExceptionInternalConstraint(e, ErrorStatus.valueOf(errorMessage), HttpHeaders.EMPTY,request); | ||
| } | ||
|
|
||
| @Override | ||
| public ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException e, HttpHeaders headers, HttpStatusCode status, WebRequest request) { | ||
|
|
||
| Map<String, String> errors = new LinkedHashMap<>(); | ||
|
|
||
| e.getBindingResult().getFieldErrors().stream() | ||
| .forEach(fieldError -> { | ||
| String fieldName = fieldError.getField(); | ||
| String errorMessage = Optional.ofNullable(fieldError.getDefaultMessage()).orElse(""); | ||
| errors.merge(fieldName, errorMessage, (existingErrorMessage, newErrorMessage) -> existingErrorMessage + ", " + newErrorMessage); | ||
| }); | ||
|
|
||
| return handleExceptionInternalArgs(e,HttpHeaders.EMPTY, ErrorStatus.valueOf("_BAD_REQUEST"),request,errors); | ||
| } | ||
|
|
||
| @ExceptionHandler | ||
| public ResponseEntity<Object> exception(Exception e, WebRequest request) { | ||
| e.printStackTrace(); | ||
|
|
||
| return handleExceptionInternalFalse(e, ErrorStatus._INTERNAL_SERVER_ERROR, HttpHeaders.EMPTY, ErrorStatus._INTERNAL_SERVER_ERROR.getHttpStatus(),request, e.getMessage()); | ||
| } | ||
|
|
||
| @ExceptionHandler(value = GeneralException.class) | ||
| public ResponseEntity onThrowException(GeneralException generalException, HttpServletRequest request) { | ||
| ErrorReasonDTO errorReasonHttpStatus = generalException.getErrorReasonHttpStatus(); | ||
| return handleExceptionInternal(generalException,errorReasonHttpStatus,null,request); | ||
| } | ||
|
|
||
| private ResponseEntity<Object> handleExceptionInternal(Exception e, ErrorReasonDTO reason, | ||
| HttpHeaders headers, HttpServletRequest request) { | ||
|
|
||
| ApiResponse<Object> body = ApiResponse.onFailure(reason.getCode(),reason.getMessage(),null); | ||
| // e.printStackTrace(); | ||
|
|
||
| WebRequest webRequest = new ServletWebRequest(request); | ||
| return super.handleExceptionInternal( | ||
| e, | ||
| body, | ||
| headers, | ||
| reason.getHttpStatus(), | ||
| webRequest | ||
| ); | ||
| } | ||
|
|
||
| private ResponseEntity<Object> handleExceptionInternalFalse(Exception e, ErrorStatus errorCommonStatus, | ||
| HttpHeaders headers, HttpStatus status, WebRequest request, String errorPoint) { | ||
| ApiResponse<Object> body = ApiResponse.onFailure(errorCommonStatus.getCode(),errorCommonStatus.getMessage(),errorPoint); | ||
| return super.handleExceptionInternal( | ||
| e, | ||
| body, | ||
| headers, | ||
| status, | ||
| request | ||
| ); | ||
| } | ||
|
|
||
| private ResponseEntity<Object> handleExceptionInternalArgs(Exception e, HttpHeaders headers, ErrorStatus errorCommonStatus, | ||
| WebRequest request, Map<String, String> errorArgs) { | ||
| ApiResponse<Object> body = ApiResponse.onFailure(errorCommonStatus.getCode(),errorCommonStatus.getMessage(),errorArgs); | ||
| return super.handleExceptionInternal( | ||
| e, | ||
| body, | ||
| headers, | ||
| errorCommonStatus.getHttpStatus(), | ||
| request | ||
| ); | ||
| } | ||
|
|
||
| private ResponseEntity<Object> handleExceptionInternalConstraint(Exception e, ErrorStatus errorCommonStatus, | ||
| HttpHeaders headers, WebRequest request) { | ||
| ApiResponse<Object> body = ApiResponse.onFailure(errorCommonStatus.getCode(), errorCommonStatus.getMessage(), null); | ||
| return super.handleExceptionInternal( | ||
| e, | ||
| body, | ||
| headers, | ||
| errorCommonStatus.getHttpStatus(), | ||
| request | ||
| ); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package com.example.phrasebe.apiPayload.exception; | ||
|
|
||
| import com.example.phrasebe.apiPayload.code.BaseErrorCode; | ||
| import com.example.phrasebe.apiPayload.code.ErrorReasonDTO; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Getter; | ||
|
|
||
| @Getter | ||
| @AllArgsConstructor | ||
| public class GeneralException extends RuntimeException{ | ||
|
|
||
| private BaseErrorCode code; | ||
|
|
||
| public ErrorReasonDTO getErrorReason() { | ||
| return this.code.getReason(); | ||
| } | ||
|
|
||
| public ErrorReasonDTO getErrorReasonHttpStatus(){ | ||
| return this.code.getReasonHttpStatus(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package com.example.phrasebe.apiPayload.exception.handler; | ||
|
|
||
| import com.example.phrasebe.apiPayload.code.BaseErrorCode; | ||
| import com.example.phrasebe.apiPayload.exception.GeneralException; | ||
|
|
||
| public class ExceptionHandler extends GeneralException { | ||
| public ExceptionHandler(BaseErrorCode code) { | ||
| super(code); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isSuccess 필드에만 @JsonProperty 어노테이션을 추가한 이유가 있나요?