HTTP协议详解

日常的web开发过程中,以及其他的开发中,接触到最多的网络交互协议就是http协议,而http协议基于tcp的二度封装协议,既然http协议如此重要,那么我
们就开始系统的对http协议进行扫盲学习吧

请求/响应模型

http属于请求/响应模型,从某种意义上来说,Http协议永远都是客户端发起请求,由服务器端接受请求处理并返回响应报文。如果没有客户端发送请求到服务端,那么服务端无法将消息发送回客户端的。而HTTP交互的流程图如下,当客户端发送请求到服务端的时候,请求头常见的包含有请求方式、URI、协议版本等,以及会携带MIME的消息内容,服务端作为一个状态行的方式响应,包括协议版本、编码、元数据和实体数据等,这样就完成了一个请求/响应流程

HTTP的发展过程

1989年3月,蒂姆 • 伯纳斯 - 李提出了一种能让远隔两地的研究者们共享知识的设想,起初的理念是:借助多文档之间相互关联形成的超文本(HyperText),连成可相互参阅的 WWW(World Wide Web,万维网)随着HTML1.0的诞生,作为其中的超文本传输协议的HTTP正式诞生

Http/0.9:1990年诞生,作为HTTP最早的协议版本,但是由于当时对于HTML相关web的定义未统一,该版本并不是正式版

HTTP/1.0:1996年5月份,随着微软公司与网景通信公司之间爆发的浏览器大战越来越烈,HTML兼容多个浏览器的头疼问题出现了,但是针对网络协议的正式版HTTP1.0出现了

HTTP/1.1:1997年1月份对于HTTP1.0的版本进行了部分内容的修订,作为更标准化的协议出现,也是目前为止依然主流的协议版本

HTTP/1.2:1999年,针对HTTP1.1的改进版本出现,1.2使用了SRV records 更好地支持负载平衡 ,改进了之前只能基于表单的认证方式,提供了Basic和Digest访问认证,增加了一套新的accepted headers

HTTP/2.0:2013年8月份出了新一代的超文本传输协议的草案,与之前的HTTP1.x版本差距颇大,无法兼容,此协议针对连接的安全性做了很大的提升,而HTTP2.0的绝大多数请求基本都是走的HTTPS,所以一般情况下,不会直接用HTTP2.0,而是HTTPS

URI和URL

看到这可能绝大多数的人都很奇怪,URL我们都知道,比如我们浏览一个网站,输入的www.xxx.com的网址就是URL,那么URI是什么呢?其实我们要明白,URI是Uniform Resource Identifier 的缩写,称之为统一资源标示符,到这可能大概明白了,URL其实就是URI的一个子集而已,而URI主要分为Uniform、Resource和Identifier

Uniform:用来方便做多种不同类型的资源得处理,而不需要根据上下文等方法识别访问方式,比如我们输入网址的时候输入的Http:

Resource:传输过程中可以用来区别其他类型的文件的集合定义,资源是一个类型的统称,不仅仅是单一的

Identifier:表示当前可以标识的对象,也称之为标示符

一个URI的例子如下:

ftp://ftp.is.co.za/rfc/rfc1808.txt
http://www.ietf.org/rfc/rfc2396.txt
tel:+1-535-555-1212
telnet://192.168.1.16:80/
urn:oasis:names:specification:docbook:dtd:xml:4.1.2

而URI使用过程中,如果需要涵盖全部信息或者准确信息,就要正确使用URI的格式:


URI的格式.png
名称 作用 是否必选
登录信息 指定用户名和密码作为从服务器端获取资源时必要的登录信息 Y
服务器地址 使用绝对 URI 必须指定待访问的服务器地址 Y
服务器端口 连接服务器的端口 N
文件路径 指定服务器上的文件路径来定位特指的资源(UNLX系统) N
查询字符串 可以作为URL携带的请求参数来协助定位具体文件资源 N
片段标识符 通常可标记出已获取资源中的子资源 N

无状态协议

Http协议属于一种不记录状态的协议,即无状态协议,具体表现为,每次请求发起都会重新进行连接,并不会根据之前的状态或者持久化保持状态。每当有新的请求发送时,就会有对应的新响应产生。协议本身并不保留之前一切的请求或响应报文的信息。这是为了更快地处理大量事务,确保协议的可伸缩性。但是Http虽然是无状态协议,但是有保存状态信息的需求,HTTP1.1开始引入了Cookie技术,使用cookie来代为管理状态信息。

HTTP协议方法

Http协议支持通过不同的请求类型--即协议方法来达成某种目的,实现功能,HTTP1.1支持的协议方法如下:

GET :获取资源

Get方法用来访问已经被URI识别的资源,指定的资源信息经由服务端解析以后返回对应的资源响应内容,也就是说如果当前访问的资源是文本等静态资源,则会返回对应的数据,如果是网关等程序,则会返回对应的结果。

POST:传输实体主体

在HTTP中推荐使用POST方法来传递我们需要传输的内容或者实体信息,虽然GET方法可以添加查询字符串来辅助传递一定的信息和参数,但是要注意的是GET方法定义仅仅是希望根据查询字符串来定位具体的资源,所以GET方法一般都希望得到快速响应,传递的数据的长度有一定的限制

PUT:传输文件

HTTP协议中,PUT方法被定义出来用来朝服务端传递文件(上传)使用的方法,要求在请求的报文中包含文件内容,然后保存文件到请求的URI所在的位置上,但是由于HTTP1.1不带有安全机制,所以一般当前方法在HTTP协议上不开放,类似的协议如:Restful会开放实现其他规范的操作

HEAD:获得报文首部

HEAD方法和GET方法一样,唯一的区别是GET方法返回完整的响应信息,而HEAD方法不需要报文主体部分,因为此方法仅用来获取头信息,用来确认URI是否有效或者触发资源的缓存更新等

DELETE:删除文件

DELETE方法和PUT方法是完全相反的定义,这里的作用是用来删除指定请求URI上位置的资源文件,而和PUT一样,在不带有安全验证机制的情况下一般也是不开放的

OPTIONS:询问支持的方法

OPTIONS方法使用场景比较特殊,一般情况下我们用来查询针对请求URI指定的资源支持的方法有哪些(和跨域操作也有一定的联系)

TRACE:追踪路径

TRACE方法是HTTP协议用来检查和追踪请求过程提供的方法,当发送请求的时候,会在Max-Forwards 首部填入数值,每次经过一个服务端就会-1,当数字减少到0的时候,就会停止传输请求,直到接受到返回的状态码响应才算结束。使用TRACE方法可以检查当前请求是否被篡改,但是由于当前方法很容易引起XST(Cross-Site Tracing,跨站追踪)攻击,所以一般都禁止使用

CONNECT:要求用隧道协议连接代理

使用CONNECT方法要求是与代理服务器建立连接隧道,实现隧道间通信TCP,常见会使用SSL或者TLS协议把内容加密后传递到隧道,CONNECT的格式如下:

CONNECT 代理服务器名:端口号 HTTP版本

我们把1.0和1.1两个版本的支持的协议方法列个表格,看下具体差异:

方法 作用 支持的协议版本
GET 获取资源 1.0、1.1
POST 传输实体主体 1.0、1.1
PUT 传输文件 1.0、1.1
HEAD 获得报文首部内容 1.0、1.1
DELETE 删除文件 1.0、1.1
OPTIONS 获取当前URI支持的方法 1.1
TRACE 追踪路径 1.1
CONNECT 需要使用隧道协议连接代理 1.1
LINK 建立和资源之间的联系 1.0
UNLINE 断开与资源的联系 1.0

HTTP报文

HTTP通讯过程中用来交互传输数据的信息称之为报文,请求端发出的称之为请求报文,响应端的称之为响应报文,而HTTP的报文则是由多行字符串组合而成,其中回车符和换行符来区分每一个报文的内容及其属性以及报文首部和报文主体,报文整体组成大概如下:

报文组成.png

从上图我们大概可以看出来报文整体通过空行拆分为报文首部和报文主体,并且请求报文和响应报文都是一样的结构,但是我们也可以看出来,请求报文和响应报文都包含通用首部字段和实体首部字段,而请求报文则是多了请求行和请求首部字段,响应报文则是在相同位置变成了状态行和响应首部字段,那么这些组成分别有什么作用呢?

请求行

首先请求行仅仅存在于请求报文中,用来记录当前发起请求的信息,比如URI,请求方法,HTTP版本等

状态行

状态行仅仅存在于响应报文中,内部记录了响应结果的状态码,成功/失败的原因说明,以及服务端返回的HTTP版本等

请求首部/响应首部/通用首部/实体首部

四种最常见的首部一般包含了请求/响应过程中各种属性及其对应的参数值,比如跨域请求,缓存过期时间等属性

其他首部

一般情况下HTTP内置的首部和组成是由上面的四种常见首部和请求行/状态行组成的,但是不要忘记,我们HTTP可以配合Cookie等实现功能,其他首部中包含了其他的非HTTP规范定义的首部信息比如Cookie首部信息等

HTTP首部

