├── 04. Membuat Projek Spring Boot dengan Spring Initializr.md ├── 02. Pengenalan REST.md ├── Istilah pada Spring.md ├── 01. Pengenalan Web Service.md ├── 03. Membuat Projek Maven.md ├── 05. Membuat Rest Controller.md └── Draft New.md /04. Membuat Projek Spring Boot dengan Spring Initializr.md: -------------------------------------------------------------------------------- 1 | # Membuat Projek Spring Boot dengan Spring Initializr 2 | - Akses http://start.spring.io (Spring Initializr) 3 | - Generate a "Maven Project" with "Java" and Spring Boot "1.5.6" (Stable Release) 4 | - Untuk Project Metadata, 5 | - Isi Group, misal: com.training.web 6 | - Isi Artifact, misal: projek-pertama 7 | - Pilih Dependencies 8 | - Web, digunakan untuk membuat web aplikasi 9 | - Devtools, digunakan untuk membuat proses pengembangan aplikasi lebih mudah, dikarenakan aplikasi akan reboot secara otomatis jika ada perubahan file 10 | - H2, digunakan untuk menggunakan in memory database 11 | - Security, digunakan untuk menambahkan spring security ke aplikasi 12 | - JPA, digunakan untuk menghubungkan aplikasi ke database 13 | - Klik Generate Project 14 | - Ekstrak file .zip dari projek 15 | - Import projek 16 | - File > Import > Existing Maven Projects 17 | -------------------------------------------------------------------------------- /02. Pengenalan REST.md: -------------------------------------------------------------------------------- 1 | ## Pengenalan REST 2 | REST (Representational State Transfer) dikenalkan dan didefinisikan pada tahun 2000 oleh Roy Fielding pada saat disertasi doktoralnya. REST merupakan suatu gaya arsitektur untuk mendesain sistem terdistribusi. 3 | 4 | ## Prinsip REST 5 | - __Resources__, mengekspos struktur direktori URI yang mudah dipahami 6 | - Suatu resource mempunyai URI (Uniform Resource Identifier) 7 | - Suatu resrouce dapat memiliki representasi yang berbeda: XML, HTML atau JSON. Umumnya JSON digunakan untuk REST. 8 | - __Representations__, mengirimkan JSON atau XML untuk merepresentasikan objek data dan atribut 9 | - __Messages__, menggunakan method HTTP (HyperText Transfer Protocol) seperti GET, POST, PUT dan DELETE 10 | - __Stateless__, tidak ada session yang disimpan di server, session disimpan oleh client. 11 | 12 | ## HTTP methods 13 | Menggunakan method HTTP untuk memapingkan operasi CRUD(Create, Retrieve, Update, Delete) ke request HTTP 14 | 15 | Method | Deskripsi | URI 16 | ------------ | ------------- | ------------- 17 | GET | Digunakan untuk mendapatkan suatu informasi | GET /addresses/1 18 | POST | Digunakan untuk membuat resource baru | POST /addresses 19 | PUT | Digunakan untuk mengupdate resource yang sudah ada. Pada saat proses update, jika hanya beberapa elemen data yang tersedia maka sisa elemen data yang tidak ada akan diupdate null | PUT /addresses/1 20 | PATCH | Digunakan untuk mengupdate spesifik elemen data pada suatu resource | PATCH /addresses/1 21 | DELETE | Digunakan untuk menghapus resource | DELETE /addresses/1 22 | 23 | ## Status Kode HTTP 24 | Status kode HTTP menunjukkan hasil atau respon dari suatu request HTTP. 25 | 26 | Kode | Deskripsi 27 | ------------ | ------------- 28 | 1XX | Informasi 29 | 2XX | Sukses 30 | 3XX | Redirection 31 | 4XX | Error pada klien 32 | 5XX | Error pada server 33 | 34 | ## Media Type 35 | __Accept__ dan __Content-Type__ pada HTTP header digunakan untuk menggambarkan konten yang sedang dikirimkan atau direquest didalam HTTP request. Klien harus mengatur Accept menjadi "application/json", jika klien merequest respon dalam bentuk JSON. sebaliknya, ketika mengirimkan data pada saat melakukan request, klien harus mengatur Content-Type menjadi "application/xml" jika klien mengirimkan data dalam bentuk XML. 36 | 37 | ## Dokumentasi Service Definition 38 | Tools yang digunakan untuk dokumentasi service definition adalah 39 | - WADL (Web Application Definition Language) 40 | - Swagger 41 | -------------------------------------------------------------------------------- /Istilah pada Spring.md: -------------------------------------------------------------------------------- 1 | ## Daftar Istilah Pada Spring 2 | 3 | Istilah | Deskripsi 4 | ------------ | ------------- 5 | @Autowired | Anotasi yang digunakan untuk melakukan inject instance dari suatu bean ke objek yang memiliki dependency 6 | @Configuration | Anotasi yang digunakan agar Spring container dapat membuat definisi bean dari suatu kelas dan menunjukkan bahwa suatu kelas mendeklarasikan satu atau lebih method @bean (kelas konfigurasi). (Best practice) Kelas yang mendefinisikan method main dapat menjadi kelas @Configuration utama. Tidak semua konfigurasi harus diletakkan dalam 1 kelas, anotasi @Import dapat digunakan untuk mengimport kelas @Configuration tambahan. Cara lain dapat menggunakan @ComponentScan untuk mencari secara otomatis kelas @Configuration. 7 | @Component | Anotasi yang digunakan agar Spring container dapat membuat definisi bean dari suatu kelas 8 | @ComponentScan | Anotasi yang digunakan untuk melakukan scan @Component, @Service, @Controller, @RestController, @Repository pada saat aplikasi mulai dijalankan, ketika @Component, @Service, @Controller, @RestController, @Repository ditemukan disinilah proses pembentukan bean terjadi yang nantinya akan digunakan untuk proses dependency injection 9 | @Controller | Anotasi yang digunakan agar Spring container dapat membuat definisi bean dari suatu kelas dan menunjukkan kelas tersebut adalah kelas controller 10 | @Repository | Anotasi yang digunakan agar Spring container dapat membuat definisi bean dari suatu kelas dan menunjukkan kelas tersebut adalah kelas repository 11 | @RestController | Anotasi yang digunakan agar Spring container dapat membuat definisi bean dari suatu kelas dan menunjukkan kelas tersebut adalah kelas rest controller 12 | @RequestMapping | Anotasi yang digunakan untuk melakukan mapping url dengan method 13 | @Service | Anotasi yang digunakan agar Spring container dapat membuat definisi bean dari suatu kelas dan menunjukkan kelas tersebut adalah kelas service 14 | @SpringBootApplication | Anotasi yang digunakan untuk menggantikan deklarasi @Configuration, @EnableAutoConfiguration dan @ComponentScan secara bersamaan dengan menggunakan atribut default dari masing-masing anotasi yang digantikan tadi. Untuk @ComponentScan atribut default untuk package yang discan adalah package yang ada pada kelas yang memiliki anotasi ini 15 | @EnableAutoConfiguration | Anotasi yang digunakan agar Spring dapat melakukan konfigurasi secara otomatis berdasarkan dependency jar yang ditambahkan. (Best practice) Dalam satu projek Spring, anotasi @EnableAutoConfiguration hanya boleh digunakan satu kali dan biasanya ditambahkan hanya di kelas @Configuration utama. 16 | Bean | Objek yang dibuat dan diatur oleh Spring IoC Container 17 | Dispatcher Servlet atau Front Controller | Program yang digunakan untuk mengatur alur kerja pemrosesan request web pada Spring 18 | IoC (Inverse of Control) atau Dependency Injection | Spring akan mengatur dependency suatu objek dengan membuat suatu instance atas dependency objek tersebut 19 | Runtime | Waktu pada saat program mulai dijalankan pada komputer (tahap compile) 20 | Servlet | Program kecil pada web server Java yang akan berjalan secara otomatis sebagai bentuk respon dari inputan user 21 | -------------------------------------------------------------------------------- /01. Pengenalan Web Service.md: -------------------------------------------------------------------------------- 1 | # Outline 2 | - Apa itu Web Service? 3 | - Pertanyaan-Pertanyaan Penting Terkait Web Service 4 | - Web Service - Key Terminology 5 | - Pengenalan RESTful Web Service 6 | 7 | # Apa itu Web Service? 8 | > Software system designed to support interoperable machine-to-machine interaction over a network. - W3C (World Wide Web Consortium) 9 | 10 | Berdasarkan pengertian dari W3C, web service adalah sistem perangkat lunak yang didesain untuk pertukaran dan pemanfaatan informasi dari interaksi mesin-ke-mesin melalui suatu jaringan. 11 | 12 | 3 Karakteristik dari Web Service: 13 | - Didesain untuk interaksi mesin-ke-mesin (atau aplikasi-ke-aplikasi) 14 | - Harus dapat bertukar dan memanfaatkan informasi, tidak bergantung pada suatu teknologi 15 | - Harus dapat berkomunikasi melalui jaringan 16 | 17 | Contoh Web Service : SOAP dan RESTful 18 | 19 | # Pertanyaan-Pertanyaan Penting Terkait Web Service 20 | ## Bagaimana pertukaran data diantara aplikasi berlangsung? 21 | - Input ke web service disebut Request. 22 | - Output dari web service disebut Response. 23 | 24 | Jika suatu aplikasi membutuhkan suatu data dari web service maka aplikasi tersebut akan melakukan request ke web service kemudian web service akan memproses request tersebut dan memberikan output atau response berupa data yang dibutuhkan oleh aplikasi tersebut. 25 | 26 | ## Bagaimana membuat web service tidak bergantung pada teknologi tertentu (platform independent)? 27 | Dengan menggunakan format pertukaran data baik pada Request ataupun Response. 28 | 29 | Format data yang dapat digunakan untuk request dan response adalah 30 | - XML (eXtensible Markup Language) 31 | ``` xml 32 | 33 | 100001 34 | 35 | ``` 36 | 37 | - JSON (JavaScript Object Notation) 38 | ``` json 39 | [ 40 | { 41 | "id": 100001, 42 | "name": "Toko Mandiri" 43 | }, 44 | { 45 | "id": 100002, 46 | "name": "Toko Jaya" 47 | } 48 | ] 49 | ``` 50 | 51 | ## Bagaimana suatu aplikasi dapat mengetahui format request dan response suatu web service? 52 | Dengan menyediakan service definition: 53 | - Request/Response Format: XML atau JSON 54 | - Struktur Request 55 | - Struktur Response 56 | - Endpoint: Bagaimana cara memanggil suatu service biasanya berupa URL 57 | 58 | # Web Service - Key Terminology 59 | - Request dan Response 60 | - Request adalah input ke web service 61 | - Response adalah output dari web service 62 | - Format pertukaran data 63 | - XML dan JSON merupakan format yang digunakan oleh Request atau Response pada saat pertukaran data atau informasi. 64 | - Service Provider atau Server 65 | Aplikasi yang menyediakan service yang akan digunakan oleh service consumer atau klien. 66 | - Service Consumer atau Klien 67 | Aplikasi yang menggunakan service yang disediakan oleh service provider atau server. 68 | - Service Definition 69 | - Format Request/Response 70 | - Struktur Request 71 | - Struktur Response 72 | - Endpoint, bagaimana cara memanggil suatu service biasanya berupa URL. 73 | - Transport 74 | - HTTP 75 | Suatu protokol yang digunakan untuk berkomunikasi melalui internet 76 | 77 | ## Pengenalan RESTful Web Service 78 | https://github.com/tedydarmawan/SpringBoot/blob/master/02.%20Pengenalan%20REST.md 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /03. Membuat Projek Maven.md: -------------------------------------------------------------------------------- 1 | ## Membuat Projek Maven 2 | 3 | Langkah-langkah membuat projek maven di Eclipse atau Spring Tool Suite (STS): 4 | 1. Klik File > New > Maven Project 5 | 2. Centang "Create a simple project (skip archetype selection)" 6 | 3. Isi Group Id: "com.mayora.web" 7 | 4. Isi Artifact Id: "sample-project". 8 | 9 | Semua projek Maven memiliki Group Id dan Artifact Id. Group Id + Artifact Id = Nama Projek. 10 | 5. Biarkan Version: "0.0.1-SNAPSHOT" dan packaging: "jar". 11 | 12 | SNAPSHOT menunjukkan bahwa projek sedang dalam fase development. 13 | 6. Klik Finish 14 | 15 | #### Struktur Projek 16 | src/main/java = source code java 17 | 18 | src/test/java = source code junit java 19 | 20 | #### Dependency 21 | Semua eksternal jar/library yang berhubungan dengan projek 22 | 23 | #### Cara manual mengatur dependency 24 | Download jar secara manual dan letakkan di dalam projek 25 | 26 | #### Maven 27 | Build tool yang dapat digunakan untuk mengatur dependency (dependency management) dengan cara mendownload jar yang berhubungan dengan projek secara otomatis. 28 | 29 | pom.xml = Project Object Model, File konfigurasi untuk projek Maven, yang medefinisikan semua dependency (eksternal jar/library) yang terkait dengan projek. 30 | ``` java 31 | 32 | 4.0.0 33 | com.mayora.web 34 | sample-project 35 | 0.0.1-SNAPSHOT 36 | 37 | ``` 38 | 39 | ## Menambahkan Dependency ke Maven 40 | Cari dependency yang akan ditambahkan di Maven repository, misal spring-core: 41 | ``` java 42 | 43 | org.springframework 44 | spring-core 45 | 4.3.8.RELEASE 46 | 47 | ``` 48 | 49 | Tambahkan dependency tersebut ke dalam pom.xml 50 | ``` java 51 | 52 | 4.0.0 53 | com.mayora.web 54 | sample-project 55 | 0.0.1-SNAPSHOT 56 | 57 | 58 | 59 | 60 | org.springframework 61 | spring-core 62 | 4.3.8.RELEASE 63 | 64 | 65 | 66 | 67 | ``` 68 | Maven akan mendownload dan meletakkan spring-core di CLASSPATH sehingga dependency tersebut dapat digunakan didalam projek. Dapat dilihat pada "Maven Dependencies" di Project Explorer, semua file jar yang terdownload akan muncul disana. 69 | 70 | #### Transitive Dependency 71 | Dependency dari suatu dependency (Transitive dependency). Jika suatu dependency (A) bergantung pada dependency (B) lain maka dependency (B) akan terdownload juga secara otomatis. 72 | 73 | ## 5 Langkah Membuat Projek Spring Boot 74 | 1. Tambahkan Spring Boot Starter Parent dependency 75 | Spring Boot Starter Parent akan menangani versi dari semua dependency Spring, java version dan default plugin. Sehingga dapat memastikan bahwa semua dependency dapat bekerja satu sama lain (compatibility). 76 | ``` xml 77 | 78 | org.springframework.boot 79 | spring-boot-starter-parent 80 | 1.5.3.RELEASE 81 | 82 | ``` 83 | 84 | 2. Tambahkan Spring Boot Starter Web dependency 85 | Spring Boot Starter Web akan menangani semua dependency Spring untuk pengembangan aplikasi web termasuk embedded tomcat yang digunakan sebagai web server. 86 | ``` xml 87 | 88 | 89 | org.springframework.boot 90 | spring-boot-starter-web 91 | 92 | 93 | ``` 94 | 95 | 3. Override Java Version 8 96 | ``` xml 97 | 98 | 1.8 99 | 100 | ``` 101 | 102 | 4. Tambahkan Spring Boot Maven Plugin 103 | Spring Boot Maven Plugin digunakan untuk membuat aplikasi jar/war dan menjalankan aplikasi Spring Boot secara independen 104 | ``` xml 105 | 106 | 107 | 108 | org.springframework.boot 109 | spring-boot-maven-plugin 110 | 111 | 112 | 113 | ``` 114 | 115 | 5. Buat Launcher untuk Spring Boot Application 116 | 117 | @SpringBootApplication sama dengan mendeklarasikan @Configuration, @EnableAutoConfiguration, @ComponentScan secara bersamaan dengan default atribut dari masing-masing anotasi. 118 | 119 | #### Application.java 120 | ``` java 121 | import org.springframework.boot.SpringApplication; 122 | import org.springframework.boot.autoconfigure.SpringBootApplication; 123 | import org.springframework.context.ApplicationContext; 124 | 125 | @SpringBootApplication 126 | public class Application { 127 | 128 | public static void main(String[] args){ 129 | ApplicationContext ctx = SpringApplication.run(Application.class, args); 130 | } 131 | 132 | } 133 | ``` 134 | 135 | ## pom.xml Lengkap 136 | ``` xml 137 | 138 | 4.0.0 139 | com.project.web 140 | sample-project 141 | 0.0.1-SNAPSHOT 142 | 143 | 144 | 1.8 145 | 146 | 147 | 148 | org.springframework.boot 149 | spring-boot-starter-parent 150 | 1.5.3.RELEASE 151 | 152 | 153 | 154 | 155 | org.springframework.boot 156 | spring-boot-starter-web 157 | 158 | 159 | 160 | 161 | 162 | 163 | org.springframework.boot 164 | spring-boot-maven-plugin 165 | 166 | 167 | 168 | 169 | 170 | ``` 171 | -------------------------------------------------------------------------------- /05. Membuat Rest Controller.md: -------------------------------------------------------------------------------- 1 | ## Membuat Rest Controller 2 | 3 | @GetMapping 4 | @PathVariable 5 | 6 | @RequestParam 7 | 8 | JSONView Chrome 9 | 10 | 11 | 12 | ## com/mayora/controller/WelcomeController.java 13 | ``` java 14 | package com.mayora.controller; 15 | 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.web.bind.annotation.RequestMapping; 18 | import org.springframework.web.bind.annotation.RestController; 19 | 20 | import com.mayora.service.WelcomeService; 21 | 22 | @RestController 23 | public class WelcomeController { 24 | 25 | //Dependency Injection 26 | @Autowired 27 | private WelcomeService service; 28 | 29 | @RequestMapping("/welcome") 30 | public String welcome(){ 31 | return service.retrieveWelcomeMessage(); 32 | } 33 | 34 | } 35 | ``` 36 | 37 | ## com/mayora/controller/SurveyController.java 38 | ``` java 39 | package com.mayora.controller; 40 | 41 | import java.net.URI; 42 | import java.util.List; 43 | 44 | import org.springframework.beans.factory.annotation.Autowired; 45 | import org.springframework.http.ResponseEntity; 46 | import org.springframework.web.bind.annotation.GetMapping; 47 | import org.springframework.web.bind.annotation.PathVariable; 48 | import org.springframework.web.bind.annotation.PostMapping; 49 | import org.springframework.web.bind.annotation.RequestBody; 50 | import org.springframework.web.bind.annotation.RequestMapping; 51 | import org.springframework.web.bind.annotation.RequestMethod; 52 | import org.springframework.web.bind.annotation.RequestParam; 53 | import org.springframework.web.bind.annotation.RestController; 54 | import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 55 | 56 | import com.mayora.model.Question; 57 | import com.mayora.service.SurveyService; 58 | 59 | @RestController 60 | public class SurveyController { 61 | 62 | @Autowired 63 | private SurveyService surveyService; 64 | 65 | @GetMapping("surveys/{surveyId}/questions") 66 | public List retrieveQuestionsForSurvey(@PathVariable String surveyId){ 67 | return surveyService.retrieveQuestions(surveyId); 68 | } 69 | 70 | @GetMapping("surveys/{surveyId}/questions/{questionId}") 71 | public Question retrieveQuestionDetails(@PathVariable String surveyId, @PathVariable String questionId){ 72 | return surveyService.retrieveQuestion(surveyId, questionId); 73 | } 74 | 75 | //@GetMapping("surveys/{surveyId}/question") 76 | @RequestMapping(path="surveys/{surveyId}/question", method=RequestMethod.GET) 77 | public Question retrieveQuestion(@PathVariable String surveyId, @RequestParam String id){ 78 | return surveyService.retrieveQuestion(surveyId, id); 79 | } 80 | 81 | @PostMapping("surveys/{surveyId}/questions") 82 | public ResponseEntity addQuestionToSurvey(@PathVariable String surveyId, @RequestBody Question newQuestion){ 83 | Question question = surveyService.addQuestion(surveyId, newQuestion); 84 | 85 | if(question == null) 86 | return ResponseEntity.noContent().build(); 87 | 88 | URI location = ServletUriComponentsBuilder.fromCurrentRequest() 89 | .path("/{id}").buildAndExpand(question.getId()).toUri(); 90 | 91 | return ResponseEntity.created(location).build(); 92 | } 93 | 94 | } 95 | ``` 96 | 97 | ## com/mayora/model/Question.java 98 | ``` java 99 | package com.mayora.model; 100 | 101 | import java.util.List; 102 | 103 | public class Question { 104 | private String id; 105 | private String description; 106 | private String correctAnswer; 107 | private List options; 108 | 109 | public Question() { 110 | // TODO Auto-generated constructor stub 111 | } 112 | 113 | public Question(String id, String description, String correctAnswer, List options) { 114 | super(); 115 | this.id = id; 116 | this.description = description; 117 | this.correctAnswer = correctAnswer; 118 | this.options = options; 119 | } 120 | 121 | public String getId() { 122 | return id; 123 | } 124 | 125 | public void setId(String id) { 126 | this.id = id; 127 | } 128 | 129 | public String getDescription() { 130 | return description; 131 | } 132 | 133 | public void setDescription(String description) { 134 | this.description = description; 135 | } 136 | 137 | public String getCorrectAnswer() { 138 | return correctAnswer; 139 | } 140 | 141 | public void setCorrectAnswer(String correctAnswer) { 142 | this.correctAnswer = correctAnswer; 143 | } 144 | 145 | public List getOptions() { 146 | return options; 147 | } 148 | 149 | public void setOptions(List options) { 150 | this.options = options; 151 | } 152 | 153 | } 154 | ``` 155 | 156 | ## com/mayora/model/Survey.java 157 | ``` java 158 | package com.mayora.model; 159 | 160 | import java.util.List; 161 | 162 | public class Survey { 163 | private String id; 164 | private String title; 165 | private String description; 166 | private List questions; 167 | 168 | public Survey(String id, String title, String description, List questions) { 169 | super(); 170 | this.id = id; 171 | this.title = title; 172 | this.description = description; 173 | this.questions = questions; 174 | } 175 | 176 | public String getId() { 177 | return id; 178 | } 179 | 180 | public void setId(String id) { 181 | this.id = id; 182 | } 183 | 184 | public String getTitle() { 185 | return title; 186 | } 187 | 188 | public void setTitle(String title) { 189 | this.title = title; 190 | } 191 | 192 | public String getDescription() { 193 | return description; 194 | } 195 | 196 | public void setDescription(String description) { 197 | this.description = description; 198 | } 199 | 200 | public List getQuestions() { 201 | return questions; 202 | } 203 | 204 | public void setQuestions(List questions) { 205 | this.questions = questions; 206 | } 207 | 208 | } 209 | ``` 210 | 211 | ## com/mayora/service/WelcomeService.java 212 | ``` java 213 | package com.mayora.service; 214 | 215 | import org.springframework.stereotype.Component; 216 | 217 | //Spring will manage this bean and create an instance of this bean 218 | @Component 219 | public class WelcomeService { 220 | public String retrieveWelcomeMessage(){ 221 | return "Selamat Datang"; 222 | } 223 | } 224 | ``` 225 | 226 | ## com/mayora/service/SurveyService.java 227 | ``` java 228 | package com.mayora.service; 229 | 230 | import java.math.BigInteger; 231 | import java.security.SecureRandom; 232 | import java.util.ArrayList; 233 | import java.util.Arrays; 234 | import java.util.List; 235 | 236 | import org.springframework.stereotype.Component; 237 | 238 | import com.mayora.model.Question; 239 | import com.mayora.model.Survey; 240 | 241 | @Component 242 | public class SurveyService { 243 | private static List surveys = new ArrayList<>(); 244 | static{ 245 | Question question1 = new Question("Question1", "Who is responsible for developing the application?", "Development", Arrays.asList("Development", "Application Support", "Master Data Management", "Demmand Planner", "Technical Business Requirement")); 246 | Question question2 = new Question("Question2", "Who is responsible for supporting distributor in the area?", "Application Support", Arrays.asList("Development", "Application Support", "Master Data Management", "Demmand Planner", "Technical Business Requirement")); 247 | Question question3 = new Question("Question3", "Who is responsible for managing master data such as discount and price?", "Master Data Management", Arrays.asList("Development", "Application Support", "Master Data Management", "Demmand Planner", "Technical Business Requirement")); 248 | Question question4 = new Question("Question4", "Who is responsible for forecasting demand of market?", "Demmand Planner", Arrays.asList("Development", "Application Support", "Master Data Management", "Demmand Planner", "Technical Business Requirement")); 249 | Question question5 = new Question("Question5", "Who is responsible for testing and creating requirement of new application development?", "Technical Business Requirement", Arrays.asList("Development", "Application Support", "Master Data Management", "Demmand Planner", "Technical Business Requirement")); 250 | 251 | List questions = new ArrayList<>(Arrays.asList(question1, question2, question3, question4, question5)); 252 | 253 | Survey survey = new Survey("Survey1", "System Support Job Description", "Questionary about job description of each department in System Support Division", questions); 254 | surveys.add(survey); 255 | } 256 | 257 | public List retrieveAllSurveys(){ 258 | return surveys; 259 | } 260 | 261 | public Survey retrieveSurvey(String surveyId){ 262 | for(Survey survey: surveys){ 263 | if(survey.getId().equals(surveyId)){ 264 | return survey; 265 | } 266 | } 267 | return null; 268 | } 269 | 270 | public List retrieveQuestions(String surveyId){ 271 | Survey survey = retrieveSurvey(surveyId); 272 | 273 | if(survey == null){ 274 | return null; 275 | } 276 | 277 | return survey.getQuestions(); 278 | } 279 | 280 | public Question retrieveQuestion(String surveyId, String questionId){ 281 | Survey survey = retrieveSurvey(surveyId); 282 | 283 | if(survey == null){ 284 | return null; 285 | } 286 | 287 | for(Question question: survey.getQuestions()){ 288 | if(question.getId().equals(questionId)){ 289 | return question; 290 | } 291 | } 292 | 293 | return null; 294 | } 295 | 296 | private SecureRandom random = new SecureRandom(); 297 | 298 | public Question addQuestion(String surveyId, Question question){ 299 | Survey survey = retrieveSurvey(surveyId); 300 | 301 | if(survey == null){ 302 | return null; 303 | } 304 | 305 | String randomId = new BigInteger(130, random).toString(32); 306 | question.setId(randomId); 307 | 308 | survey.getQuestions().add(question); 309 | 310 | return question; 311 | } 312 | 313 | 314 | } 315 | ``` 316 | 317 | ## com/mayora/springboot/Application.java 318 | ``` java 319 | package com.mayora.springboot; 320 | 321 | import org.springframework.boot.SpringApplication; 322 | import org.springframework.boot.autoconfigure.SpringBootApplication; 323 | import org.springframework.context.annotation.ComponentScan; 324 | 325 | @SpringBootApplication 326 | @ComponentScan("com.mayora") 327 | public class Application { 328 | 329 | public static void main(String[] args){ 330 | SpringApplication.run(Application.class, args); 331 | } 332 | 333 | } 334 | ``` 335 | 336 | ## pom.xml 337 | ``` xml 338 | 339 | 4.0.0 340 | com.mayora.springboot 341 | sample-project 342 | 0.0.1-SNAPSHOT 343 | 344 | 345 | 1.8 346 | 347 | 348 | 349 | org.springframework.boot 350 | spring-boot-starter-parent 351 | 1.5.3.RELEASE 352 | 353 | 354 | 355 | 356 | org.springframework.boot 357 | spring-boot-starter-web 358 | 359 | 360 | 361 | com.fasterxml.jackson.dataformat 362 | jackson-dataformat-xml 363 | 364 | 365 | 366 | org.springframework.boot 367 | spring-boot-starter-actuator 368 | 369 | 370 | 371 | org.springframework.data 372 | spring-data-rest-hal-browser 373 | 374 | 375 | 376 | org.springframework.boot 377 | spring-boot-devtools 378 | true 379 | 380 | 381 | 382 | 383 | 384 | 385 | org.springframework.boot 386 | spring-boot-maven-plugin 387 | 388 | 389 | 390 | 391 | 392 | ``` 393 | 394 | ## Konfigurasi Eksternal application.properties 395 | Spring bisa menggunakan konfigurasi eksternal dengan menggunakan application.properties. Berikut ini contoh konfigurasi eksternal untuk mengaktifkan mode DEBUG di Spring. 396 | 397 | #### /src/main/resources/application.properties 398 | ``` java 399 | logging.level.org.springframework: DEBUG 400 | ``` 401 | 402 | ## Spring Developer Tools 403 | Pada saat proses pengembangan aplikasi, setiap kali ada perubahan kode maka aplikasi Spring Boot yang sedang berjalan harus dihentikan terlebih dahulu kemudian dijalankan kembali secara manual (restart manual). Dengan adanya Spring Developer Tools dapat membantu developer untuk mempersingkat proses restart manual tadi sehingga ketika ada perubahan kode aplikasi Spring Boot otomatis akan melakukan restart agar perubahan kode tersebut masuk ke dalam aplikasi yang sedang berjalan. Untuk itu maka diperlukan untuk menambahkan dependency Spring Developer Tools ke dalam projek Java. 404 | 405 | ``` xml 406 | 407 | org.springframework.boot 408 | spring-boot-devtools 409 | true 410 | 411 | ``` 412 | -------------------------------------------------------------------------------- /Draft New.md: -------------------------------------------------------------------------------- 1 | # RESTful Web Services 2 | 3 | ## Social Media Application 4 | User -> Post 5 | 6 | - Memperoleh semua user - GET /users 7 | - Membuat user - POST /users 8 | - Memperoleh spesifik user - Get /users/{id} -> /users/1 9 | - Hapus spesifik user - DELETE /users/{id} -> /users/1 10 | 11 | - Memperoleh semua post dari spesifik user - GET users/{id}/posts 12 | - Membuat post untuk spesifik user - POST /users/{id}/posts 13 | - Memperoleh detail dari spesifik post dari spesifik user - GET /users/{id}/posts/{post_id} 14 | 15 | ## Membuat REST service serderhana dengan mengembalikan suatu String 16 | Buat kelas HelloWorldController.java 17 | ``` java 18 | import org.springframework.web.bind.annotation.GetMapping; 19 | import org.springframework.web.bind.annotation.RestController; 20 | 21 | @RestController 22 | public class HelloWorldController { 23 | 24 | @GetMapping(path = "/hello-world") 25 | public String getHello(){ 26 | return "Hello World"; 27 | } 28 | 29 | } 30 | ``` 31 | 32 | - @RestController digunakan untuk menandakan bahwa kelas tersebut adalah suatu controller rest. 33 | - @GetMapping digunakan untuk memappingkan method dengan URI dengan menggunakan method HTTP GET sehingga pada saat user mengakses URI tersebut pada browser maka method yang diberikan anotasi @GetMapping tersebut akan dieksekusi. 34 | 35 | 36 | ## Membuat REST service sederhana dengan mengembalikan suatu Bean 37 | Buatlah kelas HelloWorldBean.java 38 | ``` java 39 | public class HelloWorldBean { 40 | 41 | private String message; 42 | 43 | public HelloWorldBean(String message) { 44 | this.message = message; 45 | } 46 | 47 | public String getMessage() { 48 | return message; 49 | } 50 | 51 | public void setMessage(String message) { 52 | this.message = message; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "HelloWorldBean [message=" + message + "]"; 58 | } 59 | 60 | } 61 | ``` 62 | 63 | Pada kelas HelloWorldController.java, tambahkan method getHelloBean() 64 | 65 | ``` java 66 | import org.springframework.web.bind.annotation.GetMapping; 67 | import org.springframework.web.bind.annotation.RestController; 68 | 69 | @RestController 70 | public class HelloWorldController { 71 | 72 | @GetMapping(path = "/hello-world") 73 | public String getHello(){ 74 | return "Hello World"; 75 | } 76 | 77 | @GetMapping(path = "/hello-world-bean") 78 | public HelloWorldBean getHelloBean(){ 79 | return new HelloWorldBean("Hello World"); 80 | } 81 | 82 | } 83 | ``` 84 | 85 | ## Membuat REST service sederhana dengan menggunakan path variable 86 | Pada kelas HelloWorldController.java, tambahkan method getHelloPathVariable() 87 | 88 | ``` java 89 | import org.springframework.web.bind.annotation.GetMapping; 90 | import org.springframework.web.bind.annotation.RestController; 91 | 92 | @RestController 93 | public class HelloWorldController { 94 | 95 | @GetMapping(path = "/hello-world") 96 | public String getHello(){ 97 | return "Hello World"; 98 | } 99 | 100 | @GetMapping(path = "/hello-world-bean") 101 | public HelloWorldBean getHelloBean(){ 102 | return new HelloWorldBean("Hello World"); 103 | } 104 | 105 | @GetMapping(path = "/hello-world/{name}") 106 | public HelloWorldBean getHelloPathVariable(@PathVariable String name){ 107 | return new HelloWorldBean(String.format("Hello World, %s", name)); 108 | } 109 | 110 | } 111 | ``` 112 | 113 | ## Membuat User Bean dan User Service 114 | Buat kelas User.java 115 | ``` java 116 | import java.util.Date; 117 | 118 | public class User { 119 | private Integer id; 120 | private String name; 121 | private Date birthDate; 122 | 123 | public User() { 124 | 125 | } 126 | 127 | public User(Integer id, String name, Date birthDate) { 128 | this.id = id; 129 | this.name = name; 130 | this.birthDate = birthDate; 131 | } 132 | 133 | public Integer getId() { 134 | return id; 135 | } 136 | 137 | public void setId(Integer id) { 138 | this.id = id; 139 | } 140 | 141 | public String getName() { 142 | return name; 143 | } 144 | 145 | public void setName(String name) { 146 | this.name = name; 147 | } 148 | 149 | public Date getBirthDate() { 150 | return birthDate; 151 | } 152 | 153 | public void setBirthDate(Date birthDate) { 154 | this.birthDate = birthDate; 155 | } 156 | 157 | @Override 158 | public String toString() { 159 | return "User [id=" + id + ", name=" + name + ", birthDate=" + birthDate + "]"; 160 | } 161 | 162 | } 163 | ``` 164 | 165 | Buat kelas UserDaoService.java 166 | ``` java 167 | import java.util.ArrayList; 168 | import java.util.Date; 169 | import java.util.List; 170 | 171 | import org.springframework.stereotype.Component; 172 | 173 | @Component 174 | public class UserDaoService { 175 | private static List users = new ArrayList<>(); 176 | 177 | private static int usersCount = 3; 178 | 179 | static{ 180 | users.add(new User(1, "Tedy Darmawan", new Date())); 181 | users.add(new User(2, "Tri Apriyono", new Date())); 182 | users.add(new User(3, "Denny Krisbiantoro", new Date())); 183 | } 184 | 185 | public List findAll(){ 186 | return users; 187 | } 188 | 189 | public User save(User user){ 190 | if(user.getId() == null){ 191 | user.setId(++usersCount); 192 | } 193 | users.add(user); 194 | return user; 195 | } 196 | 197 | public User findOne(int id){ 198 | for(User user: users){ 199 | if(user.getId() == id){ 200 | return user; 201 | } 202 | } 203 | return null; 204 | } 205 | 206 | } 207 | ``` 208 | ## Implementasi method GET untuk memperoleh data User 209 | Buat kelas UserController.java 210 | ``` java 211 | import java.util.List; 212 | 213 | import org.springframework.beans.factory.annotation.Autowired; 214 | import org.springframework.web.bind.annotation.GetMapping; 215 | import org.springframework.web.bind.annotation.PathVariable; 216 | import org.springframework.web.bind.annotation.RestController; 217 | 218 | @RestController 219 | public class UserController { 220 | 221 | @Autowired 222 | private UserDaoService service; 223 | 224 | @GetMapping(path = "/users") 225 | public List getAllUsers(){ 226 | return service.findAll(); 227 | } 228 | 229 | @GetMapping(path = "/users/{id}") 230 | public User getUser(@PathVariable int id){ 231 | return service.findOne(id); 232 | } 233 | 234 | } 235 | ``` 236 | 237 | Untuk mengubah output format tanggal JSON, pada application.properties tambahkan properti berikut ini: 238 | ``` properties 239 | spring.jackson.serialization.write-dates-as-timestamps=false 240 | ``` 241 | 242 | //inspect Google Chrome untuk melihat Header dan Response 243 | 244 | ## Implementasi method POST untuk membuat data User 245 | Pada kelas UserController.java, tambahkan method POST createUser() 246 | ``` java 247 | import java.util.List; 248 | 249 | import org.springframework.beans.factory.annotation.Autowired; 250 | import org.springframework.web.bind.annotation.GetMapping; 251 | import org.springframework.web.bind.annotation.PathVariable; 252 | import org.springframework.web.bind.annotation.PostMapping; 253 | import org.springframework.web.bind.annotation.RequestBody; 254 | import org.springframework.web.bind.annotation.RestController; 255 | 256 | @RestController 257 | public class UserController { 258 | 259 | @Autowired 260 | private UserDaoService service; 261 | 262 | @GetMapping(path = "/users") 263 | public List getAllUsers(){ 264 | return service.findAll(); 265 | } 266 | 267 | @GetMapping(path = "/users/{id}") 268 | public User getUser(@PathVariable int id){ 269 | return service.findOne(id); 270 | } 271 | 272 | @PostMapping("/users") 273 | public void createUser(@RequestBody User user){ 274 | User savedUser = service.save(user); 275 | } 276 | 277 | } 278 | ``` 279 | 280 | Untuk menguji web service dengan method HTTP POST maka perlu menggunakan tools yakni Postman, yang dapat didownload dari http://www.getpostman.com. Postman merupakan rest client yang dapat digunakan untuk menguji aplikasi web service. 281 | 282 | Pada Postman, 283 | - Pilih Method HTTP POST 284 | - Masukan URL http://localhost:8080/users 285 | - Masukan Request Body, pada Tab Body, pilih raw dan JSON (application/json) 286 | ``` json 287 | { 288 | "name": "New User", 289 | "birthDate": "2017-08-04T08:10:35.979+0000" 290 | } 291 | ``` 292 | - Klik Send 293 | 294 | ## Implementasi method POST untuk membuat data User dan mengganti kode status HTTP 295 | Pada kelas UserController.java, ubah body method createUser() 296 | ``` java 297 | import java.net.URI; 298 | import java.util.List; 299 | 300 | import org.springframework.beans.factory.annotation.Autowired; 301 | import org.springframework.http.ResponseEntity; 302 | import org.springframework.web.bind.annotation.GetMapping; 303 | import org.springframework.web.bind.annotation.PathVariable; 304 | import org.springframework.web.bind.annotation.PostMapping; 305 | import org.springframework.web.bind.annotation.RequestBody; 306 | import org.springframework.web.bind.annotation.RestController; 307 | import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 308 | 309 | @RestController 310 | public class UserController { 311 | 312 | @Autowired 313 | private UserDaoService service; 314 | 315 | @GetMapping(path = "/users") 316 | public List getAllUsers(){ 317 | return service.findAll(); 318 | } 319 | 320 | @GetMapping(path = "/users/{id}") 321 | public User getUser(@PathVariable int id){ 322 | return service.findOne(id); 323 | } 324 | 325 | @PostMapping("/users") 326 | public ResponseEntity createUser(@RequestBody User user){ 327 | User savedUser = service.save(user); 328 | 329 | URI location = ServletUriComponentsBuilder.fromCurrentRequest() 330 | .path("/{id}").buildAndExpand(savedUser.getId()).toUri(); 331 | 332 | return ResponseEntity.created(location).build(); 333 | } 334 | 335 | } 336 | ``` 337 | 338 | ## Implementasi Exception Handling jika resource tidak ditemukan 339 | Buat kelas UserNotFoundException.java 340 | ``` java 341 | import org.springframework.http.HttpStatus; 342 | import org.springframework.web.bind.annotation.ResponseStatus; 343 | 344 | @ResponseStatus(HttpStatus.NOT_FOUND) 345 | public class UserNotFoundException extends RuntimeException { 346 | 347 | public UserNotFoundException(String message) { 348 | super(message); 349 | } 350 | 351 | } 352 | ``` 353 | 354 | Pada kelas UserController.java, ubah body method getUser() 355 | ``` java 356 | import java.net.URI; 357 | import java.util.List; 358 | 359 | import org.springframework.beans.factory.annotation.Autowired; 360 | import org.springframework.http.ResponseEntity; 361 | import org.springframework.web.bind.annotation.GetMapping; 362 | import org.springframework.web.bind.annotation.PathVariable; 363 | import org.springframework.web.bind.annotation.PostMapping; 364 | import org.springframework.web.bind.annotation.RequestBody; 365 | import org.springframework.web.bind.annotation.RestController; 366 | import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 367 | 368 | @RestController 369 | public class UserController { 370 | 371 | @Autowired 372 | private UserDaoService service; 373 | 374 | @GetMapping(path = "/users") 375 | public List getAllUsers(){ 376 | return service.findAll(); 377 | } 378 | 379 | @GetMapping(path = "/users/{id}") 380 | public User getUser(@PathVariable int id){ 381 | User user = service.findOne(id); 382 | 383 | if(user == null) 384 | throw new UserNotFoundException("id-" + id); 385 | 386 | return user; 387 | } 388 | 389 | @PostMapping("/users") 390 | public ResponseEntity createUser(@RequestBody User user){ 391 | User savedUser = service.save(user); 392 | 393 | URI location = ServletUriComponentsBuilder.fromCurrentRequest() 394 | .path("/{id}").buildAndExpand(savedUser.getId()).toUri(); 395 | 396 | return ResponseEntity.created(location).build(); 397 | } 398 | 399 | } 400 | ``` 401 | 402 | ## Implementasi Generic Exception Handling untuk Semua Resource 403 | Buat kelas ExceptionResponse.java 404 | ``` java 405 | import java.util.Date; 406 | 407 | public class ExceptionResponse{ 408 | private Date timestamp; 409 | private String message; 410 | private String details; 411 | 412 | public ExceptionResponse(Date timestamp, String message, String details) { 413 | super(); 414 | this.timestamp = timestamp; 415 | this.message = message; 416 | this.details = details; 417 | } 418 | 419 | public Date getTimestamp() { 420 | return timestamp; 421 | } 422 | 423 | public void setTimestamp(Date timestamp) { 424 | this.timestamp = timestamp; 425 | } 426 | 427 | public String getMessage() { 428 | return message; 429 | } 430 | 431 | public void setMessage(String message) { 432 | this.message = message; 433 | } 434 | 435 | public String getDetails() { 436 | return details; 437 | } 438 | 439 | public void setDetails(String details) { 440 | this.details = details; 441 | } 442 | 443 | } 444 | ``` 445 | 446 | Buat Kelas CustomizedResponseEntityExceptionHandler.java 447 | ``` java 448 | import java.util.Date; 449 | 450 | import org.springframework.http.HttpStatus; 451 | import org.springframework.http.ResponseEntity; 452 | import org.springframework.web.bind.annotation.ControllerAdvice; 453 | import org.springframework.web.bind.annotation.ExceptionHandler; 454 | import org.springframework.web.bind.annotation.RestController; 455 | import org.springframework.web.context.request.WebRequest; 456 | import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; 457 | 458 | @ControllerAdvice 459 | @RestController 460 | public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler{ 461 | 462 | @ExceptionHandler(Exception.class) 463 | public final ResponseEntity handleAllExceptions(Exception ex, WebRequest request){ 464 | ExceptionResponse exceptionResponse = 465 | new ExceptionResponse(new Date(), ex.getMessage(), 466 | request.getDescription(false)); 467 | return new ResponseEntity(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR); 468 | } 469 | 470 | @ExceptionHandler(UserNotFoundException.class) 471 | public final ResponseEntity handleUserNotFoundExceptions(UserNotFoundException ex, WebRequest request){ 472 | ExceptionResponse exceptionResponse = 473 | new ExceptionResponse(new Date(), ex.getMessage(), 474 | request.getDescription(false)); 475 | return new ResponseEntity(exceptionResponse, HttpStatus.NOT_FOUND); 476 | } 477 | 478 | } 479 | ``` 480 | 481 | ## Implementasi Method Delete untuk menghapus data user 482 | Pada kelas UserDaoService.java, tambahkan method deleteById() 483 | ``` java 484 | import java.util.ArrayList; 485 | import java.util.Date; 486 | import java.util.Iterator; 487 | import java.util.List; 488 | 489 | import org.springframework.stereotype.Component; 490 | 491 | @Component 492 | public class UserDaoService { 493 | private static List users = new ArrayList<>(); 494 | 495 | private static int usersCount = 3; 496 | 497 | static{ 498 | users.add(new User(1, "Tedy Darmawan", new Date())); 499 | users.add(new User(2, "Tri Apriyono", new Date())); 500 | users.add(new User(3, "Denny Krisbiantoro", new Date())); 501 | } 502 | 503 | public List findAll(){ 504 | return users; 505 | } 506 | 507 | public User save(User user){ 508 | if(user.getId() == null){ 509 | user.setId(++usersCount); 510 | } 511 | users.add(user); 512 | return user; 513 | } 514 | 515 | public User findOne(int id){ 516 | for(User user: users){ 517 | if(user.getId() == id){ 518 | return user; 519 | } 520 | } 521 | return null; 522 | } 523 | 524 | public User deleteById(int id){ 525 | Iterator iterator = users.iterator(); 526 | while(iterator.hasNext()){ 527 | User user = iterator.next(); 528 | if(user.getId() == id){ 529 | iterator.remove(); 530 | return user; 531 | } 532 | } 533 | return null; 534 | } 535 | 536 | 537 | } 538 | ``` 539 | 540 | Pada kelas UserController.java, tambahkan method DELETE deleteUser() 541 | ``` java 542 | import java.net.URI; 543 | import java.util.List; 544 | 545 | import org.springframework.beans.factory.annotation.Autowired; 546 | import org.springframework.http.ResponseEntity; 547 | import org.springframework.web.bind.annotation.DeleteMapping; 548 | import org.springframework.web.bind.annotation.GetMapping; 549 | import org.springframework.web.bind.annotation.PathVariable; 550 | import org.springframework.web.bind.annotation.PostMapping; 551 | import org.springframework.web.bind.annotation.RequestBody; 552 | import org.springframework.web.bind.annotation.RestController; 553 | import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 554 | 555 | @RestController 556 | public class UserController { 557 | 558 | @Autowired 559 | private UserDaoService service; 560 | 561 | @GetMapping(path = "/users") 562 | public List getAllUsers(){ 563 | return service.findAll(); 564 | } 565 | 566 | @GetMapping(path = "/users/{id}") 567 | public User getUser(@PathVariable int id){ 568 | User user = service.findOne(id); 569 | 570 | if(user == null) 571 | throw new UserNotFoundException("id-" + id); 572 | 573 | return user; 574 | } 575 | 576 | @PostMapping("/users") 577 | public ResponseEntity createUser(@RequestBody User user){ 578 | User savedUser = service.save(user); 579 | 580 | URI location = ServletUriComponentsBuilder.fromCurrentRequest() 581 | .path("/{id}").buildAndExpand(savedUser.getId()).toUri(); 582 | 583 | return ResponseEntity.created(location).build(); 584 | } 585 | 586 | @DeleteMapping(path = "/users/{id}") 587 | public void deleteUser(@PathVariable int id){ 588 | User user = service.deleteById(id); 589 | 590 | if(user == null) 591 | throw new UserNotFoundException("id-" + id); 592 | } 593 | 594 | } 595 | ``` 596 | 597 | ## Implementasi Validasi untuk RESTful Service 598 | Pada kelas UserController.java, tambahkan anotasi @Valid pada parameter method createUser() 599 | ``` java 600 | import java.net.URI; 601 | import java.util.List; 602 | 603 | import javax.validation.Valid; 604 | 605 | import org.springframework.beans.factory.annotation.Autowired; 606 | import org.springframework.http.ResponseEntity; 607 | import org.springframework.web.bind.annotation.DeleteMapping; 608 | import org.springframework.web.bind.annotation.GetMapping; 609 | import org.springframework.web.bind.annotation.PathVariable; 610 | import org.springframework.web.bind.annotation.PostMapping; 611 | import org.springframework.web.bind.annotation.RequestBody; 612 | import org.springframework.web.bind.annotation.RestController; 613 | import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 614 | 615 | @RestController 616 | public class UserController { 617 | 618 | @Autowired 619 | private UserDaoService service; 620 | 621 | @GetMapping(path = "/users") 622 | public List getAllUsers(){ 623 | return service.findAll(); 624 | } 625 | 626 | @GetMapping(path = "/users/{id}") 627 | public User getUser(@PathVariable int id){ 628 | User user = service.findOne(id); 629 | 630 | if(user == null) 631 | throw new UserNotFoundException("id-" + id); 632 | 633 | return user; 634 | } 635 | 636 | @PostMapping("/users") 637 | public ResponseEntity createUser(@Valid @RequestBody User user){ 638 | User savedUser = service.save(user); 639 | 640 | URI location = ServletUriComponentsBuilder.fromCurrentRequest() 641 | .path("/{id}").buildAndExpand(savedUser.getId()).toUri(); 642 | 643 | return ResponseEntity.created(location).build(); 644 | } 645 | 646 | @DeleteMapping(path = "/users/{id}") 647 | public void deleteUser(@PathVariable int id){ 648 | User user = service.deleteById(id); 649 | 650 | if(user == null) 651 | throw new UserNotFoundException("id-" + id); 652 | } 653 | 654 | } 655 | ``` 656 | 657 | Pada kelas User.java, tambahkan anotasi validator @Size dan @Past 658 | ``` java 659 | import java.util.Date; 660 | 661 | import javax.validation.constraints.Past; 662 | import javax.validation.constraints.Size; 663 | 664 | public class User { 665 | private Integer id; 666 | 667 | @Size(min=2, message="Name should have at least 2 characters") 668 | private String name; 669 | 670 | @Past 671 | private Date birthDate; 672 | 673 | public User() { 674 | 675 | } 676 | 677 | public User(Integer id, String name, Date birthDate) { 678 | this.id = id; 679 | this.name = name; 680 | this.birthDate = birthDate; 681 | } 682 | 683 | public Integer getId() { 684 | return id; 685 | } 686 | 687 | public void setId(Integer id) { 688 | this.id = id; 689 | } 690 | 691 | public String getName() { 692 | return name; 693 | } 694 | 695 | public void setName(String name) { 696 | this.name = name; 697 | } 698 | 699 | public Date getBirthDate() { 700 | return birthDate; 701 | } 702 | 703 | public void setBirthDate(Date birthDate) { 704 | this.birthDate = birthDate; 705 | } 706 | 707 | @Override 708 | public String toString() { 709 | return "User [id=" + id + ", name=" + name + ", birthDate=" + birthDate + "]"; 710 | } 711 | 712 | } 713 | ``` 714 | 715 | Pada kelas CustomizedResponseEntityExceptionHandler.java, override method handleMethodArgumentNotValid() 716 | ``` java 717 | import java.util.Date; 718 | 719 | import org.springframework.http.HttpHeaders; 720 | import org.springframework.http.HttpStatus; 721 | import org.springframework.http.ResponseEntity; 722 | import org.springframework.web.bind.MethodArgumentNotValidException; 723 | import org.springframework.web.bind.annotation.ControllerAdvice; 724 | import org.springframework.web.bind.annotation.ExceptionHandler; 725 | import org.springframework.web.bind.annotation.RestController; 726 | import org.springframework.web.context.request.WebRequest; 727 | import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; 728 | 729 | @ControllerAdvice 730 | @RestController 731 | public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler{ 732 | 733 | @ExceptionHandler(Exception.class) 734 | public final ResponseEntity handleAllExceptions(Exception ex, WebRequest request){ 735 | ExceptionResponse exceptionResponse = 736 | new ExceptionResponse(new Date(), ex.getMessage(), 737 | request.getDescription(false)); 738 | return new ResponseEntity(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR); 739 | } 740 | 741 | @ExceptionHandler(UserNotFoundException.class) 742 | public final ResponseEntity handleUserNotFoundExceptions(UserNotFoundException ex, WebRequest request){ 743 | ExceptionResponse exceptionResponse = 744 | new ExceptionResponse(new Date(), ex.getMessage(), 745 | request.getDescription(false)); 746 | return new ResponseEntity(exceptionResponse, HttpStatus.NOT_FOUND); 747 | } 748 | 749 | @Override 750 | protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, 751 | HttpHeaders headers, HttpStatus status, WebRequest request) { 752 | ExceptionResponse exceptionResponse = 753 | new ExceptionResponse(new Date(), "Validation failed", 754 | ex.getBindingResult().toString()); 755 | return new ResponseEntity(exceptionResponse, HttpStatus.BAD_REQUEST); 756 | } 757 | 758 | } 759 | ``` 760 | 761 | ## Implementasi HATEOS untuk RESTful Service 762 | HATEOAS(Hypermedia As The Engine Of Application State) 763 | 764 | Tambahkan dependency hateos 765 | ``` xml 766 | 767 | org.springframework.boot 768 | spring-boot-starter-hateoas 769 | 770 | ``` 771 | 772 | Pada kelas UserController.java, ubah body method getUser() 773 | ``` java 774 | import java.net.URI; 775 | import java.util.List; 776 | import java.util.ResourceBundle.Control; 777 | 778 | import javax.validation.Valid; 779 | 780 | import org.springframework.beans.factory.annotation.Autowired; 781 | import org.springframework.hateoas.Resource; 782 | import org.springframework.hateoas.mvc.ControllerLinkBuilder; 783 | 784 | import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*; 785 | import org.springframework.http.ResponseEntity; 786 | import org.springframework.web.bind.annotation.DeleteMapping; 787 | import org.springframework.web.bind.annotation.GetMapping; 788 | import org.springframework.web.bind.annotation.PathVariable; 789 | import org.springframework.web.bind.annotation.PostMapping; 790 | import org.springframework.web.bind.annotation.RequestBody; 791 | import org.springframework.web.bind.annotation.RestController; 792 | import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 793 | 794 | @RestController 795 | public class UserController { 796 | 797 | @Autowired 798 | private UserDaoService service; 799 | 800 | @GetMapping(path = "/users") 801 | public List getAllUsers(){ 802 | return service.findAll(); 803 | } 804 | 805 | @GetMapping(path = "/users/{id}") 806 | public Resource getUser(@PathVariable int id){ 807 | User user = service.findOne(id); 808 | 809 | if(user == null) 810 | throw new UserNotFoundException("id-" + id); 811 | 812 | Resource resource = new Resource(user); 813 | 814 | ControllerLinkBuilder linkTo = linkTo(methodOn(this.getClass()).getAllUsers()); 815 | resource.add(linkTo.withRel("all-users")); 816 | 817 | return resource; 818 | } 819 | 820 | @PostMapping("/users") 821 | public ResponseEntity createUser(@Valid @RequestBody User user){ 822 | User savedUser = service.save(user); 823 | 824 | URI location = ServletUriComponentsBuilder.fromCurrentRequest() 825 | .path("/{id}").buildAndExpand(savedUser.getId()).toUri(); 826 | 827 | return ResponseEntity.created(location).build(); 828 | } 829 | 830 | @DeleteMapping(path = "/users/{id}") 831 | public void deleteUser(@PathVariable int id){ 832 | User user = service.deleteById(id); 833 | 834 | if(user == null) 835 | throw new UserNotFoundException("id-" + id); 836 | } 837 | 838 | } 839 | ``` 840 | 841 | ## Internasionalisasi RESTful Service 842 | Pada folder src/main/resources buat file messages.properties 843 | ``` properties 844 | hello.greeting=Hello Bonjour 845 | ``` 846 | 847 | Pada folder src/main/resources buat file messages_fr.properties 848 | ``` properties 849 | hello.greeting=Bonjour 850 | ``` 851 | 852 | Pada kelas Application.java, tambahkan method localResolver() dan messageSource() 853 | 854 | ``` java 855 | import java.util.Locale; 856 | 857 | import org.springframework.boot.SpringApplication; 858 | import org.springframework.boot.autoconfigure.SpringBootApplication; 859 | import org.springframework.context.annotation.Bean; 860 | import org.springframework.context.support.ResourceBundleMessageSource; 861 | import org.springframework.web.servlet.LocaleResolver; 862 | import org.springframework.web.servlet.i18n.SessionLocaleResolver; 863 | 864 | 865 | @SpringBootApplication 866 | public class Application { 867 | 868 | public static void main(String[] args){ 869 | SpringApplication.run(Application.class, args); 870 | } 871 | 872 | @Bean 873 | public LocaleResolver localeResolver(){ 874 | SessionLocaleResolver localeResolver = new SessionLocaleResolver(); 875 | localeResolver.setDefaultLocale(Locale.US); 876 | return localeResolver; 877 | } 878 | 879 | @Bean 880 | public ResourceBundleMessageSource messageSource(){ 881 | ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); 882 | messageSource.setBasename("messages"); 883 | return messageSource; 884 | } 885 | 886 | } 887 | ``` 888 | 889 | Pada Kelas HelloWorldController.java tambahkan method getHelloInternationalized() 890 | ``` java 891 | import java.util.Locale; 892 | 893 | import org.springframework.beans.factory.annotation.Autowired; 894 | import org.springframework.context.MessageSource; 895 | import org.springframework.web.bind.annotation.GetMapping; 896 | import org.springframework.web.bind.annotation.PathVariable; 897 | import org.springframework.web.bind.annotation.RequestHeader; 898 | import org.springframework.web.bind.annotation.RestController; 899 | 900 | @RestController 901 | public class HelloWorldController { 902 | 903 | @Autowired 904 | private MessageSource messageSource; 905 | 906 | @GetMapping(path = "/hello-world") 907 | public String getHello(){ 908 | return "Hello World"; 909 | } 910 | 911 | @GetMapping(path = "/hello-world-bean") 912 | public HelloWorldBean getHelloBean(){ 913 | return new HelloWorldBean("Hello World"); 914 | } 915 | 916 | @GetMapping(path = "/hello-world/{name}") 917 | public HelloWorldBean getHelloPathVariable(@PathVariable String name){ 918 | return new HelloWorldBean(String.format("Hello World, %s", name)); 919 | } 920 | 921 | @GetMapping(path = "/hello-world-internationalized") 922 | public String getHelloInternationalized(@RequestHeader(name="Accept-Language", required=false) Locale locale){ 923 | return messageSource.getMessage("hello.greeting", null, locale); 924 | } 925 | } 926 | ``` 927 | 928 | ## Implementasi Support XML - Negosiasi Konten 929 | Tambahkan dependency jackson-dataformat-xml pada pom.xml 930 | ``` xml 931 | 932 | com.fasterxml.jackson.dataformat 933 | jackson-dataformat-xml 934 | 2.9.0 935 | 936 | ``` 937 | 938 | Accept = application/xml 939 | 940 | 941 | ## Best Practive - RESTful Service 942 | - Consumer First 943 | - Gunakan Request Method HTTP yang sesuai 944 | - GET 945 | - POST 946 | - PUT 947 | - DELETE 948 | - Gunakan Response Status yang sesuai 949 | - 200 Success 950 | - 404 Reource Not Found 951 | - 400 Bad Request 952 | - 201 Created 953 | - 401 Unauthorized 954 | - 500 Server Error 955 | - No Secure Info in URI 956 | - Use Plurals 957 | - Use Nouns For Resources 958 | 959 | ## Menambahkan Dokumentasi Pada API 960 | 1. Tambahkan dependency springfox-swagger2 961 | ``` xml 962 | 963 | io.springfox 964 | springfox-swagger2 965 | 2.7.0 966 | 967 | ``` 968 | 2. Tambahkan dependency springfox-swagger-ui 969 | ``` xml 970 | 971 | io.springfox 972 | springfox-swagger-ui 973 | 2.7.0 974 | 975 | ``` 976 | 3. Konfigurasi Swagger dengan membuat kelas SwaggerConfig.java 977 | ``` java 978 | import org.springframework.context.annotation.Bean; 979 | import org.springframework.context.annotation.Configuration; 980 | 981 | import springfox.documentation.spi.DocumentationType; 982 | import springfox.documentation.spring.web.plugins.Docket; 983 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 984 | 985 | @Configuration 986 | @EnableSwagger2 987 | public class SwaggerConfig { 988 | 989 | @Bean 990 | public Docket api(){ 991 | return new Docket(DocumentationType.SWAGGER_2); 992 | } 993 | } 994 | ``` 995 | 996 | 4. Akses dokumentasi API melalui link berikut ini: 997 | - http://localhost:8080/swagger-ui.html 998 | - http://localhost:8080/v2/api-docs 999 | 1000 | ## Memodifikasi Output Dokumentasi API 1001 | SwaggerConfig.java 1002 | ``` java 1003 | import java.util.ArrayList; 1004 | import java.util.Arrays; 1005 | import java.util.HashSet; 1006 | import java.util.Set; 1007 | 1008 | import org.springframework.context.annotation.Bean; 1009 | import org.springframework.context.annotation.Configuration; 1010 | 1011 | import springfox.documentation.service.ApiInfo; 1012 | import springfox.documentation.service.Contact; 1013 | import springfox.documentation.service.VendorExtension; 1014 | import springfox.documentation.spi.DocumentationType; 1015 | import springfox.documentation.spring.web.plugins.Docket; 1016 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 1017 | 1018 | @Configuration 1019 | @EnableSwagger2 1020 | public class SwaggerConfig { 1021 | 1022 | public static final Contact DEFAULT_API_CONTACT = new Contact("Tedy Darmawan", "http://tedydarmawan.com", "tedy.darmawan@gmail.com"); 1023 | 1024 | public static final ApiInfo DEFAULT_API_INFO = new ApiInfo("First Boot Api Documentation", "First Boot Api Documentation", "1.0", "urn:tos", 1025 | DEFAULT_API_CONTACT, "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList()); 1026 | 1027 | private static final Set DEFAULT_PRODUCES_AND_CONSUMES = new HashSet(Arrays.asList("application/json", "application/xml")); 1028 | 1029 | @Bean 1030 | public Docket api(){ 1031 | return new Docket(DocumentationType.SWAGGER_2) 1032 | .apiInfo(DEFAULT_API_INFO) 1033 | .produces(DEFAULT_PRODUCES_AND_CONSUMES) 1034 | .consumes(DEFAULT_PRODUCES_AND_CONSUMES); 1035 | } 1036 | 1037 | 1038 | } 1039 | ``` 1040 | 1041 | User.java 1042 | ``` java 1043 | import java.util.Date; 1044 | 1045 | import io.swagger.annotations.ApiModel; 1046 | import io.swagger.annotations.ApiModelProperty; 1047 | 1048 | @ApiModel(description="All details about the user.") 1049 | public class User { 1050 | private Integer id; 1051 | 1052 | @ApiModelProperty(notes="Name should have at least 2 characters") 1053 | private String name; 1054 | 1055 | @ApiModelProperty(notes="Birth date should be in the past") 1056 | private Date birthDate; 1057 | 1058 | public User() { 1059 | 1060 | } 1061 | 1062 | public User(Integer id, String name, Date birthDate) { 1063 | this.id = id; 1064 | this.name = name; 1065 | this.birthDate = birthDate; 1066 | } 1067 | 1068 | public Integer getId() { 1069 | return id; 1070 | } 1071 | 1072 | public void setId(Integer id) { 1073 | this.id = id; 1074 | } 1075 | 1076 | public String getName() { 1077 | return name; 1078 | } 1079 | 1080 | public void setName(String name) { 1081 | this.name = name; 1082 | } 1083 | 1084 | public Date getBirthDate() { 1085 | return birthDate; 1086 | } 1087 | 1088 | public void setBirthDate(Date birthDate) { 1089 | this.birthDate = birthDate; 1090 | } 1091 | 1092 | @Override 1093 | public String toString() { 1094 | return "User [id=" + id + ", name=" + name + ", birthDate=" + birthDate + "]"; 1095 | } 1096 | 1097 | } 1098 | ``` 1099 | 1100 | ## Monitoring API with Spring Actuator 1101 | Tambahkan dependency spring-boot-starter-actuator 1102 | 1103 | ``` xml 1104 | 1105 | org.springframework.boot 1106 | spring-boot-starter-actuator 1107 | 1108 | ``` 1109 | 1110 | Tambahkan dependency spring-data-rest-hal-browser 1111 | ``` xml 1112 | 1113 | org.springframework.data 1114 | spring-data-rest-hal-browser 1115 | 1116 | ``` 1117 | 1118 | Akses actuator < 2.0 http://localhost:8080/actuator atau >= 2.0 http://localhost:8080/application 1119 | --------------------------------------------------------------------------------