使用springboot开发restful Api

课程目录如下:

1:restfulApi的特点
2:使用sprintboot开发resufulApi
3:使用MockMvc对Api进行单元测试
4:使用filter对api进行过滤
5:使用interceptor对Api进行拦截
6:使用spring提供的切面对api进行日志记录
7:实现Api上传及下载功能
8:使用线程异步对api性能进行提升

restfulApi的特点

  • restfulApi使用Url描述资源
  • 使用http方法描述行为,使用http状态码来表示不同的结果
  • 使用json交换数据
  • restful只是一种风格,不是强制的标准

解释如下

例如对用户信息进行增删改查操作,传统做法
增:addUser()
删:deleteUser()
改:updateUser()
查:selectUser()
这四个操作,分别对于四个入口,对用户进行操作,在restful中,Url代表资源,增删改查均为对用户进行操作,即操作的为/user这个资源,无论增删改查,其url均保持一致,问题来了,url一致,如何区分增删改查? 在restful中,使用使用http方法描述行为,即增为post请求,删为delete请求,改为put请求,查为get请求,通过http的请求方法,即可实现同一url的请求分发。

代码演示如下:

User实体类:

public class User {
   
    private String id;
    private String username;
    private String sex;
    private String password;
    private Date birthday;
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

UserController

@RestController
@RequestMapping("/user")
public class UserController {

   private final  Logger logger = LoggerFactory.getLogger(getClass());


    //删除用户
    @DeleteMapping(value = "/{id:\\d+}")
    public void delete(@PathVariable String id) {
        System.out.println(id);
    }

    /**
     * 更新用户
     */
    @PutMapping(value = "/{id:\\d+}")
    public User update(@Valid @RequestBody User user, BindingResult errors) {
        if (errors.hasErrors()) {
            errors.getAllErrors().stream().forEach(error -> System.out.println(error.getDefaultMessage()));
        }
        System.out.println(user.getUsername());
        System.out.println(user.getBirthday());
        user.setId("1");
        return user;
    }

    //创建用户
    @PostMapping
    public User creat(@Valid @RequestBody User user, BindingResult errors) {
        if (errors.hasErrors()) {
            errors.getAllErrors().stream().forEach(error -> System.out.println(error.getDefaultMessage()));
        }
        System.out.println(user.getUsername());
        System.out.println(user.getBirthday());
        user.setId("1");
        return user;
    }

    //查询用户
    @GetMapping
    public List<User> query(User user, @PageableDefault(page = 1, size = 10, sort = "username,asc") Pageable pageable) {
        logger.info(user.toString());
        logger.info(String.valueOf(pageable.getPageNumber()));
        logger.info(String.valueOf(pageable.getPageSize()));
        logger.info(String.valueOf(pageable.getSort()));
        List<User> lists = new ArrayList<>();
        lists.add(new User("手机", "男", "123456"));
        lists.add(new User("电脑", "男", "123456"));
        lists.add(new User("平板", "男", "123456"));
        return lists;
    }

在类上标注@RestController标识,声明其为restfulApi,即可返回json数据,增删改查方法对应的url一致,根据其不同类型,分发至不同的HTTP方法处理器中。

使用MockMvc 执行测试用例:

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserControllerTest {
    @Autowired
    private WebApplicationContext wac;
    private MockMvc mockMvc;

    @Before
    public void setUp() {
        mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
    }
    @Test
    public void whenGetInfoSuccess() throws Exception {
        String result = mockMvc.perform(MockMvcRequestBuilders.get("/user/1")
                .contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.username").value("tom"))
                .andReturn().getResponse().getContentAsString();
        System.out.println(result);

    }
    @Test
    public void whenCreatSuccess() throws Exception {
        Date date = new Date();
        String content = "{\"username\":null,\"sex\":\"男\",\"password\":\"1234\",\"birthday\":\"" + date.getTime() + "\"}";
        String result = mockMvc.perform(MockMvcRequestBuilders.post("/user").
                contentType(MediaType.APPLICATION_JSON_UTF8)
                .content(content))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.id").value("1"))
                .andReturn().getResponse().getContentAsString();
        System.out.println(result);
    }

    @Test
    public void whenUpdateSuccess() throws Exception {
        Date date = new Date(LocalDateTime.now().plusYears(1).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
        String content = "{\"username\":\"wumeng\",\"id\":\"2\",\"password\":\"1234\",\"birthday\":\"" + date.getTime() + "\"}";
        String result = mockMvc.perform(MockMvcRequestBuilders.put("/user/1").
                contentType(MediaType.APPLICATION_JSON_UTF8)
                .content(content))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.id").value("1"))
                .andReturn().getResponse().getContentAsString();
        System.out.println(result);
    }

    @Test
    public void whenDeleteSuccess() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.delete("/user/1")
                .contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(MockMvcResultMatchers.status().isOk());
    }
}

使用过滤器对特定请求及进行拦截,例如记录一次访问的时间:

public class TimeFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("过滤器初始化。。。。");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        long start = new Date().getTime();
        filterChain.doFilter(servletRequest, servletResponse);
        long end = new Date().getTime();
        System.out.println("访问耗时:" +(end - start));
    }

