V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
Allianzcortex
V2EX  ›  分享创造

分享 LaraForum | rest 后端论坛应用 | Springboot+jpa | 轻量级 | 完整单测

  •  1
     
  •   Allianzcortex · 2019-03-14 21:03:25 +08:00 · 1566 次点击
    这是一个创建于 2082 天前的主题,其中的信息可能已经有所发展或是发生改变。

    LaraForum

    一个提供论坛纯后端 API 的应用 Build Status

    项目地址:Github

    最近开发了这个应用,包括用户的注册 /登陆 /登出权限管理,发帖 /删帖 /评论 /删评 /信息提醒,主题创造 /删除,文章分类分页搜索,以及基于 MySQL Full-text SearchLucene 的模糊匹配关键词搜索。

    列举 API 如下:

    得到某篇单独发帖:

    get /api/articles/get/single/{slug}
    
    return:
    
    {
        'id':{id},
        'name':{name},
        'tag':[1,2,3]
    }
    

    多重条件设置:

    get /api/articles/get/batch?tag=xx&author=yy
    

    为某个用户增加消息提醒

    post /api/read/notice/{userId}
    

    为某个用户设置权限

    /api/permission/add/{user}/2
    

    为某个用户设置角色

    /api/role/add/{user}/2
    

    根据关键字进行搜索,默认采用 MySQL,可以配置为 Lucene

    /api/search/{keyWord}
    

    etc...


    除了大家都会用到的 @ControllerAdvice 全局处理 Exception 和 @ResponseEntity 返回数据外,这里主要想描述一下这个应用的几个特点吧:

    1. 轻量级:

    这里轻量级指的是除了 Springboot+Spring JPA+Spring MVC 外其它的 组件都没有涉及到,两个比较重要的功能①认证(authentication)鉴权(authorization)没有用到 spring-security 或者 shrio,是单独 实现的。

    ① 对认证 authentication,默认采用的是 JWT,每次验证是由 filter 提供,参见 代码,每次判断则是由 interceptor 提供,参见 代码。得到的值会用 request.setAttribute 写入后由后端应用读取,避免 重复验证。关于 jwt 实现过期 logout 在纯后端应用里相对难实现(前端直接删除 jwtToken 即可,现在 采用的方法是存储到数据库里,不符合 stateless 无状态思想,因为原话是会 requires a DB lookup each time)。比较理想 的做法是在 SO 上看到的这个 评论,在写入 Token 的值里进行操作,后续会实现这一点。

    ② 对权限管理 authorization,采用的是注解 @+ 用 AspectJ 实现 AOP 去做。实现的两个注解分别是 @RequirePermissiona(代码) 和 @RequireRoles(代码),之后统一 [管理](代码),分别判断用户是否可以去执行对应行为。使用时只需要在方法前加入 @RequirePermission("youPermission") ,就会自动判断,对业务逻辑代码没有影响,类似于 Python 的装饰器。比如对于这段 代码,用户必须有 create_post 的权限,才能发表帖子。

    @RequirePermissions("create_post")
    @Transactional
    @PostMapping("permission/add/{userName}/{pNumber}")
    public void addUserPermission(@PathVariable String userName, @PathVariable Integer pNumber) {
       // logic
    }
    
    

    1. UnitTest 与 IntegrationTest

    这一点主要是因为看到的很多 sprint boot 代码里都缺少相应的测试,所以希望能补充完整。看到的文章里也有关于这两个测试的争论,比如有 SO 的回答说因为 spring 应用与现实交互所以 IntegrationTest 能更好反映程序的运行,也有文章说要以 UnitTest 为主测试单个函数的正确性,所以就都做了.... 主要用的框架是 JunitMockito,在 Repository 层面进行了 IntegrationTest,判断是否能真的写入 数据库里(代码),在 Service 层面进行了 UnitTest,判断是否会调用对应 Repository 的行为(代码).

    @Test
    public void whenUserSave_thenCheckSuccess(){
         userServiceMock.save(user);
         verify(userRepositoryMock).save(user);
    }
    

    在 Controller 层面则同时进行两种测试,判断得到的对应结果是否符合期望,同时判断是否会调用对应 Service 的行为(代码)。暂时测试了 User/UserRepositor/UserService/UserController ,后续会进一步补充。

    因为是纯后端应用所以暂时没有界面,就发一张 Postman 的截图吧(:

    整体来说还有很多要优化的点,后续也会一直开发这个项目并写前端进行匹配,多谢大家的支持 quq

    2 条回复    2019-03-14 22:08:55 +08:00
    Kilerd
        1
    Kilerd  
       2019-03-14 22:03:09 +08:00   ❤️ 1
    问题是你这个 API 设计得一点都不 restful 啊
    Allianzcortex
        2
    Allianzcortex  
    OP
       2019-03-14 22:08:55 +08:00
    @Kilerd 谢谢谢谢,是的,这个问题在 Spring 官方教程里专门提到过,https://spring.io/guides/tutorials/rest/ 里有一节在讨论:

    What makes something RESTful?
    So far, you have a web-based service that handles the core operations involving employee data. But that ’ s not enough to make things "RESTful".

    Pretty URLs like /employees/3 aren ’ t REST.

    Merely using GET, POST, etc. aren ’ t REST.

    最关键的是这句话:Having all the CRUD operations laid out aren ’ t REST.
    目前 API 没有全部遵守 RESTful 的要求,这是最近两周开发的项目,后续会进一步完成。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1181 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 42ms · UTC 18:01 · PVG 02:01 · LAX 10:01 · JFK 13:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.