一、《Spring Security开发安全的REST服务》视频笔记
1、Spring boot单元测试
eg:
@RunWith(SpringRunner.class) @SpringBootTest public class UserControllerTest { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; @Before public void setup() { mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); } @Test public void whenUploadSuccess() throws Exception { String result = mockMvc.perform(fileUpload("/file") .file(new MockMultipartFile("file", "test.txt", "multipart/form-data", "hello upload".getBytes("UTF-8")))) .andExpect(status().isOk()) .andReturn().getResponse().getContentAsString(); System.out.println(result); } @Test public void whenQuerySuccess() throws Exception { String result = mockMvc.perform( get("/user").param("username", "jojo").param("age", "18").param("ageTo", "60").param("xxx", "yyy") // .param("size", "15") // .param("page", "3") // .param("sort", "age,desc") .contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(status().isOk()).andExpect(jsonPath("$.length()").value(3)) .andReturn().getResponse().getContentAsString(); System.out.println(result); } @Test public void whenGetInfoSuccess() throws Exception { String result = mockMvc.perform(get("/user/1") .contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(status().isOk()) .andExpect(jsonPath("$.username").value("tom")) .andReturn().getResponse().getContentAsString(); System.out.println(result); } @Test public void whenGetInfoFail() throws Exception { mockMvc.perform(get("/user/a") .contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(status().is4xxClientError()); } @Test public void whenCreateSuccess() throws Exception { Date date = new Date(); System.out.println(date.getTime()); String content = "{\"username\":\"tom\",\"password\":null,\"birthday\":"+date.getTime()+"}"; String reuslt = mockMvc.perform(post("/user").contentType(MediaType.APPLICATION_JSON_UTF8) .content(content)) .andExpect(status().isOk()) .andExpect(jsonPath("$.id").value("1")) .andReturn().getResponse().getContentAsString(); System.out.println(reuslt); } @Test public void whenCreateFail() throws Exception { Date date = new Date(); System.out.println(date.getTime()); String content = "{\"username\":\"tom\",\"password\":null,\"birthday\":"+date.getTime()+"}"; String reuslt = mockMvc.perform(post("/user").contentType(MediaType.APPLICATION_JSON_UTF8) .content(content)) // .andExpect(status().isOk()) // .andExpect(jsonPath("$.id").value("1")) .andReturn().getResponse().getContentAsString(); System.out.println(reuslt); } @Test public void whenUpdateSuccess() throws Exception { Date date = new Date(LocalDateTime.now().plusYears(1).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); System.out.println(date.getTime()); String content = "{\"id\":\"1\", \"username\":\"tom\",\"password\":null,\"birthday\":"+date.getTime()+"}"; String reuslt = mockMvc.perform(put("/user/1").contentType(MediaType.APPLICATION_JSON_UTF8) .content(content)) .andExpect(status().isOk()) .andExpect(jsonPath("$.id").value("1")) .andReturn().getResponse().getContentAsString(); System.out.println(reuslt); } @Test public void whenDeleteSuccess() throws Exception { mockMvc.perform(delete("/user/1") .contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(status().isOk()); } }
2、请求url正则表达式
eg:
@PutMapping("/{id:\\d+}")
3、@JsonView注解
使用步骤:
(1)使用接口来声明多个视图
public class User { public interface UserSimpleView {}; public interface UserDetailView extends UserSimpleView {}; private String id; @MyConstraint(message = "这是一个测试") @ApiModelProperty(value = "用户名") private String username; @NotBlank(message = "密码不能为空") private String password; @Past(message = "生日必须是过去的时间") private Date birthday; @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 String getId() { return id; } public void setId(String id) { this.id = id; } @JsonView(UserSimpleView.class) public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } }
上面声明了UserSimpleView和UserDetailView两个接口。
(2)在值对象的get方法上指定视图
例如上述代码中,有两处不同:
@JsonView(UserSimpleView.class) public String getUsername() { return username; }
和
@JsonView(UserDetailView.class) public String getPassword() { return password; }
而且由于UserSimpleView和UserDetailView是继承关系,所以显示密码的地方也会显示用户名。
(3)在Controller方法上指定视图
例如:
@GetMapping @JsonView(User.UserSimpleView.class) @ApiOperation(value = "用户查询服务") public List<User> query(UserQueryCondition condition, @PageableDefault(page = 2, size = 17, sort = "username,asc") Pageable pageable) { System.out.println(ReflectionToStringBuilder.toString(condition, ToStringStyle.MULTI_LINE_STYLE)); System.out.println(pageable.getPageSize()); System.out.println(pageable.getPageNumber()); System.out.println(pageable.getSort()); List<User> users = new ArrayList<>(); users.add(new User()); users.add(new User()); users.add(new User()); return users; }
和
@GetMapping("/{id:\\d+}") @JsonView(User.UserDetailView.class) public User getInfo(@ApiParam("用户id") @PathVariable String id) { // throw new RuntimeException("user not exist"); System.out.println("进入getInfo服务"); User user = new User(); user.setUsername("tom"); return user; }
4、@RequestBody映射请求体到java方法的参数
eg:
后台接收:
public User create(@RequestBody User user)
前端发送json字符串:
@Test
public void whenCreateSuccess() throws Exception {
Date date = new Date();
System.out.println(date.getTime());
String content = "{\"username\":\"tom\",\"password\":null,\"birthday\":"+date.getTime()+"}";
String reuslt = mockMvc.perform(post("/user").contentType(MediaType.APPLICATION_JSON_UTF8)
.content(content))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value("1"))
.andReturn().getResponse().getContentAsString();
System.out.println(reuslt);
}
5、日期类型参数的处理
后台统一返回时间戳,前端根据自己的需求将时间戳转成特定格式的日期时间。
eg:
Date date = new Date();
String content = "{\"username\":\"tom\",\"password\":null,\"birthday\":"+date.getTime()+"}";
6、@Valid注解和BindingResult验证请求参数的合法性并处理校验结果
愚蠢的逐个校验(代码重构时不方便维护):
if(StringUtils.isNotBlank(pwd)){ }
可以使用@NotBlank:
public class User { public interface UserSimpleView {}; public interface UserDetailView extends UserSimpleView {}; private String id; @MyConstraint(message = "这是一个测试") @ApiModelProperty(value = "用户名") private String username; @NotBlank(message = "密码不能为空") private String password; }
但如果仅仅只加这个注解是不行的!!!还要在控制器方法中配合@Valid注解使用:
@PostMapping @ApiOperation(value = "创建用户") public User create(@Valid @RequestBody User user) { System.out.println(user.getId()); System.out.println(user.getUsername()); System.out.println(user.getPassword()); System.out.println(user.getBirthday()); user.setId("1"); return user; }
注意:如果没有加上BindingResult参数,且传进来的password为空,则该方法根本不会执行,而是直接报错!!!如果想要知道是发生了什么错误并且能进入方法进行处理,则:
@PostMapping @ApiOperation(value = "创建用户") public User create(@Valid @RequestBody User user, BindingResult errors) { if(errors.hasErrors()){ errors.getAllErrors().stream().forEach(err->System.out.println(err.getDefaultMessage())); } System.out.println(user.getId()); System.out.println(user.getUsername()); System.out.println(user.getPassword()); System.out.println(user.getBirthday()); user.setId("1"); return user; }
扩展:还有很多常用的验证注解在Hibernate Validator可以查看。
7、自定义校验注解
(1)创建注解(
注意:
@Constraint(validatedBy = MyConstraintValidator.class)
)
@Target({ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = MyConstraintValidator.class) public @interface MyConstraint { String message(); Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; }
(2)定义刚刚这个注解的校验逻辑由谁来执行(泛型参数中,第一个是注解名,第二个是注解适合用在什么类型的参数)
public class MyConstraintValidator implements ConstraintValidator<MyConstraint, Object> { @Autowired private HelloService helloService; @Override public 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; } }
(3)使用注解:
@MyConstraint(message = "这是一个测试") private String username;
8、Spring Boot中默认的错误处理机制和自定义异常处理
可以使用chrome的一个插件来模拟app的请求:Restlet Client
SpringBoot默认的错误处理机制是:检测到是浏览器发出,则返回html;是app发出,则返回json字符串。源码在BasicErrorController
二、java8新增时间api:LocalDateTime类
原文:https://www.cnblogs.com/z-y-x/p/10426974.html