上面我们分析了报文的组成部分,其中有四个首部是最常见的首部,分别是请求首部、响应首部、通用首部和实体首部,那么我们看下四种首部分别是用来做什么的,以及HTTP规范给我们定义了那些首部字段:

通用首部

所谓通用首部,即请求报文和响应报文都可以配置的首部字段,一般都是超时时间、缓存等这些通用首部字段,HTTP规范定义的通用首部字段共9个,如下:

首部字段 说明
Cache-Control 缓存行为控制
Connection 跳转首部、连接的管理
Date 创建报文的日期时间
Pragma 报文指令
Trailer 报文末端首部一览
Transfer-Encoding 报文主体的传输编码方式
Upgrade 升级为指定的其他协议
Via 代理服务器相关信息
Warning 错误信息通知

请求首部字段

当客户端发起请求的时候特有的首部,可用来指定请求的方式,请求的策略等,HTTP定义的请求首部字段共19个,如下:

首部字段 说明
Accept 用户代理可以处理的媒体类型
Accept-Charset 优先的编码字符集
Accept-Encoding 优先的内容编码
Accept-Language 优先的语言
Authorization web认证信息
Expect 期望服务端的行为
From 邮箱地址
Host 请求资源所在服务器
If-Match 比较实体标记(ETag)
If-Modified-Since 比较资源的更新时间
If-None-Match 比较实体标记(与 If-Match 相反)
If-Range 资源未更新时发送实体 Byte 的范围请求
If-Unmodified-Since 比较资源的更新时间(与If-Modified-Since相反)
Max-Forwards 最大传输逐跳数
Proxy-Authorization 代理服务器要求客户端的认证信息
Range 实体的字节范围请求
Referer 对请求中 URI 的原始获取方
TE 传输编码优先级
User-Agent HTTP 客户端程序信息

响应首部字段

HTTP交互过程中,服务端返回给客户端的时候使用的首部,包含服务端信息、重定向等,在HTTP中定义的有9种,如下:

首部字段 说明
Accept-Ranges 是否接受字节范围请求
Age 推算资源创建经过时间
ETag 资源的匹配信息
Location 令客户端重定向至此URI
Proxy-Authenticate 代理服务器对客户端的认证信息
Retry-After 再次发起请求的时机要求信息
Server 服务器的安装信息
Vary 代理服务器缓存的管理信息
WWW-Authenticate 服务器对客户端的认证信息

实体首部字段

实体首部字段是HTTP交互过程中,为了传输实体而出现的属性配置,HTTP指定了10种实体首部,如下:

首部字段 说明
Allow 当前资源支持的请求方式
Content-Encoding 实体的编码方式
Content-Language 实体对应的语言
Content-Length 实体传输需要的byte大小
Content-Location 用来替代资源的URI
Content-MD5 实体的报文摘要
Content-Range 实体主体的传输位置范围
Content-Type 实体主体的媒体类型
Expires 实体主体过期时间
Last-Modified 资源的最后修改时间

而其中有部分首部在日常中经常使用,下面我们就看看几个常见的首部字段以及可选值:

Cache-Control:缓存行为控制

可用的值如下(请求):

属性 参数 说明
no-cache 强制要求服务端请求真实数据进行返回
no-store 强制要求请求和响应不做任何数据缓存
max-age 秒(必须) 可以保持响应的最大时间
max-stale 接受已经过期的响应(xxx秒内的响应)
min-fresh 秒(必须) 缓存过期,但是在指定时间内客户端依然认可此数据
no-transform 代理不可更改媒体类型
only-if-cached 仅仅选择从缓存获取数据,没有即504
cache-extension 从新标记获取数据(token)

可用的响应值如下:

属性 参数 说明
public 公开对任何客户端都有效的缓存
private 仅针对指定客户端缓存有效,彼此客户端不干扰
no-cache 每次都确认数据是否有效
no-store 请求或者响应强制都不缓存数据
no-transform 代理不可更改媒体类型
must-revalidate 可缓存但必须再向源服务器进行确认
proxy-revalidate 要求中间缓存服务器对缓存的响应有效性再进行确认
max-age 秒(必须) 可以保持响应的最大时间
s-maxage 秒(必须) 公共缓存服务器响应的最大Age值
cache-extension 新指令标记(token)

Warning:错误信息通知

在HTTP1.1中定义的警告码如下:

警告码 警告内容 说明
110 Response is stale(响应已过期) 代理返回已过期的资源
111 Revalidation failed(验证失败) 代理在验证资源有效性时失败(服务器无法到达)
112 Disconnection operation(断开连接操作) 代理与互联网连接被故意切断
113 Heuristic expiration(试探性过期) 响应的使用期超过24小时(有效缓存时间在24h以上的情况)
199 Miscellaneous warning(杂项警告) 任意的警告
214 Transformation applied(使用了转换) 代理对内容编码或媒体类型等执行了转换等操作
299 Miscellaneous persistent warning(持久杂项警告) 任意的警告

Accept:用户代理可以处理的媒体类型

Accept属性可以通知服务器,用户代理能接受/处理的媒体文件类型以及优先级,常见的如下:

媒体文件类型 可选值(部分)
文本文件 text/html, text/plain, text/css,application/xhtml+xml, application/xml
图片文件 image/jpeg, image/gif, image/png
视频文件 video/mpeg, video/quicktime
二进制文件 application/octet-stream, application/zip

其中Accept还可以指定优先级,使用q=来额外表示权重值,用分号分割,权重范围0-1,1为最大值,可以为三位小数,不指定权重时默认q=1.0

Accept-Encoding:优先的内容编码

Accept-Encoding用来通知服务器用户代理支持的内容编码及优先级顺序,常见的编码如下:

编码类型 说明
gzip 传输数据由文本压缩程序gzip生成对应编码数据
compress 由 UNIX 文件压缩程序 compress 生成的编码格式,采用 Lempel-Ziv-Welch 算法(LZW)
deflate 组合使用 zlib 格式(RFC1950)及由 deflate 压缩算法生成的编码
identity 不执行任何压缩等操作的默认编码

HTTP状态码

了解了HTTP的报文组成和组成HTTP请求/响应的首部信息,在完成了HTTP请求后,我们会根据响应报文来确定用户操作是否规范,而依据则是响应报文中很重要的一部分--HTTP状态码

而在HTTP规范中,响应码以三位数的短数值和简要的信息表示,而三位数值的第一位数(百位数)决定了响应的类型,后两位数则是当前类型的具体子类型,目前HTTP中的code主要分为以下五种:

响应code 类型 响应原因短语
1XX Informational(信息性状态码) 接收的请求正在处理
2XX Success(成功状态码) 请求正常处理完毕
3XX Redirection(重定向状态码) 需要进行附加操作以完成请求
4XX Client Error(客户端错误状态码) 服务器无法处理请求
5XX Server Error(服务器错误状态码) 服务器处理请求出错

而这五大类型的具体子类在RFC2616 上的 HTTP 状态码就达 40 种,后续扩展了数十种,而在我们开发过程中,绝大多数几乎见不到,所以接下来我们看看开发中能碰到的十来种常见的状态码

200:完全成功

当HTTP的返回码为200的时候,恭喜你,整个流程没有出现任何问题,完全成功!说明客户端的请求信息被服务端正常处理并且成功返回给了客户端

204:Not Content

当前的状态码代表客户端的请求已经被服务端处理完毕,并且服务端也给了响应信息,但是当前的返回信息中不包含响应信息主体

206:Partial Content

当前返回码比较特殊,因为只会出现在客户端的请求是部分范围请求并且成功响应的时候,比如报文中包含 Content-Range 进行部分范围请求的操作

301:Moved Permanently

当前响应码代表着请求的资源已经被永久性重定向,即我们请求的资源在服务端的URI已经变更,服务端期望我们使用Loaction首部字段等方式保存新的URI地址,后续使用当前URI进行访问

302:Found

当前响应码和301有类似的地方,即都是请求资源被重定向了,但是区别在于301是请求的资源URI的永久性移动变更,而302响应码代表着当前请求的时候这个资源的URI被变更了,但是这个变更只是临时的,仅仅本次请求期望使用变更后的URI请求

303:See Other

如果是302响应码是临时性URI变更的话,那么303就是302的升级版,因为303响应码拥有302响应码的全部功能,但是更严格的是,303告诉你URI对应的资源中存在其他URI,并且严格要求你使用GET方式去重新访问新的URI

307:Temporary Redirect

303作为302的升级版已经出现了,但是在严格程度上还远远不够,因为按照HTTP规范,302,303的响应码都是希望用户不要把POST请求变为GET,但是不能束缚浏览器或者用户行为,而307作为严格的执行者,只要响应了当前的code,浏览器会遵照约定,不能把POST请求变成GET去访问资源

400:Bad Request

400响应码可能是最诡异的响应码之一了,开发的过程中需要格外注意当前的响应码,因为当前响应码虽然是告诉我们报文请求中存在着语法错误,但是浏览器会按照200对待此响应

401:Unauthorized

