python接口测试

一、接口测试

1、关注点

功能:功能实现,实现与设计一致, 接口通过性测试(数据交换,逻辑间的控制)

健壮性: 边界值,容错性

性能: 并发及压测

稳定性: 长期运行的稳定性

安全性: SQL注入, session依赖, 数字签名, http接口的安全性

2、常见接口种类

Http/Https接口: 通过http/https协议传送接口数据(通常按字符串/二进制传输), 如常见的网页表单, https安全性更好

RESTful Api: REST表述性状态传递. 一种设计风格,基于http/https协议, 把一切接口视为资源, 接口要分版本,在统一的域名下管理, 不同的方法(get/post..)做不同的事,通常请求及响应使用json格式

Web Service: SOAP简单面向对象协议, 基于http实现的一种RPC方案.接口返回一些对象,可以直接通过操作对象,实现我们需要的业务处理.使用xml格式传输数据

RPC接口: RPC为远程方法调用, 主要应用于分布式系统的不同服务器的方法,有不同的实现方案,基于TCP/Http协议的都有. RPC可以想 我们本地导入和调用对象一样使用. Dubbo接口也是一种RPC接口. 

3、常见接口数据类型 

请求数据类型(Content-Type):

application/x-www-form-urlencoded: 常规只有文本的网页表单

application/json: RESTful Api常用格式, 结构清晰, 含有多层嵌套

multipart/form-data: 既有文本,又有上传文件或富文本框的混合数据表单

text/xml: xml格式, RPC接口常用格式,Dubbo接口常用格式

test/html: html格式

响应数据类型

string/html: 返回字符串或网页源码

json: RESTful Api常用响应格式, 结构清晰

xml: RPC接口常用格式

4、常见接口安全验证方式

Auth_1.0/Auth_2.0: 通用接口授权方式

Session依赖: 需要登录之后才能进行接口操作

Token验证: 先要使用自己的appid/appsecret通过获取token接口验证身份获取一个token(令牌,有一定有效期), 然后带着token访问接口

数字签名: 将原本的参数按一定规则进行组合,配合时间戳或appsecret, 通过加密算法生成一个签名sign, 携带签名进行接口请求

5、常见接口请求方法

GET: 获取资源

POST: 修改资源

PUT: 上传资源

DELETE: 删除资源

6、常见状态码(RESTful规范)

1** 信息,服务器收到请求,需要请求者继续执行操作

2** 成功,操作被成功接收并处理

3** 重定向,需要进一步的操作以完成请求

4** 客户端错误,请求包含语法错误或无法完成请求

5** 服务器错误,服务器在处理请求的过程中发生了错误

200 OK - [GET]:获取资源成功

201 CREATED - [POST/PUT/PATCH]:创建/修改成功

202 Accepted - [*]:任务接受

204 NO CONTENT - [DELETE]:删除成功

301 Moved Permanently: 永久重定向

302 Found: 临时重定向

400 INVALID REQUEST - [POST/PUT/PATCH]:用户请求错误

401 Unauthorized - [*]:没有权限(鉴权失败, 接口层)

403 Forbidden - [*] 资源禁止访问(服务器层,没有访问权限)

404 NOT FOUND - [*]:资源不存在(有可能是请求url错误或参数不正确)

405 Method Not Allowd: 访问的方法不允许, 如用POST访问只支持GET请求的接口

406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)

410 Gone -[GET]:资源被永久删除

422 Unprocesable entity - [POST/PUT/PATCH] 当创建对象时,发生验证错误

500 INTERNAL SERVER ERROR - [*]:服务器发生错误

502: 网关失效

504: 网关请求超时

7、接口业务类型

返回数据型接口: 只从数据库读取数据

业务操作型接口: 需要写数据库(接口测试需要要涉及参数化或环境清理)

8、根据接口文档设计测试点

功能分析: 是否能满足业务(是否缺少某个前端需要的参数), 是否能满足所有业务场景(是否有漏开发接口, 比如只开发了单品接口,没开发套餐接口)

设计分析: 是否有不规范字段(如,nickname, passwd);不规范格式(如sex,用男,女而不是1,2);是否有易混淆字段(如amount和total);是否有单词拼错;是否有和数据库字段对应但名称不一样的(易错)

接口分析: 协议类型(http要考虑安全);请求方法(是否规范);请求编码格式(表单/Json/xml, 很多接口文档不声明,导致测试调试不通);接口授权方式;接口业务类型(关系到是否需要做参数化或环境清理); 返回值类型及结构(关系到怎么断言)

接口依赖: 需要什么环境准备和业务场景, 依赖那些接口, 有那些动态数据, 预备环境怎么保障

参数分析: 各个参数的参数类型,组成规则,是否允许不传,是否可以为空, 是否允许多传参

业务分析: 如price字段必须和数据库中的商品的price字段一致,才能校验通过

非功能性: 接口的技术实现方案是否合理, 能否满足高并发的性能要求, 边界值/极限值的处理是否合适, 是否前后端都有数据格式校验等(如精确度为秒级的订单号生成器,在高并发下会导致生成同一订单号的问题)

其他: 如反爬,对headers的一些限制和校验, ip等限制

9、编写接口测试用例

单接口用例: 正常数据/边界数据/异常数据(健壮性)/并发(一致性)/性能/安全性(抓包截取伪造/SQL注入/跨域请求)

场景用例: 列出常见的用户场景, 用接口进行覆盖, 业务场景压测(寻找某个环节的性能瓶颈)

10、接口测试点

业务功能测试:正常场景、异常场景

边界测试:业务规则边界测试

                 输入输出边界值测试:必选参数;可选参数;参数有无null;参数顺序,个数,类                       型;参数类型数值大小,输入数值的范围;参数字符串长短;参数特殊字符

参数组合测试

异常情况测试:幂等(重复提交),并发测试,事务测试,分布式测试,环境异常,大数量测                           试(同时处理大批量数据,DB数量很大时,处理----并发和并行)

性能测试:响应时间,吞吐量,并发数,服务器资源使用率(cpu,内存,IO,network)

安全测试:敏感信息是否加密(前后端数据加密,日志加密),sql注入

11、接口测试掌握点

了解OSI网络模型,TCP/UDP协议,掌握HTTP/HTTPS协议,了解RPC, Web Service及REST,理解Session和Cookie

掌握常用的接口测试工具如curl命令,Postman,Jmeter,LR,SoupUI,AB等

掌握基本的抓包工具如Chrome开发者工具,Fiddler,Charles,Wireshark,tcpdumps等

掌握一门编程语言Python或Java

了解Nginx, Apache, Tomcat等服务器中间件

掌握数据库基本查询命令,及一些NoSQL(如Redis)操作,用于检查响应结果

掌握基本的Linux日子查询和筛选命令

12、接口测试重点

动态变量参数化

接口依赖及中间变量问题

异步接口结果验证问题

相应参数及嵌套很多的验证问题

接口测试框架的稳定性问题

资源清理问题

多接口场景测试

二、python接口测试

1、接口调用库,使用第三方包requests

请求方法

requests.get():无参数,有参数使用params = {"key":"ec961279f453459b9248f0aeb6600bbe","info":"你好"}

requests.post()

参数形式1    data = {"name": "hanzhichao", "age": 18} # Post请求发送的数据,字典格式

res = requests.post(url=url, data=data) # 这里使用post方法,参数和get方法一样

参数形式2   data = '''{

        "name": "hanzhichao",

        "age": 18

        }''' # 多行文本, 字符串格式,也可以单行(注意外层有引号,为字符串) data = '{"name": "hanzhichao", "age": 18}'res = requests.post(url=url, data=data) #  data支持字典或字符串

data参数支持字典格式也支持字符串格式,如果是字典格式,requests方法会将其按照默认表单urlencoded格式转换为字符串,如果是字符串则不转化

如果data以字符串格式传输需要遵循以下几点:

必须是严格的JSON格式字符串,里面必须用双引号,k-v之间必须有逗号,布尔值必须是小写的true/false等等

不能有中文,直接传字符串不会自动编码

一般来说,建议将data声明为字典格式(方便数据添加修改),然后再用json.dumps()方法把data转换为合法的JSON字符串格式

requests.delete()

requests.session() # 用来保持session会话,如登录状态

请求参数

url: 接口地址, strurl="http://127.0.0.1:5000/add/"

headers: 请求头, dict headers={"Content-Type": "application/json"}

params: url参数, dict params={"a":"1":"b":"2"}

data: 请求数据, dict data={"a":"1":"b":"2"}

files: 文件句柄, dict files={"file": open("1.jpg")}

timeout: 超时时间,单位s, str, 超过时间会报超时错误```requests.get(url=url,params=params,timeout=10)

响应解析

resp: 响应对象

resp.status_code: 响应状态码

resp.text # 响应文本

resp.json() # 响应转化为json对象(字典)-慎用:如果接口出错或返回格式不是json格式,使用这个方法会报错

resp.content # 响应内容, 二进制类型

2、序列化和反序列化

内存对象---文本、文件  -----序列化

文本---内存对象-------反序列化

3、json格式和字典的区别

json对象,有属性和方法

json文本,是字符串

字典中的引号支持单引号和双引号,JSON格式只支持双引号

字典中的True/False首字母大写,JSON格式为true/false

字典中的空值为None, JSON格式为null

4、json格式序列化

序列化(字典 -> 文本/文件句柄): json.dumps()/json.dump()

反序列化(文本/文件句柄 -> 字典) : json.loads()/json.load()

5、文件使用

f = open("文件",r,模式)

f.close() 

with open file()

打开文件,with不需要关闭,直接打开需要关闭

三、网络基础知识

1、IP地址

查看ip地址命令   window   ipconfig     Linux  ifconfig

2、端口:设备与外界通讯交流的窗口

常见软件默认端口

Apache/Nginx(HTTP服务): 80

Tomcat: 8080

Oracle: 1521

MySQL: 3306

SQL Server: 1433

PostgreSQL: 5432

MongoDB: 27017

Redis: 6379

Memcached: 11211

查看端口命令

Windows: netstat -ano

Linux: netstat -ntlp

3、域名与DNS

DNS:域名解析系统,域名和IP地址相互映射的一个分布式数据库,提供域名转到对应ip的服务

4、OSI七层模型

上三层---应用层,控制软件方面

应用层:文件传输,电子邮件,文件服务,虚拟终端 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet

表示层:数据格式化,代码转换,数据加密

会话层:解除或建立与别的接点的联系(会话)

下四层---数据流层,用来管理硬件

传输层:提供端对端的接口 TCP,UDP

网络层:为数据包选择路由 IP,ICMP,RIP,OSPF,BGP,IGMP

数据链路层 传输有地址的帧以及错误检测功能 SLIP,CSLIP,PPP,ARP,RARP,MTU

物理层 以二进制数据形式在物理媒体上传输数据 ISO2110,IEEE802,IEEE802.2

5、TCP及UDP协议

TCP和UDP都是传输层的协议

TCP:传输控制协议

UDP: 数据报文协议

TCP和UDP的区别

UDP的特点如下:

无链接

UDP使用尽最大努力交付,不保证可靠性

UDP是面向报文的,UDP对应用层交付下来的报文,既不合并,也不拆分,而是保留这些报文的边界。应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文

UDP没有拥塞控制

UDP支持一对一、一对多、多对一和多对多的交互通信

UDP的首部开销小,只有8字节

TCP的特点:

TCP是面向连接的

每条TCP连接只能用于两个断点,一对一

TCP提供可靠交付的服务:连接传输数据、无差错、不丢失、不重复、并且按序到达

TCP提供全双工通信

面向字节流。TCP根据对方给出的窗口和当前网络拥塞的程度来决定一个报文应该包含多少个字节

6、HTTP协议

HTTP:超文本传输协议,是用于从WWW服务器传输超文本到本地浏览器的传输协议。

HTTP协议是一种无状态协议,主要包含请求和相应两大部分:

HTTP协议传输的数据都是未加密的,HTTPS协议是由HTTP+SSL协议构建的可进行加密传输、身份认证的网络协议,要比HTTP协议安全。

HTTPS和HTTP的区别

HTTPS协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。

HTTP是超文本传输协议,信息是明文传输,HTTPS则是具有安全性的SSL加密传输协议。

HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

HTTP的连接很简单,是无状态的;HTTPS协议是由HTTP+SSL协议构建的可进行加密传输、身份认证的网络协议,比HTTP协议安全。

7、请求(Request)

请求是我们发送给接口的数据对象,包含接口地址(URL),请求方法,参数,请求头(Headers), Cookies, 数据等

8、URL编码

URL编码是一种浏览器用来打包请求参数及表单参数的格式, 参数和参数之间使用&分割,非ASCII码使用%加16进制编码替换

数据编码

ASCII码: 单字节,美国信息交换标准码, 包含数字,字母,英文标点及一些控制字符

ISO-8859-1:又称Latin1,单字节,向下兼容ASCII,用于支持部分于欧洲使用的语言

ANSI编码:单字节表示英文,双字节表示汉字,对ASCII的扩展,不同的国家和地区制定了不同的标准,中文中的GBK,GB2312属于ANSI编码

Unicode编码: 采用二个字节编码(英文和中文的字符都以双字节存放),与ANSI码不兼容

UTF-8:是目前互联网上使用最广泛的一种Unicode 编码方式,又称万国码

指定请求数据编码(解决中文乱码):

请求Headers设置Content-Type: application/json; charset=utf-8

Base64: 一种用64个字符来表示任意二进制数据的方法。

Base64编码的作用:由于某些系统中只能使用ASCII字符。Base64就是用来将非ASCII字符的数据转换成ASCII字符的一种方法。

而且base64特别适合在http,mime协议下快速传输数据。

9、GET请求和POST请求的区别

GET请求:

GET请求可被缓存

GET请求保留在浏览器历史记录中

GET请求可被收藏为书签

GET请求不应在处理敏感数据时使用

GET请求有长度限制

GET请求只应当用于取回数据

POST请求:

POST请求不会被缓存

POST请求 不会保留在浏览器历史记录中

POST不能被收藏为书签

POST请求对数据长度没有要求

10、常见Headers

12、Cookie和Session

**Cookie/Cookies: **是指某些网站为了辨别用户身份进行session跟踪储存在用户本地终端上的数据(通常经过加密)。

Session:服务端为客户端访问所建立和维持的会话,通常会生成一个唯一的id,会话有一定的有效期。

由于HTTP是无状态的,即服务器不知道用户上一次做了什么,默认也无法识别用户身份。

比较流行的做法是:

用户访问时服务端建立会话(Session)

将会话id(Session ID)随响应返回,并保存在客户端的Cookies里

后续的访问中,服务器通过辨识,客户端请求时携带的Cookies内容来识别用户

Cookie和Session的区别

cookie是存在客户端(浏览器)的进程内存中和客户端所在的机器硬盘上

cookie只能能够存储少量文本,大概4K大小

cookie是不能在不同浏览器之间共享

Session存在服务器端,存在网站进程的内存中

Session在初次设置session的时候,会在session池中实例化一个session对象,以sessionid 的值作为key,同时会将key以cookie的形式保存到客户端的内存中

Session的作用域只存在当前浏览器的会话中,当浏览器关闭以后就会将sessionid丢失,但是服务器的Session对象要20分钟以后才会回收

13、授权与加密

常见的接口安全策略:

Session/Cookie机制: 即需要登录,登录后可访问各个接口,最常用的一种策略,适用于内部接口。

固定appid模式: 用户注册时会生成一个唯一的appid,用户调用接口时需要携带appid,适用于公开接口,安全性较差。

动态token模式: token即身份令牌,用户访问接口需要使用个人appid临时申请一个token,token有一定有效期,适用于公开接口,安全性较appid模式好。

开放协议: Basic Auth/ Oauth1.0 / Oauth2.0: 适用于开放接口。

数字签名: 将所有请求参数及参数值进行排列拼接,加上用户私钥,再进行Md5或其他加密生成一个请求的签名(sign),请求是需要携带签名,服务器收到请求后,会对请求重新计算签名并核实与请求所携带签名是否一致。安全性较高,可以有效防止请求被篡改。适用于内部接口及微服务接口。

常见的加密算法

在接口数据传输过程中常对一些敏感数据(如密码)进行Base64编码或MD5加密,以增加安全性。

加密算法分为对称式加密算法和非对称式加密算法,对称式加解密使用同一个秘钥,非对称式使用不同的秘钥。

对称式加密

DES: 数据加密标准,速度较快,适用于加密大量数据的场合

AES: 高级加密标准,速度快,安全级别高

非对称式加密

RSA: 是一个支持变长密钥的公共密钥算法, 分公钥和私钥,SSH协议使用该算法

MD5: 最常用的一种加密方法,是一种摘要算法。

14、缓存

HTTP 缓存机制作是 web 性能优化的重要手段,当用户第一次请求服务器资源时,服务器将资源缓存到客户端本地,在一定时间内(缓存有效期内)当用户再次向服务器请求同样的资源时,可以直接从缓存中读取,而不用从服务器下载。

接口测试中缓存相关注意点

在更新或调试接口是,注意是否需要清理缓存(或临时禁用缓存)

缓存有一定的有效期

接口性能测试中会关注缓存的命中率

15、代理

正向代理中, 代理和客户端在一个局域网内,对服务器透明

反向代理中,代理和服务器在一个局域网内,对客户端透明

例如:使用代理访问Google属于正向代理,通过不同的域名通过Nginx向同一台服务器请求不同的网站属于反向代理

反向代理可以做负载均衡

反向代理接口测试一般要先绑定本地hosts

16、Chrome开发者工具

Elements: HTML元素面板,用于定位查看元素源代码

Console: js控制台面板,js命令行,查看前端日志

Group by frame:按框架分组

Preserve log:页面重载时保留请求

Disable cache:禁用缓存

Offline:断网及弱网模拟

Sources: 资源面板,用于断点调试js

Network: 请求信息面板,查看请求及响应信息

Timeline: 时间线面板,记录网站生命周期内所发生的各类事件

Profiles: 事件详情面板

Application: 本地存储,Session存储等资源信息

Secuity: 判断当前网页是否安全

Audits: 网络性能诊断

Filters: 请求过滤器

Overview: 资源时间轴

Requests Table: 请求列表

Name: 资源名称

Status: HTTP状态码

Initiator: 请求源

Size: 从服务器下载的文件和请求的资源大小。如果是从缓存中取得的资源则该列会显示(from cache)

Timeline: 显示所有网络请求时间状态轴

Summary: 请求总数,数据传输量,加载时间信息

DOMContentLoaded:页面上DOM完全加载并解析完毕

load:页面上所有DOM、CSS、JS、图片完全加载完毕

四、Fiddler使用

1、作用

抓到请求数据,查看Raw格式/表单格式/Json/XML格式

可以拦截和修改请求

更强大的过滤器

可以抓取Postman/接口脚本发送的请求,方便调试

可以抓包手机请求 ...

2、Fiddler主界面

Fiddler的主界面分为 工具面板、会话面板、监控面板、状态面板

Inspectors: 检查员

Raw:请求的原始格式

WebForm: 请求的表单格式

Json:请求的Json格式请求

XML:请求的XML格式

AutoResponsder: 自动回复,可用于构造响应,Mock,不修改服务器文件调试接口

Composer: 设计者, 发送和调试请求

FidderScript:

Filters: 过滤器

Hosts: 按服务器过滤

Clients Process: 按客户端程序过滤

Request Headers: 按请求头过滤

Breakpoints: 设置断点

Response Status Code: 按状态码过滤

Response Type and Size: 按响应类型及大学过滤

Response Headers: 按响应头过滤

3、自动断点设置

菜单Rules -> Automatic Breakpoints -> Before Requests/After Requests

五、postman使用

1、环境变量使用方法:

选择环境,在 请求URL或者请求Body里使用{{变量名}}来使用环境变量,变量可以在请求Body的各种格式中使用,但不能直接在请求前脚本(Pre-request Script)和请求后脚本(Tests)中使用

环境管理中还可以点击“Global”添加全局变量,环境变量只有当选择了该环境时生效,全局变量在任何环境中生效,测试集中的变量只在当前测试集生效,当测试集变量,环境变量,全局变量有重复的变量名时,优先级为:环境变量>全局变量>测试集变量

2、Params使用

当请求URL中参数很多时,不方便进行添加和查看,可以点击URL输入框后的Params按钮,以表格的方式添加变量及值,从表格添加后,变量和值会自动添加到URL中

请求设计

授权:如果接口需要授权,可以在该页面设置授权方式(type)和授权信息

Header: 请求头,可以设置请求类型(Content-Type)和Cookie

Body: 请求数据

form-data:混合表单,支持上传文件

x-www-form-urlencoded:文本表单

raw:原始格式,支持JSON/XML格式(后面可选择)

binary: 二进制格式,用于发送二进制数据流

Pre-request Script: 请求前脚本,Javascript语法,用于在发送请求前生成一些动态数据或做一些处理

Tests:请求后脚本,Javascript语法,用于请求返回后做一些处理或断言结果

Tests断言

HTTP状态码断言:

tests["HTTP状态码200"]=responseCode.code==200;

响应包含内容断言:

tests["状态码200"] = responseBody.has("登录成功");

JSON响应断言

varjsonData =JSON.parse(responseBody);tests["code为200"] =jsonData.code==200tests["msg为success"] =jsonData.msg =="success"

Runner: 测试集批量执行

支持设置迭代次数

支持加载csv或json类测试数据

在电脑上新建一个data.csv文件,第一行为变量名,下面是数据

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

推荐阅读更多精彩内容