以前都是基于用户的行为的url
/user/query?name=tom get
/user/getInfo?id=1 get
/user/create?name=tom post
/user/update?id=1&name=rose post
/user/delete?id=1 get
------restful api
/user?name=tom get
/user/1 get
/user post 创建
/user/1 put 修改
/user/1 delete
1)用URL描述资源
2)使用http方法描述行为。使用HTTP状态码表示不同的结果
3)使用接送交互数据
2)restful只是一种风格,并不是一种强制的标准
作用:控制输入输出后的json
在对象上添加视图:
public interface WithoutAgeView {};
public interface WithAgeView extends WithoutAgeView {};
在属性上添加jsonview注解
@JsonView(WithoutAgeView .class)
public String getUsername() {
return this.username;
}
@JsonView(WithAgeView .class)
public String getPassword() {
return this.password;
}
controller:
@RequestMapping(value = "/user",method = RequestMethod.GET)
@JsonView(User.WithoutAgeView.class)
public List QueryUsers(@RequestParam(name = "username",required = false) String name, UserCondition condition, @PageableDefault(size = 2,page = 3,sort = "age",direction = Sort.Direction.DESC)Pageable pageable){
System.out.println(pageable.getPageSize()+"..."+pageable.getPageNumber()+"..."+pageable.getSort());
System.out.println(condition);
List users = new ArrayList<>();
System.out.println(name);
users.add(new User());
users.add(new User());
users.add(new User());
return users;
}
//可以使用正则表达式 限制使用的ID的;类型
@RequestMapping(value = "/user/{id}",method = RequestMethod.GET)
@JsonView(User.WithAgeView.class)
public User QueryById(@PathVariable Integer id){
User user = new User();
user.setName("tom");
return user;
}
@JsonView 中的这个视图不仅可以用接口,也可以是一般的类,或者说只要有Class属性就能当成视图使用。
首先需要在实体类的相应字段上添加用于充当校验条件的注解,如:@Min @NotBlank
其次在controller层的方法的要校验的参数上添加@Valid注解,并且需要传入BindingResult对象,用于获取校验失败情况下的反馈信息
@PostMapping
public User CreateUser(@Valid @RequestBody User user, BindingResult errors) {
if (errors.hasErrors()){
errors.getAllErrors().stream().forEach(error-> System.out.println(error.getDefaultMessage()));
}
// if(bindingResult.hasErrors()){
// System.out.println(bindingResult.getFieldError().getDefaultMessage());
// return null;
// }
return user;
}
@NotNull ,
@Null ,
@pattern(regex=),
@Size(min=,max=) 集合元素个数,
@Email, @Length(min=,max=) ,
@NotEmpty字符串不为null,集合有元素 ,
@NotBlank 字符串必须有字符,
@Range(min=,max=) 数字大小,
@SafeHtml 字符串是安全的HTML,
@URL 合法的URL,
@CreditCardNumber(ignoreNonDigitCharacters=) 字符串必须是信用卡号
@AssertFalse 值必须为false
@AssertTrue 值必须为true
@DecimalMax(value=,inclusive=)inclusive为true表示小于等于,否则表示小于
@DecimalMin(value=,inclusive=)inclusive为true表示大于等于,否则表示大于
@Digits(integer=,fraction=)数字格式校验,integer指定整数部分的最大长度,fraction指定小数部分的最大长度。
@Future 值必须是未来的日期
@past 值必须是过去的日期
@Max(value=)小于等于 不能注解在字符串上
@Min(value=)大于等于 不能注解在字符串上
6. springboot的异常处理机制
针对同一请求URL,springboot会根据处理的请求头中是否包含text/html将请求分为浏览器的请求和APP的请求,然后分别进行处理。
@ControllerAdvice 拦截异常同一处理
@ControllerAdvice //拦截异常同一处理
public class ControllerExcetptionHandler {
// MyRuntimeException.class 自定义异常
@ExceptionHandler(MyRuntimeException.class)
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Map handlerException(MyRuntimeException ex){
Map res = new HashMap<>();
res.put("id",ex.getId());
res.put("message",ex.getMessage());
return res;
}
}
7过滤器的使用
1)自定义过滤器
import javax.servlet.Filter;
@Component //要加此注解
public class myFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("time filter init");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("time filter start");
long start = new Date().getTime();
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("time filer : "+ (new Date().getTime()-start));
System.out.println("time filter end");
}
@Override
public void destroy() {
System.out.println("time filter destory");
}
}
2)使用第三方的过滤器(没有commponent注解)
编写Java config 类
@Configuration
public class webConfig {
@Bean
public FilterRegistrationBean timeFilter(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
myFilter filter = new myFilter();
registrationBean.setFilter(filter);
List list = new ArrayList<>();
//可以指定在哪些URL上起作用
list.add("/*");
registrationBean.setUrlPatterns(list);
return registrationBean;
}
}
问题:filter不能知道这个请求是哪个控制器的哪个方法来处理的,如果需要知道可以使用interceptor。
8. 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).getBean().getClass().getName());
System.out.println(((HandlerMethod)o).getMethod().getName());
httpServletRequest.setAttribute("startTime",new Date().getTime());
return true;//决定是不是调用后面的 方法
}
//抛出异常就不会被调用
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
Long startTime = (Long)httpServletRequest.getAttribute("startTime");
System.out.println("time interceptor : "+(new Date().getTime()-startTime));
}
//必然被调用
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("afterCompletion");
Long startTime = (Long)httpServletRequest.getAttribute("startTime");
System.out.println("time interceptor : "+(new Date().getTime()-startTime));
System.out.println(e);
}
}
还需要编写config类
@Configuration
public class webConfig extends WebMvcConfigurerAdapter {
@Autowired
private TimeInterceptor timeInterceptor;
//extends WebMvcConfigurerAdapter 复写addInterceptors
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(timeInterceptor);
}
}
问题:无法获取拦截方法中参数的值。源码:dispatcher中处理的。
time filter init ----调用方法前执行
触发请求-------开始
time filter start ----filter开始
preHandle —interceptor 开始
postHandle
time interceptor : 76
afterCompletion
time interceptor : 76
null --interceptor 结束
time filer : 92
time filter end ----filter结束
切片(类)=切入点(注解)+增强(方法)
切入点:1.在哪些方法上起作用 2.什么时候起作用
增强: 起作用是执行的业务逻辑
@Aspect
@Component
public class TimeAspect {
@Around("execution(* com.bww.controller.UserController.*(..))")
public Object handleController(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("time aspect start");
Object[] args = pjp.getArgs();
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
System.out.println("arg is "+ arg);
}
long time = new Date().getTime();
Object proceed = pjp.proceed();
System.out.println("time aspect 耗时:"+(new Date().getTime()-time));
return proceed;
}
}
问题:拿不到http请求和响应的信息。
三种拦截器的处理顺序,在抛异常时,由内往外可以对异常就行拦截处理。
1)基于runable的多线程异步处理
@RequestMapping("/order")
public Callable order() throws Exception {
logger.info("主线程开始");
Callable callable = new Callable() {
//副线程
@Override
public String call() throws Exception {
logger.info("副线程开始");
Thread.sleep(1000);
logger.info("副线程结束");
return "success";
}
};
logger.info("主线程结束");
return callable;
}
此方法主要由主线程发起副线程,在有些场景下不适用。
2)DeferredResult
@RequestMapping("/order")
public DeferredResult order1() throws Exception {
logger.info("主线程 开始...");
String orderNumber = RandomStringUtils.randomNumeric(8);
mockQueue.setPlaceOrder(orderNumber);
DeferredResult deferredResult = new DeferredResult<>();
deferredResultHolder.getMap().put(orderNumber,deferredResult);
logger.info("主线程 结束...");
return 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 {
System.out.println("------------");
new Thread(()->{
logger.info("接到下单请求: "+placeOrder);
try {
Thread.sleep(1000);
} catch (InterruptedException 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 DeferredResultHolder {
private Map> map = new HashedMap();
public Map> getMap() {
return map;
}
public void setMap(Map> map) {
this.map = map;
}
}
@Component
public class QueueListener implements ApplicationListener {
@Autowired
private MockQueue mockQueue;
@Autowired
private DeferredResultHolder deferredResultHolder;
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
System.out.println("..........eeeeee....");
new Thread(()->{
while (true){
if (StringUtils.isNotBlank(mockQueue.getCompleteOrder())){
String orderNumber = mockQueue.getCompleteOrder();
logger.info("返回订单处理结果:"+orderNumber);
DeferredResult deferredResult = deferredResultHolder.getMap().get(orderNumber);
System.out.println(deferredResult);
deferredResult.setResult("success");
mockQueue.setCompleteOrder(null);
}else {
logger.info("wait....:");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
io.springfox
springfox-swagger2
2.7.0
io.springfox
springfox-swagger-ui
2.7.0
@EnableSwagger2
@Api:修饰整个类,描述Controller的作用
@ApiOperation:描述一个类的一个方法,或者说一个接口
@ApiParam:单个参数描述
@ApiModel:用对象来接收参数
@ApiProperty:用对象接收参数时,描述对象的一个字段
@ApiResponse:HTTP响应其中1个描述
@ApiResponses:HTTP响应整体描述
@ApiIgnore:使用该注解忽略这个API
@ApiError :发生错误返回的信息
@ApiImplicitParam:一个请求参数
@ApiImplicitParams:多个请求参数
WireMock是一个开源的测试工具,支持HTTP响应存根、请求验证、代理/拦截、记录和回放。最直接的用法: