1 简介
基本理念
查询 /user/query?name=tom GET /user?name=tom GET
详情 /user/getInfo?id=1 GET /user/1 GET
创建 /user/create?name=tom POST /user POST
修改 /user/update?id=1&name=tom POST /user/1 PUT
修改 /user/delete?id=1 GET /user/1 DELETE
1.用URL描述资源
2.使用HTTP方法描述行为。使用HTTP状态码来表示不同的结果。
3.使用json交互数据
4.RESTful只是一种风格,并不是强制的标准。
基本代码
- 使用@REstController 标注此Controller 提供RestAPI
@RestController
public class UserController {
}
- @RequestParam 映射请求方法
//@RequestParam 映射请求方法
@GetMapping("/user")
public List<User> query(@RequestParam(name = "username") String userName){}
- Pageable
// 分页参数
//查询结构体
public class UserQueryCondition {
private String username;
private int age;
private int ageto;
private String xxx;}
@GetMapping("/user")
public List<User> query(UserQueryCondition condition,
@PageableDefault(page = 1,size = 17,sort = "username,asc") Pageable pageable){}
- @PathVariable 映射url片断到java 方法的参数,在url中可使用正则
@GetMapping("/user/{id:\\d+}")
public User getInfo(@PathVariable String id){ }
-
@jsonView控制json 输出内容
@JsonView 使用步骤
使用接口声明多个视图
在值对象的get 方法上指定视图
在Controller 方法上指定视图
在这里插入代码片
public class User {
// 声明接口
public interface UserSimpleView { };
public interface UserDetailView extends UserSimpleView { };
private String username;
private String password;
// get接口上指定视图
@JsonView(UserSimpleView.class)
public String getUsername() {
return username;
}
@JsonView(UserDetailView.class)
public String getPassword() {
return password;
}}
在Controller 方法上指定视图
// 分页参数
@GetMapping("/user")
// @JsonView(User.UserSimpleView.class)
public List<User> query(UserQueryCondition condition,
@PageableDefault(page = 1,size = 17,sort = "username,asc") Pageable pageable){
System.out.println( pageable.getPageNumber());
System.out.println( pageable.getPageSize());
System.out.println( pageable.getSort());
System.out.println(ReflectionToStringBuilder.toString(condition, ToStringStyle.MULTI_LINE_STYLE));
List<User> users= new ArrayList<>();
users.add(new User());
users.add(new User());
users.add(new User());
return users;
}
@JsonView(User.UserDetailView.class)
@GetMapping("/user/{id:\\d+}")
public User getInfo(@PathVariable String id){
User user =new User();
user.setUsername("tom");
return null;
}
8.简单校验
//实体
public class User {
private String id;
private String username;
@NotBlank(message = "密码不能为空")
private String password;
private Date birthday;
}
/**
* 只有 @Valid 传入字段校验不过,拦截器会直接返回
* 加入BindingResult后,会进入函数,然后写代码自己判断
* */
@PostMapping
public User create(@Valid @RequestBody User user, BindingResult error) {
// 如果有错返回true 没错返回false
if(error.hasErrors()){
error.getAllErrors().stream().forEach(e->{
FieldError fieldError=(FieldError)e;
System.out.println(fieldError.getField());
System.out.println(e.getDefaultMessage());
});
}
//其它注解
@NotNull //值不能为空
private String username1;
@Null //必须为空
private String username2;
@Pattern(regexp = "") //字符串必需匹配正则表达式
private String username3;
@Size(min=1,max = 5) //集合的元素数量必需在min和max之间
private String username4;
@Email //字符串必需是email
private String username5;
@Length(min=1,max = 8) // 检查字符串长度
private String username6;
@NotBlank //字符串必需有字符
private String username7;
@NotEmpty //字符串不为null,集合有元素
private String username8;
@Range(min = 0,max=9) //数字必需大于等于min 小于等于max
private String username10;
@URL //字符串是合法的url
private String username9;
9.复杂校验
第一步 定义注解
第二步 定义校验所用的校验类
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MyConstraintValidator.class) //校验专用,指定用哪个类校验
public @interface MyConsraint {
String message();
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
/**
* 参数有两个,一个是注解,另一个是校验的类型,写成Object就可以加到任何类型字段上面
*/
public class MyConstraintValidator implements ConstraintValidator<MyConsraint, Object> {
/**
* 这只主要是为了说明 不用在类上加任何标记。就可以直接使用 spring 注入
* */
@Autowired
private HelloService helloService;
// 初始化
@Override
public void initialize(MyConsraint constraintAnnotation) {
System.out.println("my validator init");
}
// 初始化校验逻辑
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
System.out.println(value);
return true;
}
}
- 错误处理
浏览器自定义出错页面
json 自定义格式
第一步 定义错误类型
第二步 定义错误用的controller
public class UserNotExistException extends RuntimeException {
private static final long serialVersionUID = -6112780192479692859L;
private String id;
public UserNotExistException(String id) {
super("user not exist");
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
@ControllerAdvice //这个注解的意思就是说 不接收任何请求,只处理错误信息
public class ControllerExceptionHandler {
@ExceptionHandler(UserNotExistException.class) //这个注解意思是指定错误类型
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Map<String, Object> handleUserNotExistException(UserNotExistException ex) {
Map<String, Object> result = new HashMap<>();
result.put("id", ex.getId());
result.put("message", ex.getMessage());
return result;
}
}
- 过滤器
第一步 定义过滤器
第二步 定义配置
/**
* 过滤器,这玩意是javaEE的,可以拿到原始的http请求和响应的信息,但是拿不到具体的方法
*/
public class TimeFilter implements Filter {
/* (non-Javadoc)
* @see javax.servlet.Filter#destroy()
*/
@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");
}
}
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
public FilterRegistrationBean timeFilter() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
TimeFilter timeFilter = new TimeFilter();
registrationBean.setFilter(timeFilter);
List<String> urls = new ArrayList<>();
urls.add("/*");
registrationBean.setUrlPatterns(urls);
return registrationBean;
}
}
- 拦截器
第一步 定义过滤器
第二步 定义配置
/**
* 既可以拿到原始的http请求和响应的信息,也可以拿到方法信息,但是拿不到具体参数值。
*/
public class TimeInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("preHandle");
System.out.println(((HandlerMethod)handler).getBean().getClass().getName());
System.out.println(((HandlerMethod)handler).getMethod().getName());
request.setAttribute("startTime", new Date().getTime());
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
Long start = (Long) request.getAttribute("startTime");
System.out.println("time interceptor 耗时:"+ (new Date().getTime() - start));
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("afterCompletion");
Long start = (Long) request.getAttribute("startTime");
System.out.println("time interceptor 耗时:"+ (new Date().getTime() - start));
System.out.println("ex is "+ex);
}
}
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
private TimeInterceptor timeInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(timeInterceptor);
}
}
- 切面
/**
* 可以拿到具体参数值,但是拿不到原始的http请求和响应的信息
*/
@Aspect
@Component
public class TimeAspect {
@Around("execution(* com.lgx.web.controller.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();
Object object = pjp.proceed();
System.out.println("time aspect 耗时:"+ (new Date().getTime() - start));
System.out.println("time aspect end");
return object;
}
}
12.文件上传下载
@RestController
@RequestMapping("/file")
public class FileController {
private String folder = "/Users/zhailiang/Documents/my/muke/inaction/java/workspace/github/imooc-security-demo/src/main/java/com/imooc/web/controller";
@PostMapping
public FileInfo upload(MultipartFile file) throws Exception {
System.out.println(file.getName());
System.out.println(file.getOriginalFilename());
System.out.println(file.getSize());
File localFile = new File(folder, new Date().getTime() + ".txt");
// file.getInputStream()
file.transferTo(localFile);
return new FileInfo(localFile.getAbsolutePath());
}
@GetMapping("/{id}")
public void download(@PathVariable String id, HttpServletRequest request, HttpServletResponse response) throws Exception {
try (InputStream inputStream = new FileInputStream(new File(folder, id + ".txt"));
OutputStream outputStream = response.getOutputStream();) {
response.setContentType("application/x-download");
response.addHeader("Content-Disposition", "attachment;filename=test.txt");
IOUtils.copy(inputStream, outputStream);
outputStream.flush();
}
}
}
2.多线程
2.1 Callable
@RestController
public class AsyncController {
private Logger logger= LoggerFactory.getLogger(getClass());
@RequestMapping("/order1")
public Callable<String> order1() throws Exception{
logger.info("主线程开始");
Callable<String> result = new Callable<String>() {
@Override
public String call() throws Exception {
logger.info("副线程开始");
Thread.sleep(1000);
logger.info("副线程返回");
return "success";
}
};
logger.info("主线程结束");
return result;
}
}
2.2 DeferredResult
示意图
/**
* 定义模拟消息队列
*/
@Component
public class MockQueue {
private String placeOrder;
private String completeOrder;
private Logger logger = LoggerFactory.getLogger(getClass());
public String getPlaceOrder() {
return placeOrder;
}
public void setPlaceOrder(String placeOrder) throws Exception {
new Thread(() -> {
logger.info("接到下单请求, " + placeOrder);
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
this.completeOrder = placeOrder;
logger.info("下单请求处理完毕," + placeOrder);
}).start();
}
public String getCompleteOrder() {
return completeOrder;
}
public void setCompleteOrder(String completeOrder) {
this.completeOrder = completeOrder;
}
}
/**
* 消息队列监听器
*/
@Component
public class QueueListener implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private MockQueue mockQueue;
@Autowired
private DeferredResultHolder deferredResultHolder;
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
new Thread(() -> {
while (true) {
if (StringUtils.isNotBlank(mockQueue.getCompleteOrder())) {
String orderNumber = mockQueue.getCompleteOrder();
logger.info("返回订单处理结果:"+orderNumber);
deferredResultHolder.getMap().get(orderNumber).setResult("place order success");
mockQueue.setCompleteOrder(null);
}else{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
/**
* 定义上下文
*/
@Component
public class DeferredResultHolder {
private Map<String, DeferredResult<String>> map = new HashMap<String, DeferredResult<String>>();
public Map<String, DeferredResult<String>> getMap() {
return map;
}
public void setMap(Map<String, DeferredResult<String>> map) {
this.map = map;
}
}
/**
* 控制器
* */
@RestController
public class AsyncController {
private Logger logger= LoggerFactory.getLogger(getClass());
@Autowired
private MockQueue mockQueue;
@Autowired
private DeferredResultHolder deferredResultHolder;
@RequestMapping("/order2")
public DeferredResult<String> order2() throws Exception {
logger.info("主线程开始");
String orderNumber = RandomStringUtils.randomNumeric(8);
mockQueue.setPlaceOrder(orderNumber);
DeferredResult<String> result = new DeferredResult<>();
deferredResultHolder.getMap().put(orderNumber, result);
return result;
}}
3.打印实体
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</dependency>
System.out.println(ReflectionToStringBuilder.toString(condition, ToStringStyle.MULTI_LINE_STYLE));
com.lgx.dto.UserQueryCondition@b3857e2[
username=jojo
age=18
ageto=60
xxx=yyy
]