導航:
SpringSecurity 開發安全的RESTful服務
一. 初入RESTful
1.1 本章導航
- 使用Spring MVC編寫Restful API
- 使用Spring MVC處理其他web應用常見的需求和場景
- Restful API開發常用輔助框架
1.2 使用SpringMVC 編寫RESTful API
- 使用Restful API的第一印象如圖:
- 特點如下:
- 用URL描述資源
- 使用HTTP方法描述行爲。使用HTTP狀態碼來表示不同的結果
- 使用json交互數據
- RESTful只是一種風格,並不是強制的標準。
1.3 REST成熟度模型
- 如圖:
1.4 常用註解
- 映射註解:
- @RestController:標明此Controller提供RestAPI
- @RequestMapping及其變體:映射http請求url到java方法
- @RequestParam 映射請求參數到java方法的參數
- 請求映射簡寫舉例:
- Get請求舉例:
@RequestMapping(value="/demo",method=RequestMethod.GET)
- Get請求簡寫後舉例:
@GetMapping("demo")
這裏的Get請求可以省略method,直接將@RequestMapping改爲@GetMapping,同時當括號內只有一個值的時候,value可以忽略,而"/"可以忽略,Spring會自己加上。其他的如增刪改都如此。
- Get請求舉例:
- 分頁註解:
- 分頁對象: Pageeable: 它是Spring提供的一個分頁對象,當前端需要進行分頁查詢的時候,我們可以使用此對象進行分頁;
- 分頁註解:@PageableDefault: 可以對分頁對象進行分頁 ,示例如下:
public void mthod(Model1 model1, @PageableDefault(page=2,size=17,sort="username,asc")Pageable pageable);
- 傳參註解:
- @PathVariable: 映射url片段到java方法的參數,在url聲明中使用正則表達式
- 使用@PathVariable可以指定名稱(name=“變量名”),這樣就可以將變量映射到這個參數上,如果不進行映射,則必須要保證參數名和上面的@RequestMapping()內的{變量名}與其一致。
- 映射的使用@PathVariable的前提條件是 映射的接口有一個{變量名} 進行映射,同時在方法內加上@PathVariable註解即可;示例如下:
@RequestMapping("user/{id}") public User getInfo(@PathVariable String id){ ... }
- 可以添加正則表達式,如圖:
- @JsonView控制json輸出內容
- 使用接口來聲明多個視圖
- 在值對象的get方法上指定視圖
- 在Controller方法上指定視圖
- User 實體類 -> 定義接口 和 指定視圖
public class User{ public interface UserSimpleView {}; public interface UserDetailView extends UserSimpleView {}; private String username; private String p@ssw0rd; @JsonView(UserSimpleView.class) public String getUsername(){return username}; public void setUsername(String username){this.username = username;} @JsonView(UserDetailView.class) public String getPassword(){return password} }
- 在Controller層上指定視圖:
@RequestMapping(value="/user",method= RequestMethod.GET) @JsonView(User.UserSimpleView.class) public List<User> query(UserQueryCondition condition,@PageableDefault(page=2,size = 17 ,sort ="user,desc") Pageable pageable){ System.out.println(pageable.getPageSize()); System.out.println(pageable.getPageNumber()); System.out.println(pageable.getSort()); List<User> users=new ArrayList<>(); users.add(new User()); return users; } @RequestMapping(value="/user/{id://d+}",method=RequestMethod.GET) @JsonView(User.UserDetailView.class) public User getInfo(@PathVariable String id) public User getInfo(@PathVariable String id){ User user=new User(); user.setUsername("user"); return user; }
@JsonView的作用就是在相同的對象,我們可以手動控制返回哪些字段。一個對象中的字段可以進行篩選,只返回其中的部分字段。
- @PathVariable: 映射url片段到java方法的參數,在url聲明中使用正則表達式
- @RequestBody 映射請求體到java方法的參數
當前端傳到後端是一個JSON格式的字符串的時候,後端是無法直接解析的,需要在參數前加上這個註解;
- @Valid註解和BindingResult驗證請求參數的合法性並處理校驗結果。
- 使用@Valid 的話,可以開啓校驗。如果在實體類中配置瞭如 @NotBlank等註解,就會在執行方法前先校驗參數,如果不符合要求則直接返回失敗。但是如果傳入了參數: BindingResult 對象的時候,可以將報錯信息存入此對象中,我們可以進一步處理,不必直接返回。如圖所示:
在pom.xml中加入spring-boot-maven-plugin的插件,可以打包出可以直接執行的jar包(會包含引入的依賴的jar包。)
- 使用@Valid 的話,可以開啓校驗。如果在實體類中配置瞭如 @NotBlank等註解,就會在執行方法前先校驗參數,如果不符合要求則直接返回失敗。但是如果傳入了參數: BindingResult 對象的時候,可以將報錯信息存入此對象中,我們可以進一步處理,不必直接返回。如圖所示:
- 其他常用註解:
- 常用的驗證註解
-
如圖:
這些所有的註解都有一個共有的值,叫做message,它可以自定義錯誤信息;如:
@NotBlank(message="密碼不能爲空") private String password;
-
- 自定義消息
- 自定義校驗器:
public class MyConstraintValidator implements ConstraintValidator<MyContraint,Object>{ @Autowired private HelloService helloService; @Override private void initialize(MyConstraint constraintAnnotation){ System.out.println("my validator init"); } @Override public boolean isValid(Object value,ConstraintValidatorContext context){ helloService.greeting("tom"); System.out.println(value); return true; } }
- 編寫註解類:
@Target({ElementType.METHOD,ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy= MyConstraintValidator.class) public @interface MyConstraint{ String message(); Class<?>[] groups() default{}; Class<? extends Plyload>[] payload() default{}; }
裏面的message,groups和plyload這三個是必不可少的,每個註解類都需要這個。
- 使用自定義註解:
- 自定義校驗器:
- 自定義校驗註解
- 常用的驗證註解
1.5 RESTful錯誤機制
- 本節內容:
-
Spring Boot中默認的錯誤處理機制
- SpringBoot 的一個默認機制就是會去判斷,請求是瀏覽器發送的還是手機發送的。如果是錯誤信息的話,SpringBoot能夠根據不同的設備,返回不一樣的數據格式;比如:手機返回json,瀏覽器返回html頁面等
- 在指定目錄下(error)下寫對應的頁面,當瀏覽器訪問時出現了對應的錯誤時,就會自動跳轉到對應的頁面中。如圖所示:
-
自定義異常處理
- 首先創建一個自定義異常類:(可以創建一個包Package叫做exception,將此類放到下面)
public class UserNotExistException extends RuntimeException{ private static final long serialVersionUID= -6112780192479692859L; private String id; public UserNotExistException(String id){ super("user not exit"); this.is = is; } // id 的get,set方法省略... }
- 在接口中使用自定義的異常類:
@GetMapping("/{id:\\d+}") @JsonView(User.UserDetailView.class) public User getInfo(@PathVariable String id){ throw new UserNotExistException(“user not exist”); }
- 設置統一異常處理:
@ControllerAdvice public class ControllerExceptionHandler{ @ExceptionHandler(UserNotExistException.class) @ResponseBody @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public Map<String,Object> handleUserNotExistException(UserNotExisException ex){ Map<String,Object> result=new HashMap<>(); result.put("id",ex.getId()); result.put("message",ex.getMessage()); return result; } }
- @ControllerAdvice,是Spring3.2提供的新註解,它是一個Controller增強器,可對controller中被 @RequestMapping註解的方法加一些邏輯處理。最常用的就是異常處理
- 在@ExceptionHandler中指定了異常類。這樣在拋出了此異常後就可以做統一異常處理了。
- 首先創建一個自定義異常類:(可以創建一個包Package叫做exception,將此類放到下面)
-
1.6 RESTful攔截機制
-
本節介紹內容
- 過濾器(Filter)
- 攔截器
- 切片
-
過濾器:
- 手寫一個過濾器:
@Component public class TimeFIlter implements Filter{ @Override public void destroy(){ System.out.println("time filter destroy "); } @Override public void doFilter(ServletRequest request,ServletResponse response, FilterChain chain) throws IOException,ServletException{ System.out.println("time filter start"); long start =new Date().getTime(); chain.doFilter(request,response); System.out.println("time filter:"+ (new Date().getTime()-start)); System.out.println("time filter finish"); } @Override public void init(FilterConfig arg0) throws ServletException{ System.out.println("time filter init"); } }
如destroy 是結束時進入的方法,init是服務啓動時會執行的方法,doFilter是方法被執行前被調用和執行後的方法。使用過濾器可以攔截服務請求。
- 如何把第三方沒有@Component註解的過濾器加入到過濾器鏈上去,也可以將我們自己寫的過濾器去掉@Component,然後通過下方去註冊加上去。方法如下:
- 創建一個配置類:WebConfig
@Configuration public class WebConfig{ @Bean public FilterRegistrationBean timeFilter(){ FilterRegistrationBean registrationBean =new FilterRegistrationBean(); TimeFilter timeFilter=new TimeFilter(); registrationBean.setFilter(timeFilter); List<String> urls=new ArrayList<>(); url.add("/*"); registrationBean.setUrlPatterns(urls); return registratonBean; } }
- 創建一個配置類:WebConfig
- 手寫一個過濾器: