├── .gitignore ├── README.md ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── example │ │ └── restfulwebservice │ │ ├── RestfulWebServiceApplication.java │ │ ├── config │ │ ├── SecurityConfig.java │ │ └── SwaggerConfig.java │ │ ├── exception │ │ ├── CustomizedResponseEntityExceptionHandler.java │ │ └── ExceptionResponse.java │ │ ├── helloworld │ │ ├── HelloWorldBean.java │ │ └── HelloWorldController.java │ │ └── user │ │ ├── AdminUserController.java │ │ ├── Post.java │ │ ├── PostRepository.java │ │ ├── User.java │ │ ├── UserController.java │ │ ├── UserDaoService.java │ │ ├── UserJpaController.java │ │ ├── UserNotFoundException.java │ │ ├── UserRepository.java │ │ └── UserV2.java └── resources │ ├── application.yml │ ├── data.sql │ ├── messages.properties │ ├── messages_en.properties │ └── messages_fr.properties └── test └── java └── com └── example └── restfulwebservice └── RestfulWebServiceApplicationTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | 30 | ### VS Code ### 31 | .vscode/ 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Spring Boot를 이용한 RESTful Web Service 개발 2 | 3 | 1. 사용자 관리 API 4 | * 전체 사용자 목록 조회: GET HTTP Method, http://localhost:8088/users 5 | * 개별 사용자 조회: GET HTTP Method, http://localhost:8088/users/{id} 6 | * 사용자 삭제: DELETE HTTP Method, http://localhost:8088/users/{id} 7 | * 사용자 정보 수정: PUT HTTP Method, http://localhost:8088/users/{id} 8 | 9 | 2. 게시물 관리 API 10 | * 전체 게시물 목록 조회: GET HTTP Method, http://localhost:8088/users/{id}/posts 11 | * 게시물 삭제: DELETE HTTP Method, http://localhost:8088/users/{id}/posts/{post_id} 12 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.1.13.RELEASE 9 | 10 | 11 | com.example 12 | restful-web-service 13 | 0.0.1-SNAPSHOT 14 | restful-web-service 15 | Demo project for Spring Boot 16 | 17 | 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-data-jpa 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-web 29 | 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-devtools 34 | runtime 35 | true 36 | 37 | 38 | com.h2database 39 | h2 40 | runtime 41 | 42 | 43 | org.projectlombok 44 | lombok 45 | true 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-starter-test 50 | test 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-hateoas 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-starter-actuator 59 | 60 | 61 | org.springframework.data 62 | spring-data-rest-hal-browser 63 | 64 | 65 | org.springframework.boot 66 | spring-boot-starter-security 67 | 68 | 69 | 70 | org.springframework.boot 71 | spring-boot-starter-data-jpa 72 | 73 | 74 | com.h2database 75 | h2 76 | runtime 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | io.springfox 86 | springfox-swagger2 87 | 2.9.2 88 | 89 | 90 | io.springfox 91 | springfox-swagger-ui 92 | 2.9.2 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | org.springframework.boot 101 | spring-boot-maven-plugin 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /src/main/java/com/example/restfulwebservice/RestfulWebServiceApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.restfulwebservice; 2 | 3 | import org.hibernate.Session; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.web.servlet.LocaleResolver; 8 | import org.springframework.web.servlet.i18n.SessionLocaleResolver; 9 | 10 | import java.util.Locale; 11 | 12 | @SpringBootApplication 13 | public class RestfulWebServiceApplication { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(RestfulWebServiceApplication.class, args); 17 | } 18 | 19 | @Bean 20 | public LocaleResolver localeResolver() { 21 | SessionLocaleResolver localeResolver = new SessionLocaleResolver(); 22 | localeResolver.setDefaultLocale(Locale.KOREA); 23 | return localeResolver; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/example/restfulwebservice/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.restfulwebservice.config; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 6 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 7 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 8 | 9 | @Configuration 10 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 11 | 12 | @Override 13 | protected void configure(HttpSecurity http) throws Exception { 14 | http.authorizeRequests().antMatchers("/h2-console/**").permitAll(); 15 | http.csrf().disable(); 16 | http.headers().frameOptions().disable(); 17 | } 18 | 19 | @Autowired 20 | public void configureGlobal(AuthenticationManagerBuilder auth) 21 | throws Exception { 22 | auth.inMemoryAuthentication() 23 | .withUser("kenneth") 24 | .password("{noop}test1234") 25 | .roles("USER"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/example/restfulwebservice/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package com.example.restfulwebservice.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import springfox.documentation.service.ApiInfo; 6 | import springfox.documentation.service.Contact; 7 | import springfox.documentation.spi.DocumentationType; 8 | import springfox.documentation.spring.web.plugins.Docket; 9 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.HashSet; 14 | import java.util.Set; 15 | 16 | @Configuration 17 | @EnableSwagger2 18 | public class SwaggerConfig { 19 | private static final Contact DEFAULT_CONTACT = new Contact("Kenneth Lee", 20 | "http://www.joneconsulting.co.kr", "edowon@joneconsulting.co.kr"); 21 | 22 | private static final ApiInfo DEFAULT_API_INFO = new ApiInfo("Awesome API Title", 23 | "My User management REST API service", "1.0", "urn:tos", 24 | DEFAULT_CONTACT, "Apache 2.0", 25 | "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList<>()); 26 | 27 | private static final Set DEFAULT_PRODUCES_AND_CONSUMES = new HashSet<>( 28 | Arrays.asList("application/json", "application/xml")); 29 | 30 | @Bean 31 | public Docket api() { 32 | return new Docket(DocumentationType.SWAGGER_2) 33 | .apiInfo(DEFAULT_API_INFO) 34 | .produces(DEFAULT_PRODUCES_AND_CONSUMES) 35 | .consumes(DEFAULT_PRODUCES_AND_CONSUMES); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/example/restfulwebservice/exception/CustomizedResponseEntityExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.example.restfulwebservice.exception; 2 | 3 | import com.example.restfulwebservice.user.UserNotFoundException; 4 | import org.springframework.http.HttpHeaders; 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.http.ResponseEntity; 7 | import org.springframework.web.bind.MethodArgumentNotValidException; 8 | import org.springframework.web.bind.annotation.ControllerAdvice; 9 | import org.springframework.web.bind.annotation.ExceptionHandler; 10 | import org.springframework.web.bind.annotation.RestController; 11 | import org.springframework.web.context.request.WebRequest; 12 | import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; 13 | 14 | import java.util.Date; 15 | 16 | @RestController 17 | @ControllerAdvice 18 | public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { 19 | 20 | @ExceptionHandler(Exception.class) 21 | public final ResponseEntity handleAllExceptions(Exception ex, WebRequest request) { 22 | ExceptionResponse exceptionResponse = 23 | new ExceptionResponse(new Date(), ex.getMessage(), request.getDescription(false)); 24 | 25 | return new ResponseEntity(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR); 26 | } 27 | 28 | @ExceptionHandler(UserNotFoundException.class) 29 | public final ResponseEntity handleUserNotFoundException(Exception ex, WebRequest request) { 30 | ExceptionResponse exceptionResponse = 31 | new ExceptionResponse(new Date(), ex.getMessage(), request.getDescription(false)); 32 | 33 | return new ResponseEntity(exceptionResponse, HttpStatus.NOT_FOUND); 34 | } 35 | 36 | @Override 37 | protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, 38 | HttpHeaders headers, 39 | HttpStatus status, 40 | WebRequest request) { 41 | ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(), 42 | "Validation Failed", ex.getBindingResult().toString()); 43 | 44 | return new ResponseEntity(exceptionResponse, HttpStatus.BAD_REQUEST); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/example/restfulwebservice/exception/ExceptionResponse.java: -------------------------------------------------------------------------------- 1 | package com.example.restfulwebservice.exception; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.util.Date; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | public class ExceptionResponse { 13 | private Date timestamp; 14 | private String message; 15 | private String details; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/example/restfulwebservice/helloworld/HelloWorldBean.java: -------------------------------------------------------------------------------- 1 | package com.example.restfulwebservice.helloworld; 2 | // lombok 3 | 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | @NoArgsConstructor 11 | public class HelloWorldBean { 12 | private String message; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/example/restfulwebservice/helloworld/HelloWorldController.java: -------------------------------------------------------------------------------- 1 | package com.example.restfulwebservice.helloworld; 2 | 3 | import com.example.restfulwebservice.user.User; 4 | import com.example.restfulwebservice.user.UserDaoService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.MessageSource; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.PathVariable; 9 | import org.springframework.web.bind.annotation.RequestHeader; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import java.util.Locale; 13 | 14 | @RestController 15 | public class HelloWorldController { 16 | 17 | @Autowired 18 | private MessageSource messageSource; 19 | 20 | // GET 21 | // /hello-world (endpoint) 22 | // @RequestMapping(method=RequestMethod.GET, path="/hello-world") 23 | @GetMapping(path = "/hello-world") 24 | public String helloWorld() { 25 | return "Hello World"; 26 | } 27 | 28 | // alt + enter 29 | @GetMapping(path = "/hello-world-bean") 30 | public HelloWorldBean helloWorldBean() { 31 | return new HelloWorldBean("Hello World"); 32 | } 33 | 34 | @GetMapping(path = "/hello-world-bean/path-variable/{name}") 35 | public HelloWorldBean helloWorldBean(@PathVariable String name) { 36 | return new HelloWorldBean(String.format("Hello World, %s", name)); 37 | } 38 | 39 | @GetMapping(path = "/hello-world-internationalized") 40 | public String helloWorldInternationalized( 41 | @RequestHeader(name="Accept-Language", required=false) Locale locale) { 42 | return messageSource.getMessage("greeting.message", null, locale); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/example/restfulwebservice/user/AdminUserController.java: -------------------------------------------------------------------------------- 1 | package com.example.restfulwebservice.user; 2 | 3 | import com.fasterxml.jackson.databind.ser.FilterProvider; 4 | import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; 5 | import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; 6 | import org.springframework.beans.BeanUtils; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.http.converter.json.MappingJacksonValue; 9 | import org.springframework.web.bind.annotation.*; 10 | import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 11 | 12 | import javax.validation.Valid; 13 | import java.net.URI; 14 | import java.util.List; 15 | 16 | @RestController 17 | @RequestMapping("/admin") 18 | public class AdminUserController { 19 | private UserDaoService service; 20 | 21 | public AdminUserController(UserDaoService service) { 22 | this.service = service; 23 | } 24 | 25 | @GetMapping("/users") 26 | public MappingJacksonValue retrieveAllUsers() { 27 | List users = service.findAll(); 28 | 29 | SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter 30 | .filterOutAllExcept("id", "name", "joinDate", "password"); 31 | 32 | FilterProvider filters = new SimpleFilterProvider().addFilter("UserInfo", filter); 33 | 34 | MappingJacksonValue mapping = new MappingJacksonValue(users); 35 | mapping.setFilters(filters); 36 | 37 | return mapping; 38 | } 39 | 40 | // GET /admin/users/1 -> /admin/v1/users/1 41 | // @GetMapping("/v1/users/{id}") 42 | // @GetMapping(value = "/users/{id}/", params = "version=1") 43 | // @GetMapping(value = "/users/{id}", headers="X-API-VERSION=1") 44 | @GetMapping(value = "/users/{id}", produces = "application/vnd.company.appv1+json") 45 | public MappingJacksonValue retrieveUserV1(@PathVariable int id) { 46 | User user = service.findOne(id); 47 | 48 | if (user == null) { 49 | throw new UserNotFoundException(String.format("ID[%s] not found", id)); 50 | } 51 | 52 | SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter 53 | .filterOutAllExcept("id", "name", "password", "ssn"); 54 | 55 | FilterProvider filters = new SimpleFilterProvider().addFilter("UserInfo", filter); 56 | 57 | MappingJacksonValue mapping = new MappingJacksonValue(user); 58 | mapping.setFilters(filters); 59 | 60 | return mapping; 61 | } 62 | 63 | // @GetMapping("/v2/users/{id}") 64 | // @GetMapping(value = "/users/{id}/", params = "version=2") 65 | // @GetMapping(value = "/users/{id}", headers="X-API-VERSION=2") 66 | @GetMapping(value = "/users/{id}", produces = "application/vnd.company.appv2+json") 67 | public MappingJacksonValue retrieveUserV2(@PathVariable int id) { 68 | User user = service.findOne(id); 69 | 70 | if (user == null) { 71 | throw new UserNotFoundException(String.format("ID[%s] not found", id)); 72 | } 73 | 74 | // User -> UserV2 75 | UserV2 userV2 = new UserV2(); 76 | BeanUtils.copyProperties(user, userV2); // id, name, joinDate, password, ssn 77 | userV2.setGrade("VIP"); 78 | 79 | SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter 80 | .filterOutAllExcept("id", "name", "joinDate", "grade"); 81 | 82 | FilterProvider filters = new SimpleFilterProvider().addFilter("UserInfoV2", filter); 83 | 84 | MappingJacksonValue mapping = new MappingJacksonValue(userV2); 85 | mapping.setFilters(filters); 86 | 87 | return mapping; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/example/restfulwebservice/user/Post.java: -------------------------------------------------------------------------------- 1 | package com.example.restfulwebservice.user; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import javax.persistence.*; 9 | 10 | @Entity 11 | @Data 12 | @AllArgsConstructor 13 | @NoArgsConstructor 14 | public class Post { 15 | @Id 16 | @GeneratedValue 17 | private Integer id; 18 | 19 | private String description; 20 | 21 | // User : Post -> 1 : (0~N), Main : Sub -> Parent : Child 22 | @ManyToOne(fetch = FetchType.LAZY) 23 | @JsonIgnore 24 | private User user; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/example/restfulwebservice/user/PostRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.restfulwebservice.user; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | @Repository 7 | public interface PostRepository extends JpaRepository { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/example/restfulwebservice/user/User.java: -------------------------------------------------------------------------------- 1 | package com.example.restfulwebservice.user; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFilter; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 6 | import io.swagger.annotations.ApiModel; 7 | import io.swagger.annotations.ApiModelProperty; 8 | import lombok.AllArgsConstructor; 9 | import lombok.Data; 10 | import lombok.NoArgsConstructor; 11 | 12 | import javax.persistence.Entity; 13 | import javax.persistence.GeneratedValue; 14 | import javax.persistence.Id; 15 | import javax.persistence.OneToMany; 16 | import javax.validation.constraints.Past; 17 | import javax.validation.constraints.Size; 18 | import java.util.Date; 19 | import java.util.List; 20 | 21 | @Data 22 | @AllArgsConstructor 23 | //@JsonIgnoreProperties(value={"password"}) 24 | @NoArgsConstructor 25 | //@JsonFilter("UserInfo") 26 | @ApiModel(description = "사용자 상세 정보를 위한 도메인 객체") 27 | @Entity 28 | public class User { 29 | @Id 30 | @GeneratedValue 31 | private Integer id; 32 | 33 | @Size(min=2, message = "Name은 2글자 이상 입력해 주세요.") 34 | @ApiModelProperty(notes = "사용자 이름을 입력해 주세요.") 35 | private String name; 36 | @Past 37 | @ApiModelProperty(notes = "사용자의 등록일을 입력해 주세요.") 38 | private Date joinDate; 39 | 40 | @ApiModelProperty(notes = "사용자의 패스워드를 입력해 주세요.") 41 | private String password; 42 | @ApiModelProperty(notes = "사용자의 주민번호를 입력해 주세요.") 43 | private String ssn; 44 | 45 | @OneToMany(mappedBy = "user") 46 | private List posts; 47 | 48 | public User(int id, String name, Date joinDate, String password, String ssn) { 49 | this.id = id; 50 | this.name = name; 51 | this.joinDate = joinDate; 52 | this.password = password; 53 | this.ssn = ssn; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/example/restfulwebservice/user/UserController.java: -------------------------------------------------------------------------------- 1 | package com.example.restfulwebservice.user; 2 | 3 | import org.springframework.hateoas.Resource; 4 | import org.springframework.hateoas.mvc.ControllerLinkBuilder; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.*; 7 | import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 8 | 9 | import javax.validation.Valid; 10 | import java.net.URI; 11 | import java.util.List; 12 | 13 | import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; 14 | import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; 15 | 16 | @RestController 17 | public class UserController { 18 | private UserDaoService service; 19 | 20 | public UserController(UserDaoService service) { 21 | this.service = service; 22 | } 23 | 24 | @GetMapping("/users") 25 | public List retrieveAllUsers() { 26 | return service.findAll(); 27 | } 28 | 29 | // GET /users/1 or /users/10 -> String 30 | @GetMapping("/users/{id}") 31 | public Resource retrieveUser(@PathVariable int id) { 32 | User user = service.findOne(id); 33 | 34 | if (user == null) { 35 | throw new UserNotFoundException(String.format("ID[%s] not found", id)); 36 | } 37 | 38 | // HATEOAS 39 | Resource resource = new Resource<>(user); 40 | ControllerLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllUsers()); 41 | resource.add(linkTo.withRel("all-users")); 42 | 43 | return resource; 44 | } 45 | 46 | @PostMapping("/users") 47 | public ResponseEntity createUser(@Valid @RequestBody User user) { 48 | User savedUser = service.save(user); 49 | 50 | URI location = ServletUriComponentsBuilder.fromCurrentRequest() 51 | .path("/{id}") 52 | .buildAndExpand(savedUser.getId()) 53 | .toUri(); 54 | 55 | return ResponseEntity.created(location).build(); 56 | } 57 | 58 | @DeleteMapping("/users/{id}") 59 | public void deleteUser(@PathVariable int id) { 60 | User user = service.deleteById(id); 61 | 62 | if (user == null) { 63 | throw new UserNotFoundException(String.format("ID[%s] not found", id)); 64 | } 65 | } 66 | 67 | // {id: "1", name: "new name", password: "new password"} 68 | @PutMapping("/users/{id}") 69 | public Resource updateUser(@PathVariable int id, @RequestBody User user) { 70 | User updatedUser = service.update(id, user); 71 | 72 | if (updatedUser == null) { 73 | throw new UserNotFoundException(String.format("ID[%s] not found", user.getId())); 74 | } 75 | 76 | // HATEOAS 77 | Resource resource = new Resource<>(updatedUser); 78 | ControllerLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllUsers()); 79 | resource.add(linkTo.withRel("all-users")); 80 | 81 | return resource; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/example/restfulwebservice/user/UserDaoService.java: -------------------------------------------------------------------------------- 1 | package com.example.restfulwebservice.user; 2 | 3 | import org.springframework.stereotype.Component; 4 | import org.springframework.stereotype.Service; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Date; 8 | import java.util.Iterator; 9 | import java.util.List; 10 | 11 | @Service 12 | public class UserDaoService { 13 | private static List users = new ArrayList<>(); 14 | 15 | private static int usersCount = 3; 16 | 17 | static { 18 | users.add(new User(1, "Kenneth", new Date(), "pass1", "701010-1111111")); 19 | users.add(new User(2, "Alice", new Date(), "pass2", "801010-2222222")); 20 | users.add(new User(3, "Elena", new Date(), "pass2", "901010-1111111")); 21 | } 22 | 23 | public List findAll() { 24 | return users; 25 | } 26 | 27 | public User save(User user) { 28 | if (user.getId() == null) { 29 | user.setId(++usersCount); 30 | } 31 | 32 | users.add(user); 33 | return user; 34 | } 35 | 36 | public User findOne(int id) { 37 | for (User user : users) { 38 | if (user.getId() == id) { 39 | return user; 40 | } 41 | } 42 | 43 | return null; 44 | } 45 | 46 | public User deleteById(int id) { 47 | Iterator iterator = users.iterator(); 48 | 49 | while (iterator.hasNext()) { 50 | User user = iterator.next(); 51 | 52 | if (user.getId() == id) { 53 | iterator.remove(); 54 | return user; 55 | } 56 | } 57 | 58 | return null; 59 | } 60 | 61 | public User update(int id, User user) { 62 | for (User storedUser : users) { 63 | if (storedUser.getId() == id) { 64 | storedUser.setName(user.getName()); 65 | storedUser.setPassword(user.getPassword()); 66 | 67 | return storedUser; 68 | } 69 | } 70 | 71 | return null; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/example/restfulwebservice/user/UserJpaController.java: -------------------------------------------------------------------------------- 1 | package com.example.restfulwebservice.user; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.hateoas.Resource; 5 | import org.springframework.hateoas.mvc.ControllerLinkBuilder; 6 | import org.springframework.http.ResponseEntity; 7 | import org.springframework.web.bind.annotation.*; 8 | import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 9 | 10 | import javax.validation.Valid; 11 | import java.net.URI; 12 | import java.util.List; 13 | import java.util.Optional; 14 | 15 | import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo; 16 | import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn; 17 | 18 | @RestController 19 | @RequestMapping("/jpa") 20 | public class UserJpaController { 21 | @Autowired 22 | private UserRepository userRepository; 23 | 24 | @Autowired 25 | private PostRepository postRepository; 26 | 27 | // http://localhost:8088/jpa/users or http://localhost:8088/users 28 | @GetMapping("/users") 29 | public List retrieveAllUsers() { 30 | return userRepository.findAll(); 31 | } 32 | 33 | @GetMapping("/users/{id}") 34 | public Resource retrieveUser(@PathVariable int id) { 35 | Optional user = userRepository.findById(id); 36 | 37 | if (!user.isPresent()) { 38 | throw new UserNotFoundException(String.format("ID[%s} not found", id)); 39 | } 40 | 41 | Resource resource = new Resource<>(user.get()); 42 | ControllerLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllUsers()); 43 | resource.add(linkTo.withRel("all-users")); 44 | 45 | return resource; 46 | } 47 | 48 | @PutMapping("/users/{id}") 49 | public ResponseEntity updateUser(@PathVariable int id, @RequestBody User user) { 50 | Optional optionalUser = userRepository.findById(id); 51 | 52 | if (!optionalUser.isPresent()) { 53 | throw new UserNotFoundException(String.format("ID[%s} not found", id)); 54 | } 55 | 56 | User storedUser = optionalUser.get(); 57 | storedUser.setName(user.getName()); 58 | storedUser.setPassword(user.getPassword()); 59 | 60 | User updatedUser = userRepository.save(storedUser); 61 | 62 | URI location = ServletUriComponentsBuilder.fromCurrentRequest() 63 | .path("/{id}") 64 | .buildAndExpand(updatedUser.getId()) 65 | .toUri(); 66 | 67 | return ResponseEntity.created(location).build(); 68 | } 69 | 70 | @DeleteMapping("/users/{id}") 71 | public void deleteUser(@PathVariable int id) { 72 | userRepository.deleteById(id); 73 | } 74 | 75 | @PostMapping("/users") 76 | public ResponseEntity createUser(@Valid @RequestBody User user) { 77 | User savedUser = userRepository.save(user); 78 | 79 | URI location = ServletUriComponentsBuilder.fromCurrentRequest() 80 | .path("/{id}") 81 | .buildAndExpand(savedUser.getId()) 82 | .toUri(); 83 | 84 | return ResponseEntity.created(location).build(); 85 | } 86 | 87 | // /jpa/users/90001/posts 88 | @GetMapping("/users/{id}/posts") 89 | public List retrieveAllPostsByUser(@PathVariable int id) { 90 | Optional user = userRepository.findById(id); 91 | 92 | if (!user.isPresent()) { 93 | throw new UserNotFoundException(String.format("ID[%s} not found", id)); 94 | } 95 | 96 | return user.get().getPosts(); 97 | } 98 | 99 | @PostMapping("/users/{id}/posts") 100 | public ResponseEntity createPost(@PathVariable int id, @RequestBody Post post) { 101 | Optional user = userRepository.findById(id); 102 | 103 | if (!user.isPresent()) { 104 | throw new UserNotFoundException(String.format("ID[%s} not found", id)); 105 | } 106 | 107 | post.setUser(user.get()); 108 | Post savedPost = postRepository.save(post); 109 | 110 | URI location = ServletUriComponentsBuilder.fromCurrentRequest() 111 | .path("/{id}") 112 | .buildAndExpand(savedPost.getId()) 113 | .toUri(); 114 | 115 | return ResponseEntity.created(location).build(); 116 | } 117 | 118 | @DeleteMapping("/users/{id}/posts/{post_id}") 119 | public void deletePost(@PathVariable int id, @PathVariable int post_id) { 120 | Optional user = userRepository.findById(id); 121 | 122 | if (!user.isPresent()) { 123 | throw new UserNotFoundException(String.format("ID[%s} not found", id)); 124 | } else { 125 | postRepository.deleteById(post_id); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/com/example/restfulwebservice/user/UserNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.example.restfulwebservice.user; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | // HTTP Status code 7 | // 2XX -> OK 8 | // 4XX -> Client 9 | // 5XX -> Server 10 | @ResponseStatus(HttpStatus.NOT_FOUND) 11 | public class UserNotFoundException extends RuntimeException { 12 | public UserNotFoundException(String message) { 13 | super(message); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/example/restfulwebservice/user/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.restfulwebservice.user; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | @Repository 7 | public interface UserRepository extends JpaRepository { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/example/restfulwebservice/user/UserV2.java: -------------------------------------------------------------------------------- 1 | package com.example.restfulwebservice.user; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFilter; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | import javax.validation.constraints.Past; 9 | import javax.validation.constraints.Size; 10 | import java.util.Date; 11 | 12 | @Data 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | @JsonFilter("UserInfoV2") 16 | public class UserV2 extends User { 17 | private String grade; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8088 3 | 4 | logging: 5 | level: 6 | org.springframework: DEBUG 7 | 8 | spring: 9 | jpa: 10 | show-sql: true 11 | messages: 12 | basename: messages 13 | security: 14 | user: 15 | name: username 16 | password: passw0rd 17 | 18 | management: 19 | endpoints: 20 | web: 21 | exposure: 22 | include: "*" 23 | -------------------------------------------------------------------------------- /src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | insert into user values(90001, sysdate(), 'User1', 'test1111', '701010-1111111'); 2 | insert into user values(90002, sysdate(), 'User2', 'test2222', '801010-2222222'); 3 | insert into user values(90003, sysdate(), 'User3', 'test3333', '901010-1111111'); 4 | 5 | insert into post values(10001, 'My first post', 90001); 6 | insert into post values(10002, 'My second post', 90001); -------------------------------------------------------------------------------- /src/main/resources/messages.properties: -------------------------------------------------------------------------------- 1 | greeting.message=안녕하세요 -------------------------------------------------------------------------------- /src/main/resources/messages_en.properties: -------------------------------------------------------------------------------- 1 | greeting.message=Hello -------------------------------------------------------------------------------- /src/main/resources/messages_fr.properties: -------------------------------------------------------------------------------- 1 | greeting.message=Bonjour -------------------------------------------------------------------------------- /src/test/java/com/example/restfulwebservice/RestfulWebServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.example.restfulwebservice; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class RestfulWebServiceApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | --------------------------------------------------------------------------------