Spring Security项目Spring MVC开发RES
2018-08-30 本文已影响5人
郭少华
查询请求
常用注解
- @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);
}
image.png自定义注解
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;
}
查看返回的异常信息
image.png
处理状态码错误
创建文件结构如下404错误将跳转对应页面
image.png
RESTful API的拦截
过滤器(Filter)
image.png创建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标签去除
image.png
@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)
image.png创建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文件
image.png
@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)
image.png@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的配置类
image.png
@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 用在实体对象的字段上 用于描述字段含义