Spring Controller

Posted by Adam on August 24, 2022
```java package poisondog.app.api; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.view.RedirectView; import poisondog.app.data.User; import poisondog.app.service.UserService; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; //查詢一個使用者 @GetMapping("/find") public Mono<User> findOne(@RequestParam Integer id) { return userService.findOne(id); } //查詢全部使用者,年紀遞增排序 @GetMapping("/asc") public Flux<User> asc() { return userService.findAll().sort((a, b) -> a.getAge() - b.getAge()); } //查詢全部使用者,年紀遞減排序 @GetMapping("/desc") public Flux<User> desc() { return userService.findAll().sort((a, b) -> b.getAge() - a.getAge()); } //查詢超過年紀的使用者 @GetMapping("/over/{age}") public Flux<User> over(@PathVariable("age") Integer age) { return userService.findAll().filter(a -> a.getAge() > age); } //查詢超過年紀且符合性別的使用者 @GetMapping("/over/{age}/{gender}") public Flux<User> over(@PathVariable("age") Integer age, @PathVariable("gender") String gender) { return userService.findAll().filter(a -> a.getAge() > age).filter(a -> StringUtils.equals(a.getGender(), gender)); } //儲存一個使用者 @PostMapping("/save") public Mono<String> save(@RequestBody User user) { Mono<User> userMono = Mono.just(user); return userService.save(userMono) .onErrorReturn("Over Limit!!"); // 當接收到錯誤例外時回傳 Over Limit!! } //刪除一個使用者 @DeleteMapping("/delete/{id}") public Mono<Void> delete(@PathVariable Integer id) { Mono<Integer> userMono = Mono.just(id); return userService.delete(userMono); } /** * https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html */ @PostMapping("/error") public Mono<String> error(@RequestBody Integer id) { // doOnNext 會將輸入 pass 給輸出,並額外進行 Consumer 的運算 // do 開頭的函式皆是相同概念。e.g. doOnError、doOnEach、doOnCancel return Mono.just(id).doOnNext(i -> { if (i > 5) { throw new IllegalArgumentException("for test"); } }).thenReturn("Yes!!") .onErrorResume(e -> Mono.error(e)); } /** * CookieValue 範例 */ @GetMapping("/cookie") public String readCookie(@CookieValue(value = "username", defaultValue = "") String username) { return "Hey! My username is " + username; } @PostMapping("/setCookie") public ResponseEntity setCookie(@RequestBody String username, HttpServletResponse response) { Cookie cookie = new Cookie("username", username); cookie.setMaxAge(60); cookie.setSecure(true); cookie.setHttpOnly(true); response.addCookie(cookie); return ResponseEntity.status(HttpStatus.OK).body("OK"); } /** * Header 範例 */ @GetMapping("/hello") public ResponseEntity hello(@RequestHeader("Authorization") String au) { return ResponseEntity.status(HttpStatus.OK).body(au); } /** * HttpSession 及 Redirect 範例 */ @GetMapping("/redirect") public RedirectView redirect(HttpSession session) { return new RedirectView("/web/code"); } } ``` ### 網頁轉導 在Spring Webmvc中,網頁轉導可以通過使用Controller中的方法來實現。以下是一個簡單的範例: ```java @Controller public class RedirectController { @RequestMapping("/redirectToPage") public String redirect() { return "redirect:/destinationPage"; } @RequestMapping("/destinationPage") public String displayPage() { return "destinationPage"; } } ``` 在這個例子中,當用戶請求訪問"/redirectToPage"時,`redirect()`方法將返回"redirect:/destinationPage",表示將用戶重定向到"/destinationPage"。最後,`displayPage()`方法將返回"destinationPage",顯示目的地頁面內容。 另外,Spring Webmvc還提供了`redirect:`和`forward:`的前綴來實現重定向和轉發,例如: - 重定向:`return "redirect:/destinationPage"` - 轉發:`return "forward:/destinationPage"` 在Spring MVC中,redirect:和forward:都是用來導向不同的頁面連結的方法。 1. redirect:是用來將請求重定向到另一個URL。當使用redirect:時,瀏覽器會收到HTTP 302或303的重定向狀態碼,然後再發送一個新的請求到指定的URL。這樣可以防止表單的重複提交,並且可以將請求轉發到不同的域或網站。 範例: ```java @RequestMapping("/redirectPage") public String redirectPage() { return "redirect:/newPage"; } ``` 2. forward:是用來在後端控制器之間轉發請求,不會改變URL。當使用forward:時,請求將被轉發到指定的URL,但瀏覽器不會收到任何重定向的指令。這樣可以實現內部的頁面跳轉,並且可以在後端控制器之間共享數據。 範例: ```java @RequestMapping("/forwardPage") public String forwardPage() { return "forward:/newPage"; } ``` 總結來說,redirect:和forward:主要的差異在於是否改變URL和瀏覽器的表現行為,使用時需要根據具體的需求來選擇。 # Context-Path 在Spring MVC中,`context-path`是一個非常重要的設置,它定義了應用的基礎路徑,所有的控制器(controller)和資源映射都會以此為基準。設置`context-path`可以是在不同的層面上,包括Web伺服器、Spring Boot應用、或是使用外部容器如Tomcat。下面將逐一說明如何在這些不同的情境中設定`context-path`。 ### 1. Spring Boot 應用 在Spring Boot中,你有幾種方式可以設定`context-path`: #### a. 在`application.properties`或`application.yml`文件中設置 對於Spring Boot 2.x 版本,你可以在`application.properties`文件中增加以下設置: ```properties server.servlet.context-path=/myapp ``` 或者如果你使用YAML格式的配置文件(`application.yml`),則可以配置如下: ```yaml server: servlet: context-path: /myapp ``` #### b. 通過命令行參數設置 啟動你的Spring Boot應用時,也可以通過命令行參數來覆蓋`application.properties`中的設置: ```sh java -jar yourapp.jar --server.servlet.context-path=/myapp ``` ### 2. 非Spring Boot 應用(使用Tomcat等外部容器) #### a. 在Web伺服器(如Tomcat)中設定 如果你的Spring MVC應用部署在一個外部的Servlet容器中,比如Tomcat,你可以在`web.xml`文件中或者通过修改容器的配置來設定context path。 ##### 使用`web.xml`配置: 在`WEB-INF/web.xml`文件中,你可以設置一個`<context-param>`參數來指定context path。 ```xml <web-app> <context-param> <param-name>contextPath</param-name> <param-value>/myapp</param-value> </context-param> </web-app> ``` 但是這种方法并不改变部署的context path,它只是在應用内部定义了一个參數。 ##### 修改容器配置: 對於Tomcat,你可以在`server.xml`文件中修改`<Context>`標籤的`path`屬性: ```xml <Context path="/myapp" docBase="path_to_your_app" ...> <!-- 其他配置 --> </Context> ``` 这种方法会直接改变应用的部署路径。 通过以上方法,你可以根据你的应用部署环境和需要选择合适的方式来设置`context-path`。希望这些例子能够清晰地解释了如何在不同环境中配置`context-path`!