[사이드프로젝트] 그저 그런 REST API로 괜찮은가? - 진정한 REST API 구현해보기 - Event Controller & Event Exception Handler
EventController Test 만들기
테스트 케이스는 다음과 같다.
- 정상적인 EventDto를 보냈을 때 DB에 insert되서 id 값을 담은 Event를 반환하는가
- 잘못된 EventDto를 보냈을 때 Error Code가 반환되는가.
고려 사항
- mockMvc에 content를 담을 때, Json 형식으로 담는다 - objectMapper 활용
- Error Code는 Interceptor를 활용해서 반환한다.
- 현재 Service Layer가 없다 - 임시로 Repository를 컨트롤러 쪽에서 사용한다.
package com.carrykim.restapi.event;
import com.carrykim.restapi.event.model.dto.EventDto;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.HttpHeaders;
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.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
@SpringBootTest
@AutoConfigureMockMvc
public class EventControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
private EventDto createEventDto(){
return EventDto.builder()
.name("My Event")
.description("This is my first Event")
.build();
}
@Test
public void create_event_success() throws Exception {
//Given
EventDto eventDto = createEventDto();
//When
//Then
mockMvc.perform(post("/api/events")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaTypes.HAL_JSON)
.content(objectMapper.writeValueAsString(eventDto)))
.andDo(print())
.andExpect(status().isCreated())
.andExpect(jsonPath("id").exists())
.andExpect(header().exists(HttpHeaders.LOCATION))
.andExpect(header().string(HttpHeaders.CONTENT_TYPE,MediaTypes.HAL_JSON_VALUE));
}
@Test
public void create_event_bad_request_empty_input() throws Exception {
//Given
EventDto eventDto = EventDto.builder().build();
//When
//Then
mockMvc.perform(post("/api/events")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(eventDto)))
.andDo(print())
.andExpect(status().isBadRequest())
.andExpect(jsonPath("timestamp").exists())
.andExpect(jsonPath("status").exists())
.andExpect(jsonPath("error").exists())
.andExpect(jsonPath("message").exists());
}
}
EventController 만들기
package com.carrykim.restapi.event.controller;
import com.carrykim.restapi.event.model.Event;
import com.carrykim.restapi.event.infra.EventRepository;
import com.carrykim.restapi.event.model.dto.EventDto;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.net.URI;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
@RestController()
@RequestMapping(value = "/api/events", produces = MediaTypes.HAL_JSON_VALUE)
public class EventController {
private final EventRepository eventRepository;
public EventController(EventRepository eventRepository) {
this.eventRepository = eventRepository;
}
@PostMapping("")
public ResponseEntity create(@RequestBody @Valid EventDto eventDto) {
Event newEvent = this.eventRepository.save(eventDto.toModel());
URI uri = linkTo(methodOn(EventController.class).create(new EventDto())).slash(newEvent.getId()).toUri();
return ResponseEntity.created(uri).body(newEvent);
}
}
ErrorResponse 만들기
- Response에서 자동으로 Json 매칭을 사용하기 위해선 Getter 선언이 필수!
- Request도 마찬가지 -> Lombok의 Getter 어노테이션 사용
package com.carrykim.restapi.event.global.exception;
import lombok.Builder;
import lombok.Getter;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
@Getter
public class ErrorResponse {
private final LocalDateTime timestamp = LocalDateTime.now();
private int status;
private List<String> message;
private String error;
public ErrorResponse(HttpStatus httpStatus , BindingResult bindingResult) {
this.status = httpStatus.value();
this.error = httpStatus.name();
this.message = bindingResult.getFieldErrors().stream()
.map(e -> {
String m = e.getField() + " : " + e.getDefaultMessage();
return m;
})
.collect(Collectors.toList());
}
}
EventExceptionHandler 만들기
package com.carrykim.restapi.event.global.exception;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class EventExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity validationErrorException(final MethodArgumentNotValidException e) throws JsonProcessingException {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse(HttpStatus.BAD_REQUEST,e.getBindingResult()));
}
}
Author And Source
이 문제에 관하여([사이드프로젝트] 그저 그런 REST API로 괜찮은가? - 진정한 REST API 구현해보기 - Event Controller & Event Exception Handler), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@carrykim/사이드프로젝트-그저-그런-REST-API로-괜찮은가-진정한-REST-API-구현해보기-Event-Controller-Event-Exception-Handler저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)