当响应401响应码的时候,即代表服务端会发起质询的方式的一个认证请求,告诉我们需要进行认证才可以访问,这个时候我们需要将信息填写并确定才可以拿到具体的结果

403:Forbidden

403响应码在开发的时候也是经常出现,经常让人琢磨不透原因,因为此响应码出现即代表我们的请求被服务端拒绝访问了,但是服务端经常不会返回给具体的响应实体描述

404:Not Found

404作为开发过程中接触最多的响应码之一,对此已经非常熟悉,此响应码代表服务端无法找到具体的请求资源

501:Internal Server Error

501状态码表明当前服务端在执行过程中发生了错误,也可能是某个流程突发bug

503:Service Unavailable

503状态码代表我们请求服务端的过程中,服务端可能超过了负载或者服务端的机器已经宕机又或者正在停服更新状态等

BASIC 认证

上面有分析开发和日常使用过程中最常见的十数种响应码及其含义,其中401是代表我们需要经过认证,那么接下来我们了解下HTTP协议给我们提供的几种认证方式吧,其中HTTP1.1版本默认支持的认证方式有如下:

BASIC:基础认证、DIGEST:信息摘要认证、SSL:基于ssl协议的客户端认证、FormBase:基于表单的认证方式

那么我们从BASIC认证开始,此认证是HTTP1.0就开始存在的认证方式,比较古老,现在的web开发过程中基本已经不使用此方式,但是一些古老的工程依然存在(比如我之前的某企业的工程),其认证过程大概如下:

1).发起请求,服务端返回401,并且返回带 WWW-Authenticate 首部字段的响应

2).客户端接受到401响应后,将用户ID和密码直接明文方式填写,而发送的字符串会以id:密码进行组合后,经过base64编码为字符串发送


Basic认证.png

需要注意的是此认证方式弊端很多,首先是明文填写,发送的过程中也没有加密,仅仅是组合+base64进行编码,最重要的是此认证方式基本上没有注销的选项,所以基本没人使用了

DIGEST 认证

为弥补 BASIC 认证存在的弱点,从 HTTP/1.1 起就有了 DIGEST 认 证。DIGEST 认证同样使用质询 / 响应的方式 ,但是不会和BASIC一样直接发送明文密码,请求过程如下:

1).发送请求,服务端返回401,并且返 回带 WWW-Authenticate 首部字段的响应 ,此字段中包含响应方式对应的质询码(首部字段 WWW-Authenticate 内必须包含 realm 和 nonce 这两个字段的 信息。客户端就是依靠向服务器回送这两个值进行认证的)

2).此时客户端接受到401的响应以后,可以拿到DIGEST认证需要的相关信息,比如realm和nonce,而完成此认证还需要username和uri,这部分内容填写完毕后开始进行计算发送给服务端(这部分进行了加密计算,比较复杂,可以参考 RFC2617 )

3).接受到到包含首部字段 Authorization 请求的服务器,会确认认 证信息的正确性。认证通过后则返回包含 Request-URI 资源的响应

digest认证.png

但是需要注意的是,此认证虽然改善了basic认证的明文问题,但是不能防止客户端被攻击或者伪造的情况,使用起来也不能定制化,所以和basic一样,很少有人使用

SSL 客户端认证

SLL认证是借助HTTPS的客户端证书完成用户身份认证,服务端也可以确认当前请求的客户端是不是符合规范的客户端,此种方式一般都是选择HTTPS,是目前主流的安全的认证方式之一,认证步骤大概如下:

1).接受到客户端的请求后,服务端发送送 Certificate Request 报文,要求客户端提供客户端证书

2).用户在客户端选择证书,客户端会把证书信息发送给服务端

3).服务端会对证书进行验证签名等操作确定客户端证书的有效性,通过后会按照证书内部的公开密钥开始进行HTTPS通信

表单认证

HTTPS虽然是能保证相对的安全性,但是在使用和操作上繁琐,并且更重要的一点是,证书是要钱的,在企业开发中,一般只会给比较私密的接口或者服务比如交易等进行HTTPS通信,其他部分基本也不会选择HTTPS,这个时候就需要一种自定义的安全扩展认证方式,而HTTP给我们提供了表单认证,也是目前互联网企业使用最多的方式

使用表单认证基本是企业内部自己开发和定义具体的认证响应流程,而具体使用仅仅是在网页提供表单填写相关信息,提交给服务端,服务端根据自定义的规则解析或者解密并响应,此种方式主要在于灵活,比如表单填写的信息可任意,文本形式可以任意,而请求过程可以自定义也可以选择表单的submit提交,服务端灵活度更高,是目前主流并且推荐的认证方式

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容