这个来自以前作的培训,删减了一些业务相关的,参考了不少资料( 参考资料列表),谢谢前辈们,么么哒 😘
目前先后端的通讯通常都是经过协议来完成的,这里介绍和前端开发相关的各种协议。javascript
HTTP 协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是万维网的数据通讯的基础。设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法,如今基本上什么类型的文件均可以传输了,好比图片、CSS、JS、数据报文等。html
HTTP通常基于TCP/IP通讯协议来传递数据,它有以下特色:前端
HTTP使用统一资源标识符(Uniform Resource Identifiers, URI)来传输数据和创建链接。URL是一种特殊类型的URI,包含了用于查找某个资源的足够的信息。一个完整的URL通常包括如下几部分:html5
协议
域名
端口
虚拟目录
文件名
参数
锚
java
好比:http://test.google.com:80/test/test.html?query=admin#home
git
一般一个HTTP请求消息包含以下内容:请求行、请求头、空行、消息主体。程序员
好比:github
GET /books/?sex=man&name=Professional HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)
Gecko/20050225 Firefox/1.0.1
Connection: Keep-Alive
复制代码
GET /books/?sex=man&name=Professional HTTP/1.1
用来讲明请求类型,要访问的资源以及所使用的HTTP版本。比较常见的请求类型有GET,POST,PUT,DELETE,OPTIONS等。带有请求数据的POST请求:web
POST / HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)
Gecko/20050225 Firefox/1.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 40
Connection: Keep-Alive
sex=man&name=Professional 复制代码
通常状况下,服务器接收并处理客户端发过来的请求后会返回一个HTTP的响应消息。一般一个HTTP请求消息包含以下内容:状态行、消息报头、空行、响应正文。typescript
好比:
HTTP/1.1 200 OK
Date: Fri, 22 May 2009 06:07:21 GMT
Content-Type: text/html; charset=UTF-8
<html>
<head></head>
<body>
<!--body goes here-->
</body>
</html>复制代码
状态码由三位数字组成,第一个数字定义了响应的类别,共分五种类别:
常见的状态码有以下几种:
HTTP2即超文本传输协议2.0版本,是HTTP协议的下一个版本。由于标准委员会不打算再发布子版本了,因此直接叫HTTP/2,而不叫HTTP/2.0。
HTTP2相对于以前的HTTP协议有如下几个优势:
HTTP 1.1会让资源排队加载,以下图所示:
但当咱们开启了HTTP/2以后,有了TCP多路复用,个数几乎没有限制了,以下图所示:
HTTP/2 将 HTTP 协议通讯分解为二进制编码帧的交换,这些帧对应着特定数据流中的消息。全部这些都在一个 TCP 链接内复用。这是 HTTP/2 协议全部其余功能和性能优化的基础。
目前支持HTTP2协议传输的浏览器依然不多,随着技术的发展和浏览器的更新迭代,HTTP2的时代终会到来,但咱们依然不能在短期内企图经过它来帮咱们进行页面优化。
HTTPS(超文本传输安全协议 Hypertext Transfer Protocol Secure)经由HTTP进行通讯,但利用SSL/TLS来加密数据包。HTTPS的主要思想是在不安全的网络上建立一安全信道。一般,HTTP 直接和 TCP 通讯。当使用 SSL 时,则演变成先和 SSL 通讯,再由 SSL 和 TCP 通讯了。简言之,所谓 HTTPS,其实就是身披 SSL 协议这层外壳的 HTTP。HTTP的URL由“http://”起始且默认使用端口80,HTTPS的URL由“https://”起始且默认使用端口443。
SSL 是独立于 HTTP 的协议,因此不光是 HTTP 协议,其余运行在应用层的 SMTP 和 Telnet 等协议都可配合 SSL(Secure Socket Layer) 协议使用。
HTTP是不安全的,攻击者经过监听和中间人攻击等手段,能够获取网站账户和敏感信息等。人们对 HTTPS 有一个广泛的错误认识,认为只有处理敏感通讯的网站才须要 HTTPS。 每一个未受保护的 HTTP 请求均可能暴露与用户行为和身份有关的信息。尽管访问一次未受保护的网站可能看上去无害,但一些入侵者会查看汇总的用户浏览活动,以推断他们的行为和意图,从而进行去匿名化攻击,查出匿名用户的身份。例如,员工可能在阅读未受保护的医疗文章时不经意地向其雇主泄露敏感的健康信息。
加密和解密同用一个密钥的方式称为共享密钥加密(Common key crypto system),也被叫作对称密钥加密。
以共享密钥方式加密时必须将密钥也发给对方。可究竟怎样才能安全地转交?在互联网上转发密钥时,若是通讯被监听那么密钥就可会落入攻击者之手,同时也就失去了加密的意义。
SSL 采用一种叫作公开密钥加密(Public-key cryptography)的加密处理方式。公开密钥加密使用一对非对称的密钥。一把叫作私有密钥(private key),另外一把叫作公开密钥(public key)。顾名思义,私有密钥不能让其余任何人知道,而公开密钥则能够随意发布,任何人均可以得到。
使用公开密钥加密方式,发送密文的一方使用对方的公开密钥进行加密处理,对方收到被加密的信息后,再使用本身的私有密钥进行解密。利用这种方式,不须要发送用来解密的私有密钥,也没必要担忧密钥被攻击者窃听而盗走。
HTTPS 采用混合加密机制:
公开密钥加密方式仍是存在一些问题的。那就是没法证实公开密钥自己就是货真价实的公开密钥。
证书颁发机构 (CA) 是一个组织,对公钥和与公共 DNS 名称之间的映射进行证明。例如,客户端如何知道特定公钥是否为 www.foobar.com 的真实公钥?按理说,没法知道。CA 证明特定密钥是特定网站的真实密钥,它使用本身的私钥来加密签名该网站的公钥。此签名在计算上是没法伪造的。浏览器(和其余客户端)维护信任锚存储库,它包含知名 CA 拥有的公钥,而且它们使用这些公钥来加密验证 CA 的签名。
最后使用共享密钥来进行之后的通讯,详细流程:
在实际的前端应用项目中,除了使用应答模式的HTTP协议进行普通网络资源文件的请求加载外,有时也须要创建客户端与服务端之间的实时链接进行通讯,例如网页实时聊天的应用场景,这就必须涉及浏览器端的实时通讯协议了。对于这些对实时性要求较高的应用场景,普通的HTTP协议就并不适用。虽然前端能够经过Ajax定时向服务端轮询的方式来持续获取服务端的消息,可是这种方式效率相对较低。
WebSocket是浏览器端和服务器端创建实时链接的一种通讯协议,能够在服务器和浏览器端创建相似Socket方式的消息通讯。相对于HTTP1.1协议,WebSocket协议的优点是方便服务器和浏览器之间的双向数据实时通讯。
这里只是相似Socket方式,WebSocket 是创建在 TCP/IP 协议之上,属于应用层的协议,而 Socket 是在应用层和传输层中的一个抽象层,它是将 TCP/IP 层的复杂操做抽象成几个简单的接口来提供给应用层调用。简单回顾一下7层网络模型:
简单来讲,咱们在传输数据时,能够只使用(传输层)TCP/IP协议,可是那样的话,若是没有应用层,便没法识别数据内容。若是想要使传输的数据有意义,则必须使用到应用层协议。应用层协议有不少,好比HTTP、FTP、TELNET等,也能够本身定义应用层协议。WEB使用HTTP协议做应用层协议,以封装HTTP文本信息,而后使用TCP/IP作传输层协议将它发到网络上。
TCP/IP只是一个协议栈,就像操做系统的运行机制同样,必需要具体实现,同时还要提供对外的操做接口。这个就像操做系统会提供标准的编程接口,好比win32编程接口同样,TCP/IP也要提供可供程序员作网络开发所用的接口,这就是Socket编程接口。Socket的出现只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,从而造成了咱们知道的一些最基本的函数接口,好比create、listen、connect、accept、send、read和write等等。
WebSocket 的实现分为握手,数据发送/读取,关闭链接。
var ws = new WebSocket("wss://echo.websocket.org");
ws.onopen = function(evt) {
console.log("Connection open ...");
ws.send("Hello WebSockets!");
};
ws.onmessage = function(evt) {
console.log( "Received Message: " + evt.data);
ws.close();
};
ws.onclose = function(evt) {
console.log("Connection closed.");
}; 复制代码
在线演示:html5demos.com/web-socket/
DDP ( Distributed Data Protocol,分布式数据协议)是一种新型的客户端与服务器端的实时通讯协议,因为兼容性的缘由,目前使用还不普遍。
DDP使用JSON的数据格式在客户端和浏览器之间进行数据传输通讯,因此对于前端开发者来讲使用很是方便。有名的Meteor Web框架的双向实时数据更新机制底层使用的就是DDP,这种协议模式下客户端可向服务器端发起远程过程调用,客户端也能够订阅服务端数据,在服务端数据变化时,服务器会向客户端发起通知,触发浏览器响应的操做。
//建立服务端
const DDPClient = require('ddp');
const client = new DDPClient({
host: 'localhost',
port: 3000
});
//监听消息
client.on('message', function(data, flags){
console.log('[DDP消息]: ', data);
});
//建立链接
client.connect(function(){
client.subscribe('post',[],function(){
console.log('[post订阅消息]');
});
});复制代码
REST (Representational State Transfer,表述性状态转化)并非某一种具体的协议,而是定义了一种网络应用软件之间的架构关系并提出了一套与之对应的网络之间交互调用的规则。与之相似的例如早期的WebSevice,WebSevice如今基本都不用了。
在REST形式的软件应用服务中,每一个资源都有一个与之对应的URI地址,资源自己都是方法调用的目标,方法列表对全部资源都是同样的,并且这些方法都推荐使用HTTP协议的标准方法,例如GET、POST、PUT、DELETE等。若是一个网络应用软件的设计是按照REST定义的,咱们就能够认为它使用的交互调用的方法设计遵循RESTful规范。换种方式理解,RESTful 是一种软件架构之间交互调用数据的协议风格规范,它建议以一种通用的方式来定义和管理数据交互调用接口。
例如:对于书籍book的记录管理接口,有增、刪、改、查操做,因而咱们定义接口:
看上去好像没有什么问题。后来,另外一个项目也有相似的接口定义,却可能叫做:
接着有一天,项目负责人可能会说,要升级接口来知足新的需求,因而咱们又添加了:
这样用起来是没有什么问题,可是这些随意的定义会增长数据接口维护难度和项目继续开发的成本。
这时,咱们或许会考虑使用文档或规范,规定必定要使用add来添加,新的接口版本号放前面 path/v2/addBook,开发的人必须严格按照文档规范去写。这样作很好,但依然不够完善,缘由有如下几点:
这时若是有一个风格更好的通用规范来定义数据交互接口,就不用这么麻烦了。因此咱们彻底能够利用RESTful设计的规范特性来解决上面遇到的问题。对于书籍记录操做接口的命名能够以下操做:
HTTP方法 | URI | 描述 |
---|---|---|
POST | path/v1/book | 新增书籍信息 |
DELETE | path/v1/book | 删除书籍信息 |
PUT | path/v1/book | 更新书籍信息 |
GET | path/v1/book | 获取书籍信息 |
使用RESTful规范来从新设计接口后,一切就变得很清晰天然,这样新的工程师接手项目时,只要他足够了解RESTful规范,几乎没有时间成本。即便他不了解RESTful规范,也能够很快地去了解,这就能够避免他去读那份看似完善其实冗长杂的文档。
这里涉及到了几个设计的原则:
RESTful API 的主要设计原则就是这些,总结来讲就是结合HTTP的固有方式来表征资源的状态变化描述,而不是经过动词加名词的方式来设计。
Github RESTful API:developer.github.com/v3/
GraphQL 对 API 中的数据提供了一套易于理解的完整描述,使得客户端可以准确地得到它须要的数据,并且没有任何冗余,也让 API 更容易地随着时间推移而演进。
GraphQL 解决的最重要的3个问题分别是:
一个简单的示例:咱们要作一个星球大战人物信息展现的UI界面:须要显示人物的姓名,出生年份,星球名称以及全部他们参演的电影的名称。
这个 UI 的 JSON 数据可能相似于:
{
"data": {
"person": {
"name": "Darth Vader",
"birthYear": "41.9BBY",
"planet": {
"name": "Tatooine"
},
"films": [
{ "title": "A New Hope" },
{ "title": "The Empire Strikes Back" },
{ "title": "Return of the Jedi" },
{ "title": "Revenge of the Sith" }
]
}
}
}复制代码
若是使用 React.js ,通常会这样表示视图:
// The Container Component:
<PersonProfile person={data.person} ></PersonProfile>
// The PersonProfile Component:
Name: {person.name}
Birth Year: {person.birthYear}
Planet: {person.planet.name}
Films: {person.films.map(film => film.title)}复制代码
若是使用 RESTful API,咱们可能这样请求数据:
一、获取人物信息:
GET - /people/{id}复制代码
{
"name": "Darth Vader",
"birthYear": "41.9BBY",
"planetId": 1
"filmIds": [1, 2, 3, 6],
...
}复制代码
二、获取星球信息:
GET - /planets/1复制代码
三、获取全部电影信息:
GET - /films/1
GET - /films/2
GET - /films/3
GET - /films/6复制代码
咱们须要发送6个请求才能获取到全部须要的数据,每一个获取数据的方法都是命令式的。每一个接口返回的信息还有不少字段不是咱们所须要的。为了解决这个问题,咱们可能会新增长一个接口,好比:
GET - /people/{id}/films复制代码
可是这样就不是纯粹的RESTful API了,并且后端要额外的建立这个接口,用来知足前端的数据要求,若是增减字段或对象,后端还要添加接口或者从新编码。
若是使用GraphQL,咱们能够这样来查询:
GET or POST - /graphql?query={...}复制代码
好比参数使用:
{
person(personID: 4) {
name,
birthYear,
homeworld {
name
},
filmConnection {
films {
title
}
}
}
}复制代码
一个请求就完成了全部数据的获取。
GraphQL的灵活性也会带来一些问题,好比增长复杂度,资源耗尽攻击,N+1查询等。FB针对N+1给出了 dataloader 的方案。
Github GraphQL API:developer.github.com/v4/
在线调试:developer.github.com/v4/explorer…
Hybrid App是在Native App应用的基础上结合了Web App应用所造成的模式,通常称之为混合App。从技术开发上来看,相比于传统的桌面浏览器端的Web App,它具备如下几方面的特征:
在HTML5中调用Native程序通常有几种较通用的方法:
1、经过URI请求。
Native应用可在移动端系统中注册一个Scheme协议的URI,这个URI可在系统的任意地方受权访问来调起一段原生方法或一个原生的界面。一样,Native 的WebView控件中的JavaScript脚本的请求也能够匹配调用这一通用的Scheme协议。例如咱们经过对 window.location.href 赋值或使用iframe的方式发送一个URI的请求,这个请求能够被Native应用的系统捕获并调起Native应用注册匹配的这个Scheme协议内容。
好比微信的scheme为(weixin://)。
2、经过addJavascriptInterface(Android)或JavaScriptCore(iOS)注入方法到页面中调用。
Android,原生Webview须要先注册可供前端调用的JS函数:
// Android容器容许JS脚本,必需要
webSettings.setJavaScriptEnabled(true);
// Android容器设置侨连对象
mWebView.addJavascriptInterface(getJSBridge(), "JSBridge");
// Android4.2版本及以上,本地方法要加上注解@JavascriptInterface,不然会找不到方法。
private Object getJSBridge(){
Object insertObj = new Object(){
@JavascriptInterface
public String foo(){
return "foo";
}
@JavascriptInterface
public String foo2(final String param){
return "foo2:" + param;
}
};
return insertObj;
}复制代码
而后H5中便可调用原生中注册的函数:
// 调用方法一
window.JSBridge.foo(); // 返回:'foo'
// 调用方法二
window.JSBridge.foo2('test'); // 返回:'foo2:test'复制代码
iOS,须要引入JavaScriptCore库:
#import <JavaScriptCore/JavaScriptCore.h>复制代码
而后原生须要注册API:
//webview加载完毕后设置一些js接口
-(void)webViewDidFinishLoad:(UIWebView *)webView{
[self hideProgress];
[self setJSInterface];
}
-(void)setJSInterface{
JSContext *context =[_wv valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
// 注册名为foo的api方法
context[@"foo"] = ^() {
//获取参数
NSArray *args = [JSContext currentArguments];
NSString *title = [NSString stringWithFormat:@"%@",[args objectAtIndex:0]];
//作一些本身的逻辑
//返回一个值 'foo:'+title
return [NSString stringWithFormat:@"foo:%@", title];
};
}复制代码
以后前端就能够调用了:
// 调用方法,用top是确保调用到最顶级,由于iframe要用top才能拿到顶级
window.top.foo('test'); // 返回:'foo:test'复制代码
3、改写浏览器原有对象。
经过修改原来浏览器的window某些方法,而后拦截固定规则的参数,而后分发给Java对应的方法去处理。这里经常使用的是如下四个方法:
须要先使用JavaScript 在HTML5页面全局中声明相对应的方法。
而后Native向HTML5发起调用,Android平台通常经过loadUrl,iOS一般经过stringByEvaluatingJavaScriptFromString实现。
Android调HTML5:
// 异步执行JS代码,并获取返回值
mWebView.evaluateJavascript("javascript: 方法名('参数,须要转为字符串')", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
// 这里的value即为对应JS方法的返回值
}
});复制代码
iOS调HTML5:
// 能够取得JS函数执行的返回值
// 方法必须是Html页面绑定在最顶层的window上对象的
// 如window.top.foo
[webView stringByEvaluatingJavaScriptFromString:@"方法名(参数);"];复制代码
JSBridge是HTML5与Native通讯的桥梁,其做用是实现HTML5与Native间的双向通讯。JSBridge综合了上面的技术,更多的是一种形式、一种思想,各家的实现方式也略有差别。好比微信JSSDK,就是基于WeixinJSBridge,微信浏览器中的页面,经过WeixinJSBridge调用微信提供的一些原生功能。