导航:
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
- 手写一个过滤器: