随着前端NodeJs技术的火爆,如今的前端已经非之前传统意义上的前端了,各类前端框架(Vue、React、Angular......)井喷式发展,配合NodeJs服务端渲染引擎,目前前端能完成的工做不只仅局限于CSS,JS等方面,不少系统的业务逻辑均可以放在前端来完成,例如我司的管控html
那可能有些人会说,前端这么火,NodeJs发展这么迅猛,后端是否是之后都没事情干了,其实否则,拿Java来讲,通过这么多年发展,已经至关稳定,完善的生态圈也非最近今年发展起来的NodeJs可比,咱们经常说闻道有前后,术业有专攻,用在这里最合适不过了,集群、分布式、高可用等等技术仍是须要后端架构师来思考的事情前端
目前前端同后端的合做方式是先后端分离,经过Nginx+Tomcat的组合部署(还可加nodejs中间件)方式能有效的进行解耦,而且先后端分离为项目之后的架构扩展、微服务化、组件化都打下重要基础,因此这在之后是一个发展的必然趋势,咱们须要去适应,作出改变!!!java
早期的开发方式以下图:node
这也是我前面工做1-3年的开发方式,咱们没有前端帮咱们写JS函数功能,全部的页面表单验证,数据渲染,数据接口编写都是咱们后端所有实现,看上去更像是一个全栈工程师,从需求分析、搭建整个技术架构、数据库表设计、功能设计、编码开发,再到最终部署上线,咱们无所不在,这可能也是目前不少小公司仍然在沿用的开发方式,不少后端同窗担负起了项目的方方面面linux
以我目前的经验来看,这样的开发方式对我我的的成长是有益无害的,由于你只有在了解了前端的JS/CSS/HTML的状况下,而后再谈目前的先后端分离,会让你的工做事半功倍,在写后端接口前,你脑子里浮现的是整个功能的交互页面,最终呈现的是先后端合做开发好后的的终端结果,这大大缩减了先后端的沟通交流nginx
可能因为我在前面三年积累了丰富的前端经验,在上家公司主要负责开发官网、微信、后台等相关系统的接口,前期咱们的开发方式虽然也是先后端分离的方式,但大都使用jsonp跨域接口调用的方式来达到分离效果,后端全部的接口都是可跨域调用的jsonp形式,抛开须要登陆的受权以外的接口,前端在开发的时候本地无需开启服务便可调用服务端接口,而后渲染数据,完成页面交互渲染效果git
jsonp的优势web
不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制ajax
兼容性更好,在更低版本的ie浏览器中都能兼容,这里区别于cors跨域类型redis
jsonp的原理其实很简单,固然,这也涉及到前端的知识,简单点说就是js端的function函数执行
正常的后端响应数据,例如:
{ "code":"8200", "data":{ "id":"100", "name":"Test" //more...... } }
jsonp须要的返回格式:
callback({ "code":"8200", "data":{ "id":"100", "name":"Test" //more...... } });
前端在页面定义callback回调函数,callback函数接收后端响应回来的data-json数据,后端响应后执行callback函数达到调用前端业务逻辑的目的,渲染页面
这种配合开发方式也是适合前端尚未引入Node等一站式开发解决方案的状况下引入的,纯粹的HTML+CSS+JS同后端对接,绑定业务接口,渲染数据
咱们在使用JSONP开发的时候,前端都是须要在页面端写死HOST+IP接口地址,存在很重大一个弊端就是前端须要些config文件,来配置咱们后端的接口请求地址,若是前端工程师规范意识强一点,会通用到一个配置文件里,可是若是没有这方面的意识的话,就会出现代码里硬编码的状况,不利于服务器迁移,代码更新,接口变更等操做
为规避上面碰到的问题,使用nginx的反向代理功能,将后端服务器代理下来,前端在开发的时候本地开启nginx服务,即解决了jsonp跨域问题,同时也解决了无需写死后端的服务ip+端口地址,利于后端在部署时整合代码,减小没必要要的错误
随着NodeJs的火热,前端已经能够本地开启服务写接口的状况下,就相似服务端开启tomcat同样,在这样的状况下,前端框架VUE、React等都在此基础上,提供了一套完整的技术解决方案,这和上面说到的开启nginx服务架构有点相似
这样作的意义:真正的解放了先后端,专一各自擅长的领域
技术架构以下:
前端node服务直接访问后端Java Restful Api接口服务,Api接口最终访问数据库完成数据查询最终返回node层,node渲染响应数据到前端
若是存在会话信息同步等问题,可使用中间件,例如redis缓存数据库,解决前端node和后端Api信息同步问题,传参能够经过JWT等方式完成接口权限验证
无论是jsonp仍是ajax+nginx这两种方式,node做为中间件均可以轻松切换处理,并且node做为中间层,还能够将多个后端接口组合成一整个数据集,最终以同步的方式渲染前端,这也利于作SEO优化,也是前面两种方式没法作到的
关于先后端分离,详细可阅读先后端分离的思考与实践,该文章详细的列述了关于先后端分离的实际经验
随着先后端的分离,后端工程师不须要编写页面,不须要写JS,只须要提供接口便可,但是就是仅仅这一个接口,对于不少后端开发工程师而言,在实际开发,同前端对接的过程当中,依然问题重重
不少后端同窗说我只负责写接口,其余我一律无论,这样形成的后果就是
一、接口结构无序、杂乱无章
二、接口和实际业务场景不相匹配、不可用
三、频繁的同前端沟通,简单的事情复杂化,先后端都很恼火
四、事情没作好
后端在编写接口前,首先是对业务的理解,在对业务未理解透彻以前,编码都是无心义的,做为后端来讲,须要锻炼本身对整个系统全局考虑的能力,接口之间并不是是毫无关联的,咱们在写第一个接口之间,其余接口之间的业务逻辑也许考虑到,这在后端团队合做开发不一样功能的状况下显得尤其重要.
后端在开发接口时,我以为主要从如下几个方面须要注意:
接口url 定义
接口类型、参数
全局错误码定义
接口json格式
接口文档编写
对于后端开发人员来讲,接口前端入参,最终组合查询数据库资源,通过一系列相关业务场景下的计算,响应给前端json数据,每一层url的path定义须要清晰明了,这和后端在使用AOP定义事务管理同理,后端service须要知足必定的命名规范,这样方便统一管理,并且有这层规范后,后续的先后端对接会轻松不少
为了在许多API和长时间内提供一致的开发人员体验,API使用的全部名称应为:
这包括接口,资源,集合,方法和消息的名称。
因为许多开发人员不是英文母语人士,所以这些命名约定的目标之一是确保大多数开发人员可以轻松了解API。 它经过鼓励在命名方法和资源时使用简单,一致和小的词汇表来实现。
关于接口的请求类型,目前比较经常使用的:GET
、POST
、PUT
、DELETE
、PATCH
GET(SELECT):从服务器取出资源(一项或多项)。
POST(CREATE):在服务器新建一个资源。
PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
DELETE(DELETE):从服务器删除资源。
后端可根据不一样的业务场景定义不一样的接口类型
在定义接口参数之时,目前咱们经常使用的几种提交方式
表单提交,application/x-www-form-urlencoded
表单提交主要针对key-value
的提交形式
以下Java片断:
@PostMapping("/queryAll") public RestfulMessage queryAll(RuleCheckLogs ruleCheckLogs, @RequestParam(value = "current_page",defaultValue = "1")Integer current_page , @RequestParam(value = "page_size",defaultValue = "10")Integer page_size , @RequestParam(value = "tableName",required = false) String tableName){ RestfulMessage restfulMessage=new RestfulMessage(); try{ assertArgumentNotEmpty(ruleCheckLogs.getProjectId(),"质检方案id不能为空"); restfulMessage.setData(qcRuleCheckLogsService.queryRuleLogsByPage(ruleCheckLogs,tableName,current_page,page_size)); }catch (Exception e){ restfulMessage=wrapperException(e); } return restfulMessage; }
文件流提交
json提交,application/json
json提交方式在SpringMVC或Spring Boot中主要有两种,一种是以@RequestBody
注解接收方式,另一种是以HttpEntity<String> requestEntity
字节接收
Java代码示例:
@PostMapping("/mergeModelEntitys") public RestfulMessage mergeModelEntitys(HttpEntity<String> requestEntity){ RestfulMessage restfulMessage=new RestfulMessage(); try{ JsonObject paramsJson = paramJson(requestEntity); assertJsonNotEmpty(paramsJson,"请求参数不能为空"); //more... }catch (Exception e){ restfulMessage=wrapperException(e); } return restfulMessage; }
错误码的定义同HTTP请求状态码同样,对接者能经过系统定义的错误码,快速了解接口返回错误信息,方便排查错误缘由
{ "code": "8200", "message": "Success", "data": { "total_page": 1, "current_page": 1, "page_size": 10, "count": 5, "data": [ { "id": "a29ab07f1d374c22a72e884d4e822b29", //...... }//.... ] } }
后端响应json给前端须要注意如下几点:
一、json格式需固定
例如以下图形
如上图所示,横向是时间,纵向是value值
咱们给出的json结构应该如此:
[ { "date":"2018-01", "value":100 }, { "date":"2018-02", "value":200 } //more... ]
在工做中,咱们常常遇见这样的数据格式:
[ "2018-01":{ value:100 }, "2018-02":{ value:200 } //more... ]
这里所说的json格式固定主要针对此种状况,后端给到前端的接口格式必须是固定的,全部动态数据值都需相应的key与之对应
二、全部返回接口数据需直接可用,越简单越好
后端提供给前端的接口数据,最终交给前端的工做,只须要让前端渲染数据便可,越简单越好,不因掺杂过多的业务逻辑让前端处理,全部复杂的业务逻辑,能合并规避掉的都需后端处理掉.
接口文档编写是先后端对接重要依据,后端写明接口文档,前端根据接口文档对接
文档形势目前主要分几种:
一、依赖swagger框架,自动生成接口文档(swagger只能生成基于key-value详细参数方式,针对json格式,没法说明具体请求内容)
二、手动编写说明文档,推荐markdown编写
万事俱备,只欠东风,虽然上面咱们准备了全部咱们该准备的,接口定义天衣无缝,接口文档也已说明,但在对接时任然可能出现问题,此时我想咱们还需注意的细节
一、后端接口需自行进行Junit单元测试
Spring目前集成Junit框架可方便进行单元测试,包括对业务bean的方法测试,以及针对api的mock测试
@RunWith(SpringRunner.class) @SpringBootTest public class QcWebApplicationTests { @Autowired private WebApplicationContext context; private MockMvc mvc; @Autowired QcFieldService qcFieldService; @Before public void setUp() throws Exception { //初始化mock对象 mvc = MockMvcBuilders.webAppContextSetup(context).build(); } @Test public void queryByDsId(){ try { //针对mock-接口Controller层测试 mvc.perform(MockMvcRequestBuilders.post("/qc/entity/queryByDsId") .contentType(MediaType.APPLICATION_JSON_UTF8) .param("dsId", "7d4c101498c742368ef7232f492b95bc") .accept(MediaType.APPLICATION_JSON)) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()); } catch (Exception e) { e.printStackTrace(); } } @Test public void testUpdateField(){ QcField qcField=new QcField(); qcField.setId("513ee55f5dc2498cb69b14b558bc73e6"); qcField.setShortName("密码"); //业务bean-service方法测试 qcFieldService.updateBatchFields(Lists.newArrayList(qcField)); }
二、使用工具测试,推荐PostMan
做为接口调试神器,Postman大名想必你们都已知道
做为后端来讲,咱们须要学会查看chrome推荐给咱们的审查元素的功能,可参看Chrome开发工具介绍
chrome提供了一个能够copy当前接口的url功能,最终生成curl命令行
最终经过Copy as cURL(bash)
功能可生成curl命令
curl 'http://demo.com/qc/ds/getAll' -H 'Origin: http://demo.com' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: zh-CN,zh;q=0.9' -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36' -H 'Content-Type: application/x-www-form-urlencoded' -H 'Accept: application/json, text/plain, */*' -H 'Referer: http://demo.com/index.html' -H 'Connection: keep-alive' --data 'current_page=1&page_size=6&' --compressed
以上命令能够在Linux等各终端直接执行
curl命令是一个利用URL规则在命令行下工做的文件传输工具。它支持文件的上传和下载,因此是综合传输工具,但按传统,习惯称curl为下载工具。做为一款强力工具,curl支持包括HTTP、HTTPS、ftp等众多协议,还支持POST、cookies、认证、从指定偏移处下载部分文件、用户代理字符串、限速、文件大小、进度条等特征。作网页处理流程和数据检索自动化,curl能够祝一臂之力。
postman提供导入curl命令行
三、先后端需心平气和沟通,勿推卸责任,先后端开发人员水平不尽相同,做为同事,须要的是团结合做,努力将事情作好,而非相互推卸
先后端分离,简化了咱们的开发方式,不一样人专一于不一样的领域,技术价值最大化,大大提升工做效率,咱们在掌握这些技能的同时,也须要增强自身的发展,以适应当前的技术发展趋势,无论是前端仍是后端,多了解一些,老是没错的,古人云:技多不压身,我想也正是此理!!!
最后,推荐一个Swagger的Ui工具swagger-bootstrap-ui项目,Java开发者若是使用swagger的话能够直接使用,很方便哦,先后端接口对接更加方便、清晰.快试试吧