作為一名 Java程序員,對 Controller肯定不陌生,它是與外部客戶端通信的入口,比如常見的 REST 操作(GET、PUT、POST、DELETE等),那么,Controller里面應該如何編寫才算優雅呢?
其實,一個優雅的 Controller,里面的代碼主要包含下面 6個部分:
下面一一講解這 6個部分:
接收 HTTP(s)請求是 Controller的入口,這里以查詢用戶信息為例進行說明,如下代碼:
@RestControllerpublic class UserController { @GetMapping("/user/{userId}") public void getUserById(@PathVariable String userId) { // 業務邏輯 }}
在上面的示例中,我們使用 URL/user/{id}接收用戶發出的 GET請求,然后通過getUserById方法進行真實的業務處理。通過上面的代碼,一個請求就被 Controller層成功接收了。
接收到請求后,一般需要對請求參數進行解析,如下示例代碼:
@RestControllerpublic class UserController { @PostMapping("/user/register") public void getGradeById(@RequestBody User user) { // 代碼邏輯 }}public class User { private String nickname; private Integer age; // getters and setters and constructors}
上述示例代碼將請求的 body映射到 User對象上,因此,請求的 body體應該是:
{ "nickname": "huahua", "age": "18"}
在 SpringMVC 中,常見的參數類型及其用途如下:
直接接收原始的 HTTP請求和響應對象,HttpServletRequest 和 HttpServletResponse
@RequestMapping("/test")public void example(HttpServletRequest request, HttpServletResponse response) { // 處理請求和響應}
用于獲取 URL 路徑中的動態部分。
@RequestMapping("/user/{id}")public String getUser(@PathVariable("id") String userId) { // 使用 userId 進行處理 return "userDetail";}
用于獲取 URL 查詢參數或表單數據。
@RequestMapping("/search")public String search(@RequestParam("query") String query) { // 使用 query 進行搜索 return "searchResults";}
用于接收請求體中的數據,常用于處理 JSON 或 XML 格式的數據。
@RequestMapping(value = "/create", method = RequestMethod.POST)public String create(@RequestBody User user) { // 處理 user 對象 return "user";}
用于綁定表單數據到模型對象。
@RequestMapping("/register")public String register(@ModelAttribute User user) { // 處理 user 對象 return "user";}
用于訪問會話中的屬性。
@RequestMapping("/profile")public String profile(@SessionAttribute("user") User user) { // 處理會話中的 user 對象 return "profile";}
用于訪問 HTTP 請求頭信息。
@RequestMapping("/headers")public String headers(@RequestHeader("User-Agent") String userAgent) { // 使用 userAgent 進行處理 return "headerInfo";}
用于訪問 Cookie 的值。
@RequestMapping("/cookies")public String cookies(@CookieValue("sessionId") String sessionId) { // 使用 sessionId 進行處理 return sessionId;}
可以通過實現 HandlerMethodArgumentResolver接口來自定義參數解析邏輯。
@RequestMapping("/custom")public String custom(CustomObject customObject) {// 使用自定義對象進行處理 return "";}
請求參數的驗證需要在 Controller層完成,如下代碼,對 nickname進行判空處理,參數驗證一般有 2種方式:
// 原始方式校驗參數@RestControllerpublic class UserController { @PostMapping("/user/register") public void getGradeById(@RequestBody User user) { // 代碼邏輯 if (StringUtils.isBlank(user.getNickname)) { throw new Exception("Nickname is required."); } }}
或者使用 Spring validation驗證機制,Controller需要增加@Validated注解,User對象中增加@NotBlank注解。
// 借助Spring validation方式校驗參數@RestControllerpublic class UserController { @PostMapping("/user/register") public void getGradeById(@Validated @RequestBody User user) { // 代碼邏輯 }}public class User { @NotBlank(message = "Nickname is required.") private String nickname; private Integer age; // getters and setters and constructors}
如下代碼,調用 UserService.register()進行注冊業務處理:
@RestControllerpublic class UserController { private final UserService userService; public UserController(UserService userService) { this.userService = userService; } @PostMapping("/user/register") public void getGradeById(@Validated @RequestBody User user) { // 調用注冊的業務方法 userService.register(user); }}public class User { @NotBlank(message = "Nickname is required.") private String nickname; private Integer age; // getters and setters and constructors}
關于調用業務方法,這里的業務方法是寫一個大而全的方法?還是需要按業務歸類?
遵守一個原則:有強關聯性的邏輯放在一個service方法內,沒有強關聯性的單令拎出來。
這里以用戶注冊之后需要新人發券為例進行說明:
大而全的方法:
@PostMapping("/user/register") public void getGradeById(@Validated @RequestBody User user) { // 調用注冊的業務方法 userService.doRegister(user); } public String doRegister(Uswr user){ String userId = userService.register(user); coupon.sendCoupon(userId); // 其他業務邏輯 return userId; }
業務歸類:
@PostMapping("/user/register") public void getGradeById(@Validated @RequestBody User user) { // 調用注冊的業務方法 userService.register(user); coupon.sendCoupon(userId); }
如下代碼,調用 UserService.register()進行注冊業務處理:
@RestControllerpublic class UserController { private final UserService userService; public UserController(UserService userService) { this.userService = userService; } @PostMapping("/user/register") public UserResponse getGradeById(@Validated @RequestBody User user) { // 調用注冊的業務方法 String userId = userService.regist(user); return new UserResponse(userId, user.getNickname); }}public class UserResponse { private String userId; private String nickname; // getters and setters and constructors}
比如上述過程在 userService.regist(user);出現異常時,可以做一個try-catch,然后在 Controller層封裝有業務意思的異常信息:
@RestControllerpublic class UserController { private final UserService userService; @PostMapping("/user/register") public UserResponse getGradeById(@Validated @RequestBody User user) { // 調用注冊的業務方法 try { String userId = userService.regist(user); } catch (Exception e) { throw new CustomException(); } return new UserResponse(userId, user.getNickname); }}
看過很多代碼,業務邏輯全部寫在 Controller層,并不能說這樣的做法是錯的,但是看起來很別扭,不優雅!因此,建議在編寫代碼時,最好能遵守一個比較好的規范,比如常見的SOLID規范。
SOLID 實際上是五個設計原則首字母的縮寫,它們分別是:
另外,建議我們技術人員平時多去閱讀一些優秀開源框架,學習他們的設計思想,代碼規范,相信我:養成一個良好的編碼規范,絕對受益頗多!
本文鏈接:http://www.tebozhan.com/showinfo-26-96045-0.html如何編寫優雅的 Controller 代碼?
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com