查詢請求
常用註解
- @RestController 標明此Controller提供RestAPI
- @RequestMapping 對映http請求url到java方法
- @RequestParam 對映請求引數到java方法到引數
- @PageableDefault 指定分頁引數預設值
編寫一個簡單的UserController類
@RestController
@RequestMapping(value = "/user")
public class UserController {
@RequestMapping(method = RequestMethod.GET)
public List<User> query(@RequestParam(name = "username",required = true) String username, @PageableDefault(page = 1,size = 20,sort = "username",direction = Sort.Direction.DESC)Pageable pageable){
System.out.println(pageable.getSort());
List<User>users=new ArrayList<>();
users.add(new User("aaa","111"));
users.add(new User("bbb","222"));
users.add(new User("ddd","333"));
return users;
}
}
複製程式碼
@PageableDefault SpingData分頁引數 page當前頁數預設0開始 sizi每頁個數預設10 sort 排序
Srping boot 測試用例
在demo的pom.xml裡面引入spirngboot的測試
<!--spring測試框架-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
複製程式碼
測試/user介面
@RunWith(SpringRunner.class) //執行器
@SpringBootTest
public class UserControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void stup(){
mockMvc= MockMvcBuilders.webAppContextSetup(wac).build();
}
//測試用例
@Test
public void whenQuerSuccess() throws Exception {
String result=mockMvc.perform(MockMvcRequestBuilders.get("/user")
//傳過去的引數
.param("username","admin")
.contentType(MediaType.APPLICATION_JSON_UTF8))
//判斷請求的狀態嗎是否成功,200
.andExpect(MockMvcResultMatchers.status().isOk())
//判斷返回的集合的長度是否是3
.andExpect(MockMvcResultMatchers.jsonPath("$.length()").value(3))
//列印資訊
.andDo(MockMvcResultHandlers.print())
.andReturn().getResponse().getContentAsString();
//列印返回結果
System.out.println(result);
}
複製程式碼
使用者詳情請求
常用註解
- @PathVariable 對映url片段到java方法引數
- @JsonView 控制json輸出內容
實體物件
@NoArgsConstructor
@AllArgsConstructor
public class User {
public interface UserSimpleView{};
public interface UserDetailView extends UserSimpleView{};
private String username;
private String password;
@JsonView(UserSimpleView.class)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@JsonView(UserDetailView.class)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
複製程式碼
Controller類
@RestController
@RequestMapping(value = "/user")
public class UserController {
@RequestMapping(value = "/{id:\\d+}",method = RequestMethod.GET)
// 正規表示式 :\\d+ 表示只能輸入數字
//使用者名稱密碼都顯示
@JsonView(User.UserDetailView.class)
public User userInfo(@PathVariable String id){
User user=new User();
user.setUsername("tom");
return user;
}
}
複製程式碼
測試用例
@RunWith(SpringRunner.class) //執行器
@SpringBootTest
public class UserControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void stup(){
mockMvc= MockMvcBuilders.webAppContextSetup(wac).build();
}
//使用者詳情用例
@Test
public void whenUserInfoSuccess() throws Exception {
String result=mockMvc.perform(MockMvcRequestBuilders.get("/user/1")
.contentType(MediaType.APPLICATION_JSON_UTF8))
//判斷請求的狀態嗎是否成功,200
.andExpect(MockMvcResultMatchers.status().isOk())
//判斷返回到username是不是tom
.andExpect(MockMvcResultMatchers.jsonPath("$.username").value("tom"))
//列印資訊
.andDo(MockMvcResultHandlers.print())
.andReturn().getResponse().getContentAsString();
//列印返回結果
System.out.println(result);
}
}
複製程式碼
使用者處理建立請求
常用註解
- @RequestBody 對映請求體到java方法到引數
- @Valid註解和BindingResult驗證請求引數合法性並處理校驗結果
實體物件
@NoArgsConstructor
@AllArgsConstructor
public class User {
public interface UserSimpleView{};
public interface UserDetailView extends UserSimpleView{};
private String id;
private String username;
//不允許password為null
@NotBlank
private String password;
private Date birthday;
@JsonView(UserSimpleView.class)
public String getId() { return id; }
public void setId(String id) { this.id = id; }
@JsonView(UserSimpleView.class)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@JsonView(UserDetailView.class)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@JsonView(UserSimpleView.class)
public Date getBirthday() { return birthday; }
public void setBirthday(Date birthday) { this.birthday = birthday; }
}
複製程式碼
Controller類
@RequestMapping(method = RequestMethod.POST)
@JsonView(User.UserSimpleView.class)
//@Valid啟用校驗password不允許為空
public User createUser(@Valid @RequestBody User user, BindingResult errors){
//如果校驗有錯誤是true並列印錯誤資訊
if(errors.hasErrors()){
errors.getAllErrors().stream().forEach(error -> System.out.println(error.getDefaultMessage()));
}
System.out.println(user.getUsername());
System.out.println(user.getPassword());
System.out.println(user.getBirthday());
user.setId("1");
return user;
}
複製程式碼
測試用例
@RunWith(SpringRunner.class) //執行器
@SpringBootTest
public class UserControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void stup(){
mockMvc= MockMvcBuilders.webAppContextSetup(wac).build();
}
//使用者建立用例
@Test
public void whenCreateSuccess() throws Exception {
Date date=new Date();
String content="{\"username\":\"tom\",\"password\":null,\"birthday\":"+date.getTime()+"}";
String result=mockMvc.perform(MockMvcRequestBuilders.post("/user")
.content(content)
.contentType(MediaType.APPLICATION_JSON_UTF8))
//判斷請求的狀態嗎是否成功,200
.andExpect(MockMvcResultMatchers.status().isOk())
//判斷返回到username是不是tom
.andExpect(MockMvcResultMatchers.jsonPath("$.id").value("1"))
.andReturn().getResponse().getContentAsString();
//列印返回結果
System.out.println(result);
}
}
複製程式碼
修改和刪除請求
驗證註解
註解 | 解釋 |
---|---|
@NotNull | 值不能為空 |
@Null | 值必須為空 |
@Pattern(regex=) | 字串必須匹配正規表示式 |
@Size(min=,max=) | 集合的元素數量必須在min和max之間 |
字串必須是Email地址 | |
@Length(min=,max=) | 檢查字串長度 |
@NotBlank | 字串必須有字元 |
@NotEmpty | 字串不為null,集合有元素 |
@Range(min=,max=) | 數字必須大於等於min,小於等於max |
@SafeHtml | 字串是安全的html |
@URL | 字串是合法的URL |
@AssertFalse | 值必須是false |
@AssertTrue | 值必須是true |
@DecimalMax(value=,inclusive) | 值必須小於等於(inclusive=true)/小於(inclusive=false) value指定的值 |
@DecimalMin(value=,inclusive) | 值必須大於等於(inclusive=true)/大於(inclusive=false) value指定的值 |
@Digits(integer=,fraction=) | integer指定整數部分最大長度,fraction小數部分最大長度 |
@Future | 被註釋的元素必須是一個將來的日期 |
@Past | 被註釋的元素必須是一個過去的日期 |
@Max(value=) | 值必須小於等於value值 |
@Min(value=) | 值必須大於等於value值 |
自定義註解修改請求
實體物件
@NoArgsConstructor
@AllArgsConstructor
public class User {
public interface UserSimpleView{};
public interface UserDetailView extends UserSimpleView{};
private String id;
//自定義註解
@MyConstraint(message = "賬號必須是tom")
private String username;
//不允許password為null
@NotBlank(message = "密碼不能為空")
private String password;
//加驗證生日必須是過去的時間
@Past(message = "生日必須是過去的時間")
private Date birthday;
@JsonView(UserSimpleView.class)
public String getId() { return id; }
public void setId(String id) { this.id = id; }
@JsonView(UserSimpleView.class)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@JsonView(UserDetailView.class)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@JsonView(UserSimpleView.class)
public Date getBirthday() { return birthday; }
public void setBirthday(Date birthday) { this.birthday = birthday; }
}
複製程式碼
Controller類
@RequestMapping(value = "/{id:\\d+}",method = RequestMethod.PUT)
@JsonView(User.UserSimpleView.class)
//@Valid啟用校驗password不允許為空
public User updateUser(@Valid @RequestBody User user, BindingResult errors){
//如果校驗有錯誤是true並列印錯誤資訊
if(errors.hasErrors()){
errors.getAllErrors().stream().forEach(error -> System.out.println(error.getDefaultMessage()));
}
System.out.println(user.getUsername());
System.out.println(user.getPassword());
System.out.println(user.getBirthday());
user.setId("1");
return user;
}
複製程式碼
測試用例
@RunWith(SpringRunner.class) //執行器
@SpringBootTest
public class UserControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void stup(){
mockMvc= MockMvcBuilders.webAppContextSetup(wac).build();
}
//使用者修改用例
@Test
public void whenUpdateSuccess() throws Exception {
//當前時間加一年
Date date = new Date(LocalDateTime.now().plusYears(1).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
String content = "{\"id\":\"1\",\"username\":\"44\",\"password\":null,\"birthday\":" + date.getTime() + "}";
String result = mockMvc.perform(MockMvcRequestBuilders.put("/user/1")
.content(content)
.contentType(MediaType.APPLICATION_JSON_UTF8))
//判斷請求的狀態嗎是否成功,200
.andExpect(MockMvcResultMatchers.status().isOk())
//判斷返回到username是不是tom
.andExpect(MockMvcResultMatchers.jsonPath("$.id").value("1"))
.andReturn().getResponse().getContentAsString();
//列印返回結果
System.out.println(result);
}
複製程式碼
自定義註解
MyConstraint類
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//作用在欄位跟方法上面
@Target({ElementType.FIELD,ElementType.METHOD})
//執行時註解
@Retention(RetentionPolicy.RUNTIME)
//需要校驗註解的類
@Constraint(validatedBy = MyConstraintValidator.class)
public @interface MyConstraint {
String message() default "{org.hibernate.validator.constraints.NotBlank.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
複製程式碼
MyConstraintValidator類
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
//範型1.驗證的註解 2.驗證的資料型別
public class MyConstraintValidator implements ConstraintValidator<MyConstraint,Object> {
@Override
public void initialize(MyConstraint myConstraint) {
//校驗器初始化的規則
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext) {
//校驗username如果是tom驗證通過
if (value.equals("tom")){
return true;
}else{
return false;
}
}
}
複製程式碼
刪除請求
Controller類
@RequestMapping(value = "/{id:\\d+}",method = RequestMethod.DELETE)
//@Valid啟用校驗password不允許為空
public void deleteUser(@PathVariable String id){
System.out.println(id);
}
複製程式碼
測試用例
@RunWith(SpringRunner.class) //執行器
@SpringBootTest
public class UserControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void stup(){
mockMvc= MockMvcBuilders.webAppContextSetup(wac).build();
}
//使用者刪除用例
@Test
public void whenDeleteSuccess() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.delete("/user/1")
.contentType(MediaType.APPLICATION_JSON_UTF8))
//判斷請求的狀態嗎是否成功,200
.andExpect(MockMvcResultMatchers.status().isOk());
}
複製程式碼
服務異常處理
把BindingResult errors去掉
@RequestMapping(method = RequestMethod.POST)
@JsonView(User.UserSimpleView.class)
//@Valid啟用校驗password不允許為空
public User createUser(@Valid @RequestBody User user){
//如果校驗有錯誤是true並列印錯誤資訊
// if(errors.hasErrors()){
// errors.getAllErrors().stream().forEach(error -> System.out.println(error.getDefaultMessage()));
// }
System.out.println(user.getUsername());
System.out.println(user.getPassword());
System.out.println(user.getBirthday());
user.setId("1");
return user;
}
複製程式碼
檢視返回的異常資訊
處理狀態碼錯誤
建立檔案結構如下404錯誤將跳轉對應頁面
RESTful API的攔截
過濾器(Filter)
建立filter檔案
@Component
public class TimeFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("TimeFilter init");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("TimeFilter doFilter");
long start=new Date().getTime();
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("耗時"+(new Date().getTime()-start));
}
@Override
public void destroy() {
System.out.println("TimeFilter destroy");
}
}
複製程式碼
自定義filter
需要吧filter檔案@Component標籤去除
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean timeFilterRegistration(){
FilterRegistrationBean registration=new FilterRegistrationBean();
TimeFilter timeFilter=new TimeFilter();
registration.setFilter(timeFilter);
//filter作用的地址
List<String>urls=new ArrayList<>();
urls.add("/user");
registration.setUrlPatterns(urls);
return registration;
}
}
複製程式碼
攔截器(Interceptor)
建立Interceptor檔案
@Component
public class TimeInterceptor implements HandlerInterceptor {
//控制器方法呼叫之前
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("preHandle");
System.out.println("進入方法"+((HandlerMethod)o).getMethod().getName());
httpServletRequest.setAttribute("startTime",new Date().getTime());
//是否呼叫後面的方法呼叫是true
return true;
}
//控制器方法被呼叫
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
Long start= (Long) httpServletRequest.getAttribute("startTime");
System.out.println("time interceptor耗時"+(new Date().getTime()-start));
}
//控制器方法完成之後
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("afterCompletion");
System.out.println("exception is"+e);
}
}
複製程式碼
把過濾器新增到webconfig檔案
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
private TimeInterceptor timeInterceptor;
//過濾器
@Bean
public FilterRegistrationBean timeFilterRegistration(){
FilterRegistrationBean registration=new FilterRegistrationBean();
TimeFilter timeFilter=new TimeFilter();
registration.setFilter(timeFilter);
//filter作用的地址
List<String>urls=new ArrayList<>();
urls.add("/user/*");
registration.setUrlPatterns(urls);
return registration;
}
//攔截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(timeInterceptor);
}
}
複製程式碼
切片(Aspect)
@Aspect
@Component
public class TimeAspect {
//@Befor方法呼叫之前
//@After()方法呼叫
//@AfterThrowing方法呼叫之後
//包圍,覆蓋前面三種
@Around("execution(* com.guosh.web.controller.UserController.*(..))")//表示式表示usercontroller裡所有方法其他表示式可以查詢切片表示式
public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("time aspect start");
//可以獲取到傳入引數
Object[]args=pjp.getArgs();
for (Object arg: args) {
System.out.println("arg is"+arg);
}
long start=new Date().getTime();
//相當於filter裡doFilter方法
Object object=pjp.proceed();
System.out.println("time aspect耗時"+(new Date().getTime()-start));
System.out.println("time aspect end");
return object;
}
}
複製程式碼
總結
過濾器Filter :可以拿到原始的http請求與響應資訊 攔截器Interceptor :可以拿到原始的http請求與響應資訊還可以拿到處理請求方法的資訊 切片Aspect :可以拿到方法呼叫傳過來的值
使用rest方式處理檔案服務
返回的上傳檔案後路徑物件 在application.yml裡新增上傳地址
#上傳檔案路徑
uploadfiledir:
filePath: /Users/shaohua/webapp/guoshsecurity
複製程式碼
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FileInfo {
private String path;
}
複製程式碼
@RestController
@RequestMapping("/file")
public class FileController {
@Value("${uploadfiledir.filePath}")
private String fileDataStorePath;//檔案上傳地址
@RequestMapping(method = RequestMethod.POST)
public FileInfo upload(@RequestParam("file") MultipartFile file) throws IOException {
//檔名
System.out.println(file.getOriginalFilename());
//檔案大小
System.out.println(file.getSize());
//獲取檔案字尾名
String ext=StringUtils.getFilenameExtension(file.getOriginalFilename());
File fileDir = new File(fileDataStorePath);
//判斷是否建立目錄
if (!fileDir.exists()) {
if (!fileDir.mkdirs() || !fileDir.exists()) { // 建立目錄失敗
throw new RuntimeException("無法建立目錄!");
}
}
File localFile=new File(fileDataStorePath, UUID.randomUUID().toString().replace("-", "")+"."+ext);
file.transferTo(localFile);
//返回上傳的路徑地址
return new FileInfo(localFile.getAbsolutePath());
}
//下載檔案
@RequestMapping(value ="/{id}" ,method = RequestMethod.GET)
public void download(@PathVariable String id, HttpServletResponse response){
//模擬下載直接填好了下載檔名稱
try(InputStream inputStream = new FileInputStream(new File(fileDataStorePath,"13a2c075b7f44025bbb3c590f7f372eb.txt"));
OutputStream outputStream=response.getOutputStream();){
response.setContentType("application/x-download");
response.addHeader("Content-Disposition","attachment;filename="+"13a2c075b7f44025bbb3c590f7f372eb.txt\"");
IOUtils.copy(inputStream,outputStream);
} catch (Exception e) {
e.printStackTrace();
}
}
}
複製程式碼
使用Swagger工具
在demo模組引入
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
複製程式碼
新增swagger的配置類
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Value("${sys.swagger.enable-swgger}")
private Boolean enableSwgger;
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.enable(enableSwgger)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.guosh.web")) //swgger外掛作用範圍
//.paths(PathSelectors.regex("/api/.*"))
.paths(PathSelectors.any()) //過濾介面
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("SpringSecurityDemo") //標題
.description("API描述") //描述
.contact(new Contact("guoshaohua", "http://www.guoshaohua.cn", ""))//作者
.version("1.0")
.build();
}
}
複製程式碼
常用註解
- 通過@Api用於controller類上對類的功能進行描述
- 通過@ApiOperation註解用在controller方法上對類的方法進行描述
- 通過@ApiImplicitParams、@ApiImplicitParam註解來給引數增加說明
- 通過@ApiIgnore來忽略那些不想讓生成RESTful API文件的介面
- 通過@ApiModel 用在返回物件類上描述返回物件的意義
- 通過@ApiModelProperty 用在實體物件的欄位上 用於描述欄位含義