[RESTful Web Service]-4

Dowon Lee님의 Spring Boot를 이용한 RESTful Web Services 개발 강의를 학습한 내용입니다.

RESTful Service 기능 확장

Validation API

의존성 추가

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
            <version>2.5.6</version>
        </dependency>
    @Size(min = 2, message = "Name은 2글자 이상")
    private String name;
    @Past
    private Date joinDate;

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

@PostMapping("/users")
    public ResponseEntity<User> createUser(@Valid @RequestBody User user)

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

@RestControllerAdvice
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
//...

    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

        ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(),
                "Validation Failed", ex.getBindingResult().toString());
        return new ResponseEntity(exceptionResponse, HttpStatus.BAD_REQUEST);
    }
}
  • 2글자 미만의 name을 POST할 때 400Bad Request

다국어 처리

@SpringBootApplication
public class RestfulWebServiceApplication {
//...
    @Bean
    public LocaleResolver localeResolver(){
        SessionLocaleResolver localeResolver = new SessionLocaleResolver();
        localeResolver.setDefaultLocale(Locale.KOREA);
        return localeResolver;
    }
}

HelloWorldController

public class HelloWorldController {

    @Autowired 
    private MessageSource messageSource;
    //...
        @GetMapping(path = "/hello-world-internationalized")
    public String helloWorldInternationalized(@RequestHeader(name = "Accept-Language", required = false) Locale locale){

        return messageSource.getMessage("greeting.message", null, locale);
    }
}
  • @Autowired : 의존성 주입

application.yml

spring:
  messages:
    basename: messages

messages.properties(default, en, fr)

greeting.message = 안녕하세요

greeting.message = Hello

greeting.message = Bonjour

Response 데이터 형식 변환(XML)

의존성 추가

        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
            <version>2.10.2</version>
        </dependency>

Response 데이터 제어(Filtering)

    @JsonIgnore //외부에 노출시키지 않음
    private String password;
    @JsonIgnore
    private String ssn;
  • @JsonIgnore : 외부에 노출시키지 않음

@JsonIgnore 없이

@JsonIgnoreProperties(value = {"password", "ssn"})
public class User {
//...
}
  • 동일한 결과

프로그래밍으로 제어하는 Filtering

개별 사용자 조회

@JsonFilter("UserInfo")
public class User {
//...
}
  • @JsonFilter(필터이름)
@RestController
@RequestMapping("/admin")
public class AdminUserController {
//...

    @GetMapping("/users/{id}")
    public MappingJacksonValue retrieveUser(@PathVariable int id) {
        User user = service.findOne(id);

        if (user == null) {
            throw new UserNotFoundException(String.format("ID[%s] not found", id));
        }

        SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter
                .filterOutAllExcept("id", "name", "joinDate", "ssn"); 
        FilterProvider filters = new SimpleFilterProvider().addFilter("UserInfo", filter);

        MappingJacksonValue mapping = new MappingJacksonValue(user);
        mapping.setFilters(filters);

        return mapping;
    }
}
  • @RequestMapping(" ") : prefix
  • SimpleBeanPropertyFilter.filterOutAllExcept(""); : 포함하는 것

전체 사용자 조회

    @GetMapping("/users")
    public MappingJacksonValue retrieveAllUsers() {
        List<User> users = service.findAll();
        SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter
                .filterOutAllExcept("id", "name", "joinDate", "ssn"); 
        FilterProvider filters = new SimpleFilterProvider().addFilter("UserInfo", filter);

        MappingJacksonValue mapping = new MappingJacksonValue(users);
        mapping.setFilters(filters);

        return mapping;
    }

URI를 이용한 REST API 버전 관리

@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonFilter("UserInfoV2")
public class UserV2 extends User{
    private String grade;
}
public class AdminUserController {
//...
    @GetMapping("/v2/users/{id}")
    public MappingJacksonValue retrieveUserV2(@PathVariable int id) {
        User user = service.findOne(id);

        if (user == null) {
            throw new UserNotFoundException(String.format("ID[%s] not found", id));
        }

        UserV2 userV2 = new UserV2();
        BeanUtils.copyProperties(user, userV2);
        userV2.setGrade("VIP");

        SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter
                .filterOutAllExcept("id", "name", "joinDate", "grade"); //포함하는 것

        FilterProvider filters = new SimpleFilterProvider().addFilter("UserInfoV2", filter);

        MappingJacksonValue mapping = new MappingJacksonValue(userV2);
        mapping.setFilters(filters);

        return mapping;
    }
}

Request Parameter를 이용한 버전 관리

    @GetMapping(value = "/users/{id}/", params = "version=1")
    public MappingJacksonValue retrieveUserV1(@PathVariable int id) {
    //...
    }

    @GetMapping(value = "/users/{id}/", params = "version=2")
    public MappingJacksonValue retrieveUserV2(@PathVariable int id) {
    //...
    }
  • ex) localhost:8088/admin/users/1/?version=2

Header를 이용한 버전 관리

    @GetMapping(value = "/users/{id}", headers = "X-API-VERSION=1")
    public MappingJacksonValue retrieveUserV1(@PathVariable int id){
    //...
    }
    @GetMapping(value = "/users/{id}", headers = "X-API-VERSION=2")
    public MappingJacksonValue retrieveUserV2(@PathVariable int id){
    //...
    }

MIME Type을 이용한 버전 관리

    @GetMapping(value = "/users/{id}", produces = "application/vnd.company.appv1+json")
    public MappingJacksonValue retrieveUserV1(@PathVariable int id){
    //...
    }
    @GetMapping(value = "/users/{id}", produces = "application/vnd.company.appv2+json")
    public MappingJacksonValue retrieveUserV2(@PathVariable int id){
    //...
    }
  • URI, RequestParameter : 일반 브라우저에서 실행 가능
  • Header, MIME Type : 일반 브라우저 실행 불가능

좋은 웹페이지 즐겨찾기