    @Override
    public void destroy() {
        System.out.println("过滤器销毁。。。");
    }
}

过滤器写好之后需要进行配置

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter{
    @Bean
    public FilterRegistrationBean timeFilter(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        TimeFilter timeFilter = new TimeFilter();
        filterRegistrationBean.setFilter(timeFilter);
        List<String> urls = new ArrayList<>();
        urls.add("/*");
        filterRegistrationBean.setUrlPatterns(urls);
        return  filterRegistrationBean;
    }
}

使用Interceptor对请求进行拦截

@Component
public class TimeInterceptor implements HandlerInterceptor{
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {
        System.out.println("preHandle。。。");
        System.out.println(((HandlerMethod)handler).getBean().getClass().getName());
        System.out.println(((HandlerMethod)handler).getMethod().getName());
        httpServletRequest.setAttribute("start",new Date().getTime());
        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("start");
        System.out.println("耗时:"+(new Date().getTime()-start));

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("afterCompletion。。。");
        long start = (long)httpServletRequest.getAttribute("start");
        System.out.println("耗时:"+(new Date().getTime()-start));
        System.out.println("ex is"+e);
    }
}

对拦截器进行配置

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter{
    @Autowired
    private TimeInterceptor timeInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(timeInterceptor);
    }

    @Bean
    public FilterRegistrationBean timeFilter(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        TimeFilter timeFilter = new TimeFilter();
        filterRegistrationBean.setFilter(timeFilter);
        List<String> urls = new ArrayList<>();
        urls.add("/*");
        filterRegistrationBean.setUrlPatterns(urls);
        return  filterRegistrationBean;
    }
}

使用切面对请求进行拦截,首先需要在类上声明@Aspect注解表明这是一个切面,其次需要在方法上声明切入点及通知方法,示例如下

@Aspect
@Component
public class TimeAspect {
    @Around("execution(* com.wiseweb.controller.UserController.*(..))")
    public Object handlerControllerMethod(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("进入切面");
        Object[] args = pjp.getArgs();
        for (Object arg : args) {
            System.out.println("在切面中获取到的参数为" + arg);
        }
        long start = new Date().getTime();
        Object obj = pjp.proceed();
        System.out.println("切面执行的时间为:" + (new Date().getTime() - start));
        return obj;
    }
}

过滤器,拦截器,切面均可实现对请求时间的记录,那他们之间有什么区别?
过滤器:在过滤器中只可以拿到请求的url及访问的参数
拦截器:不仅可以拿到请求的url及访问参数,还可以得到具体访问的类名及方法名
切面:除了可以拿到参数之外,还可以拿到目标方法的返回对象
在实际应用中,可根据需求使用不同的实现方式。

上传及下载功能:

```
@RestController
@RequestMapping("/file")
public class FileController {

    private String folder = "D:/source/spring_security/security_demo/src/main/java/com/wise/web/controller";

    //文件上传
    @PostMapping
    public FileInfo upload(MultipartFile file) throws IOException {
        System.out.println(file.getName());
        System.out.println(file.getSize());
        System.out.println(file.getOriginalFilename());
        File localFile = new File(folder, new Date().getTime() + ".txt");
        file.transferTo(localFile);
        return new FileInfo(localFile.getAbsolutePath());
    }

    //处理文件下载
    @GetMapping("/{id}")
    public void download(@PathVariable String id, HttpServletRequest request, HttpServletResponse response) throws IOException {
        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();
        }
    }
}

使用异步方法对性能进行提升:

@RestController
public class AsyncController {
    private Logger logger = LoggerFactory.getLogger(getClass());

    //传统rest处理模式
    @RequestMapping("/order")
    private String order() throws InterruptedException {
        logger.info("主线程开始");
        Thread.sleep(100);
        logger.info("主线程结束");
        return "success";
    }

    //异步rest处理模式
    @RequestMapping("/async/order")
    private Callable<String> asyncOrder() throws InterruptedException {
        logger.info("主线程开始");
        Callable<String> result = new Callable<String>() {
            @Override
            public String call() throws Exception {
                logger.info("副线程开始");
                Thread.sleep(100);
                logger.info("副线程结束");
                return "SUCCESS";
            }
        };
        logger.info("主线程结束");
        return result;
    }
}

至此resutful风格的Api就告一段落,接下来会写一些关于权限的文章,例如如何通过SpringSecurity对Api进行保护,在学习过程中如有疑问,可将疑问发送至我的邮箱(wm_rmx@163.com),欢迎交流 .

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程简介这是一门使用Java语言,SpringBoot框架,从0开发一个RESTful API应用,接近企业级的项目(我的云音乐),课程包含了基础内容,高级内容,项目封装,项目重构等知识,99%代码为手写;因为这是项目课程;所以不会深入到源码讲解某个知识点,以及原理,但会粗略的讲解下基础原理;主要是讲解如何使用系统功能,流行的第三方框架,第三方服务,完成接近企业级项目,目的是让大家,学到真正的企业级项目开发技术。适用人群刚刚毕业的学生想提高职场竞争力想学从零开发SpringBoot项目想提升SpringBoot项目开发技术想学习SpringBoot项目架构技术想学习企业级项目开发技术就是想学习SpringBoot开发能学到什么从0开发一个类似企业级项目学会能做出市面上90%通用API快速增加1到2年实际开发经验刚毕业学完后能找到满意的工作已经工作学完后最高涨薪30%课程信息全课程目前是82章,155小时,每节视频都经过精心剪辑。在线学习分辨率最高1080P课程知识点1~11章:学习方法,项目架构,编码规范,Postman使用方法,Git和Github版本控制12~16章:搭建开发环境,快速入门SpringBoot框架17~20章:快速入门MySQL数据库21~30章:MyBatis,登录注册,找回密码,发送短信,发送邮件,企业级接口配置31~41章:实现歌单,歌单标签,音乐,列表分页,视频,评论,好友功能42~48章:阿里云OSS,话题,MyBatis-plus,应用监控49~53章:Redis使用,集成Redis,SpringCache,HTTP缓存54~58章:Elasticsearch使用,集成Elasticsearch,使用ES搜索59~61章:商城,集成支付宝SDK,支付宝支付62~64章:常用哈希和加密算法,接口加密和签名65~67章:实时挤掉用户,企业级项目测试环境,企业级接口文档68~69章:SpringBoot全站HTTPS,自签证书,申请免费证书70~73章:云MySQL数据库,云Redis数据库使用,轻量级应用部署环境,域名解析74~80章:Docker使用,生产级Kubernetes集群,域名解析,集群全站HTTPS81~82章:增强和重构项目,课程总结,后续学习计划

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值