前后端整合
从单体应用改造成前后端分离的架构后,理想状态下,前后端可以分别独立开发、测试、部署,然而若想实现整体业务,则需要将前后两端整合。本节将介绍我们开展改造工作以来,在前后端整合领域积累的部分最佳实践。
RESTful接口
后端接口均按照社区RESTful接口标准定义:
• 语义化URL,活用GET/POST/PUT/DELETE四种HTTP方法;
• 支持JSON与XML两种数据呈现格式,默认情况下,HTTP请求和响应均使用JSON,加入XML参数,请求和响应改为使用XML;
• 优先使用HTTP状态码(Status Code)表现后端成功状态或各类常见错误,如HTTP 200(OK)、401(Unauthorized)、422 (Unprocessable Entity) 等;
• 统一业务错误码和错误消息;
• 以ISO 8061标准输入输出日期时间,如:2015-09-08T01:55:28Z。
在前端我们基于浏览器Fetch接口,封装了Spark-fetch包,提供如下功能:
• 浏览器Fetch的所有功能;
• JSON序列化、反序列化;
• 为HTTP错误统一显示对话框,其中401状态会跳转至登录页面;
• 根据用户需要缓存特定资源;
• 防止Cross-Site Request Forgery (CSRF)。
我们为前端开发了一套简单的Discover服务发现,以Key-value方式描述前端中会用到的RESTful服务,Spark-fetch包在发起HTTP请求时只要传入Key和相关参数即可。目前主要用来防止前端代码里Hard-code服务URL,之后会与整个公司级别的服务发现整合起来。
除此之外,我们还在后端开发了一套API Gateway,提供认证(Authentication)、限流(Throttling)、跨域等公共功能。上述RESTful接口本身无须处理认证等逻辑。在部署后端服务后,只有API Gateway开放给外网访问,其他RESTful接口均限于机房内网访问,经由API Gateway的反向代理提供给外网。即前端在调用这些接口时,必须经过API Gateway调用。
认证授权
文章一开始提到的单体Web应用其实在FreeWheel有多套,分别对应于多个业务线或产品线。这些单体应用开发的阶段有先有后,架构和实现的设计也存在着差别,其中很重要的一点就是认证方式的差别,为了满足多个应用联合登录的需求,尤其是向后兼容SPA的联合登录,我们在后端以Golang开发了新的SSO服务。SPA在登录页面调用SSO接口,登录成功则获取Token并存入Cookie,这样后续的接口请求就会将Cookie传入API Gateway以获取认证信息。
至于授权(Authorization),我们在现有的Ruby on Rails应用中大部分是基于CanCan框架实现的,改造为前后端分离架构后,我们将与导航、功能入口相关的授权信息从后端完整传回前端,用前端代码判断特定导航或组件是否显示、是否禁用。当然,RESTful接口中仍有完整的授权判断逻辑。如果有恶意用户通过Hack的方式修改了前端授权信息访问了本不能访问的界面,他依旧无法获得列表数据、也无法提交数据修改。
后端Docker容器化
在业务模块开发过程中,开发人员需要在开发前端代码的同时能访问到后端接口及测试数据。如果是单体应用的开发,开发人员只要配置一套开发环境即可达到这个目标,但在前后端分离后,前端开发人员除了配置前端开发环境,还要配置后端。后端代码有更新时,需要及时检出代码并顺利编译,数据库有更新时也需要执行相应的SQL脚本。这些日常工作成为前端开发人员的痛点。
后端Docker容器化有效解决了这一痛点。我们目前的CI (Continuous Integration) Pipeline会在后端代码检入远程Git后触发编译,编译成功后会创建一个包含该编译版本的Docker Image并上传至公司内部的Docker Image仓库,类似的还有数据库,以及其他中间件的Image。前端开发人员不再需要搭建后端开发环境,只需在开发机上安装Docker(如Docker for Mac),在前端工程内会维护一个docker-compose. xml,声明了前端工程所需要的后端Docker Image,每次该文件更新后,前端开发人员只需要运行docker-compose up -d即可启动一系列Docker Container,在本机运行完整的后端服务,这里甚至包含了适用于开发的部分测试数据。
整合测试
前后端的分离和整合对质量保证提出了新的要求。我们在前端编写Fetch逻辑时,会以Mock方式编写对应的单元测试。后端每个接口也有响应的单元测试。而这两端分别的单元测试还不足以保证软件质量,理论上讲,纵使两者单元测试覆盖率均达到100%,也不能保证覆盖所有用例。作为质量保证的关键环节,在两端的单元测试都通过后,我们的CI会执行端到端的自动化测试。这些自动化测试模仿了用户的使用场景,完整的覆盖了前端、后端、数据库乃至其他中间件。