Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- 네트워크 설정
- iterator
- 티스토리 쿠키 삭제
- 페이징
- 목록처리
- 자바편
- 코드로배우는스프링부트웹프로젝트
- 자료구조와함께배우는알고리즘입문
- 구멍가게코딩단
- ㅒ
- Kernighan의 C언어 프로그래밍
- baeldung
- resttemplate
- 친절한SQL튜닝
- 스프링 시큐리티
- /etc/network/interfaces
- 자료구조와 함께 배우는 알고리즘 입문
- 선형대수
- 알파회계
- network configuration
- GIT
- 서버설정
- d
- 처음 만나는 AI수학 with Python
- 데비안
- 코드로배우는스프링웹프로젝트
- 리눅스
- 스프링부트핵심가이드
- 처음 만나는 AI 수학 with Python
- 이터레이터
Archives
- Today
- Total
bright jazz music
blog03: 데이터 검증3 (ExceptionController) 본문
요약:
테스트 코드 ==> PostController ==> 바인딩 에러 발생 ==> ExceptionController ==> 에러를 꺼내서 ErrorResponse 객체에 바인딩하여 json형식으로 클라이언트에게 반환.
예상된 값(여기서는 코드 또는 메시지가 반환되지 않으면 에러가 발생하는 문제가 있음)
---
test2() 실행하여 json형식으로 PostController에 request
//PostControllerTest.java
package com.endofma.blog.controller;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; //이게 맞음
//import static org.springframework.test.web.client.match.MockRestRequestMatchers.jsonPath; 원래 이걸로 했음.
@WebMvcTest
class PostControllerTest {
@Autowired
private MockMvc mockMvc; ////Could not autowire. No beans of 'MockMvc' type found.
@Test
@DisplayName("/posts 요청시 hello world를 출력한다")
void test() throws Exception {
//expected
//기본적으로 Content-Type을 application/json으로 보냄. 예전에는 application/x-www-form-urlencoded를 썼다.
mockMvc.perform(post("/posts")
.contentType(MediaType.APPLICATION_JSON) //기본값이라 주석처리
.content("{\"title\": \"제목입니다.\", \"content\": \"내용입니다.\"}") //json 형태로 값 넣어주기
)
.andExpect(status().isOk()) //http response 가 200인지
.andExpect(content().string("{}")) // 내용이 hello world인지
.andDo(print()); //요청에 대한 전반적인 요약을 출력해준다.
}
//PostControllerTest.java
@Test
@DisplayName("/posts 요청시 title 값은 필수다")
void test2() throws Exception {
//expected
mockMvc.perform(post("/posts")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"title\": null, \"content\": \"내용입니다.\"}") //title 값을 빈 스트링으로 설정하였다.
// .content("{\"title\": \"안녕\", \"content\": \"내용입니다.\"}") //title 값을 빈 스트링으로 설정하였다.
)
.andExpect(status().isBadRequest()) //.OK()
.andExpect(jsonPath("$.code").value("400")) //json 검증
.andExpect(jsonPath("$.message").value("잘못된 요청입니다!"))
.andDo(print());
}
}
PostController 내부에 진입하지 못하고 바인딩 에러 발생
//PostController.java
package com.endofma.blog.controller;
import com.endofma.blog.request.PostCreate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@RestController
public class PostController {
@PostMapping("/posts")
public Map<String, String> post(@RequestBody @Valid PostCreate params) {
//파라미터에서 BindingResult 제거.
//이미 바인딩에서 에러 발생. 여기까지 오지 못함.
log.info("params={}", params.toString());
return Map.of();
}
}
- @ControllerAdvice가 붙은 클래스에서 에러 가로챔.
- MethodArgumentNotValidException 객체를 사용해서 에러를 받고, 변수명은 e로 선언.
- getFieldErrors()를 사용하여 e에서 필드 에러만 뽑아냄.
- FieldError 객체를 사용해서 뽑아낸 에러를 바인딩.
- getField()와 getDefaultMessage()로 fieldError에서 필드와 기본 에러 메시지를 뽑아냄.
- 그걸 우리가 만든 ErrorResponse 객체에 바인딩하여 리턴
//ExceptionController.java
package com.endofma.blog.controller;
import com.endofma.blog.response.ErrorResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@ControllerAdvice
public class ExceptionController {
@ResponseStatus(HttpStatus.BAD_REQUEST) //400
@ResponseBody //응답을 json으로 보내기 위해 추가
@ExceptionHandler(MethodArgumentNotValidException.class)
public ErrorResponse invalidRequestHandler(MethodArgumentNotValidException e) {
ErrorResponse response = new ErrorResponse("400", "잘못된 요청입니다!");
for(FieldError fieldError : e.getFieldErrors()){
response.addValidation(fieldError.getField(), fieldError.getDefaultMessage());
}
return response;
}
}
//ErrorResponse.java
package com.endofma.blog.response;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.HashMap;
import java.util.Map;
/*
* {
* "code": "400",
* "message": "잘못된 요청입니다."
* "validation": {
* "title": "값을 입력해주세요"
* }
* }
*/
@Getter //게터가 없으면 값을 가져올 수 없어서 ResponseBody에 넣어주질 못함.(빈 바디)
@RequiredArgsConstructor
public class ErrorResponse {
private final String code;
private final String message;
private final Map<String, String> vaidation = new HashMap<>(); //final로 선언하면 생성자 파라미터가 3개가 됨. 맵 대신 클래스로 받아보자...
public void addValidation(String fieldName, String errorMessage){
ValidationTuple validationTuple = new ValidationTuple(fieldName, errorMessage);
this.vaidation.put(fieldName, errorMessage);
}
// Map을 안 쓰기 위해서 내부 클래스를 만들었으니 이걸로 위의 Map을 대체해 보자..현재는 안 되어 있음
@RequiredArgsConstructor
private class ValidationTuple{
private final String fieldName;
private final String errorMessage;
}
}
결과
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.7.6)
2022-12-27 12:53:48.209 INFO 36332 --- [ Test worker] c.e.blog.controller.PostControllerTest : Starting PostControllerTest using Java 11.0.12 on DESKTOP-8H1PTVG with PID 36332 (started by markany-hjcha in D:\personal\blog)
2022-12-27 12:53:48.211 INFO 36332 --- [ Test worker] c.e.blog.controller.PostControllerTest : No active profile set, falling back to 1 default profile: "default"
2022-12-27 12:53:49.281 INFO 36332 --- [ Test worker] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet ''
2022-12-27 12:53:49.281 INFO 36332 --- [ Test worker] o.s.t.web.servlet.TestDispatcherServlet : Initializing Servlet ''
2022-12-27 12:53:49.282 INFO 36332 --- [ Test worker] o.s.t.web.servlet.TestDispatcherServlet : Completed initialization in 1 ms
2022-12-27 12:53:49.310 INFO 36332 --- [ Test worker] c.e.blog.controller.PostControllerTest : Started PostControllerTest in 1.483 seconds (JVM running for 3.742)
MockHttpServletRequest:
HTTP Method = POST
Request URI = /posts
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Content-Length:"46"]
Body = {"title": null, "content": "내용입니다."}
Session Attrs = {}
Handler:
Type = com.endofma.blog.controller.PostController
Method = com.endofma.blog.controller.PostController#post(PostCreate)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.web.bind.MethodArgumentNotValidException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 400
Error message = null
Headers = [Content-Type:"application/json"]
Content type = application/json
Body = {"code":"400","message":"잘못된 요청입니다!","vaidation":{"title":"타이틀을 입력하세요!"}}
Forwarded URL = null
Redirected URL = null
Cookies = []
BUILD SUCCESSFUL in 6s
4 actionable tasks: 3 executed, 1 up-to-date
PM 12:53:50: Execution finished ':test --tests "com.endofma.blog.controller.PostControllerTest.test2"'.
-----
e!!!=org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public java.util.Map<java.lang.String, java.lang.String> com.endofma.blog.controller.PostController.post(com.endofma.blog.request.PostCreate): [Field error in object 'postCreate' on field 'title': rejected value [null]; codes [NotBlank.postCreate.title,NotBlank.title,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [postCreate.title,title]; arguments []; default message [title]]; default message [타이틀을 입력하세요!]]
fieldError!!!=Field error in object 'postCreate' on field 'title': rejected value [null]; codes [NotBlank.postCreate.title,NotBlank.title,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [postCreate.title,title]; arguments []; default message [title]]; default message [타이틀을 입력하세요!]
'Projects > blog' 카테고리의 다른 글
blog04: 작성글 저장2 - ObjectMapper(jackson)사용, 클래스 분리 (0) | 2022.12.28 |
---|---|
blog04: 작성글 저장1 - 게시글 저장 구현 (0) | 2022.12.28 |
blog03: 데이터 검증2 (ExceptionController, @ControllerAdvice) (0) | 2022.12.13 |
blog03: 데이터 검증1 (@NotBlank, @Valid, BindingResult) (0) | 2022.12.12 |
blog02: POST로 데이터 전송(x-www-form-urlencoded, Json) (0) | 2022.12.12 |
Comments