使用springmvc开发restful api

1.增删改查

以前都是基于用户的行为的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

2.特点与区别

1)用URL描述资源
2)使用http方法描述行为。使用HTTP状态码表示不同的结果
3)使用接送交互数据
2)restful只是一种风格,并不是一种强制的标准

3.@JsonView

作用:控制输入输出后的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属性就能当成视图使用。

4. @Valid注解

首先需要在实体类的相应字段上添加用于充当校验条件的注解,如:@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;
    }

5. 校验注解 使用spring data JPA 进行数据库开发

@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结束

9. 基于切片的拦截

切片(类)=切入点(注解)+增强(方法)
切入点: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请求和响应的信息。
使用springmvc开发restful api_第1张图片
三种拦截器的处理顺序,在抛异常时,由内往外可以对异常就行拦截处理。

10.异步处理请求

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();
    }
}

11. Swagger2 自动生成文档

      
            
            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:多个请求参数

12. wireMock

WireMock是一个开源的测试工具,支持HTTP响应存根、请求验证、代理/拦截、记录和回放。最直接的用法:

  1. 为Web/移动应用构建Mock Service
  2. 快速创建Web API原型
  3. 模拟Web Service中错误返回
  4. 录制HTTP请求和回放

你可能感兴趣的:(IT技术分享,springboot)