curl使用指南

curl是一个命令行工具,通过指定的URL来上传或下载数据,并将数据展示出来。curl中的c表示client,而URL,就是URL。这里我们介绍一下curl的使用。

1. 命令行基础

1.1 命令行选项

1. 短形式

在curl中可以使用短形式的命令行选项,比如通知curl打开verbose模式,可以使用-v选项:

$ curl -v www.baidu.com

这里,-v就是短形式的选项,我们使用一个中划线(-)并紧跟着一个字母来指定一个短形式的选项。

在这个例子中,-v就像一个开关一样,指定某个变量是false还是true。我们可以在一个中划线后面跟多个单字母的选项:

$ curl -vL www.baidu.com

在curl中,命令行解析器总是解析整个命令行,因此,选项可以放在整个命令行的任何位置:

$ curl www.baidu.com -vL

这个和上面的命令具有同样的效果。当然,虽然是任何位置,但不能放在curl前面啊:

$ -vL curl www.baidu.com    // No command '-vL' found

2. 长形式

单个字母的选项敲起来和用起来都很方便,但是字母的个数有限而需要控制的东西又太多,这个时候就可以使用选项的长形式。同样,为了使得命令便于阅读,绝大多数短形式都有一个对应的长形式。

和短形式不同的是,长形式的选项使用两个中划线(--)指定,然后紧跟着具体的选项。还有就是,在使用长形式的时候,后面只能跟一个选项。对于-v,对应的长形式如下:

$ curl --verbose www.baidu.com

同样,长形式的选项也可以出现在命令的任何地方:

$ curl www.baidu.com --verbose

对于-vL来说,对应的长形式可以是:

$ curl --verbose --location www.baidu.com

或者:

$ curl --location www.baidu.com --verbose

3. 选项的参数

在上面的命令中选项-v(或--verbose)以及-L(或--location)都是bool类型的标志位,来告诉curl打开或关闭某些特征。curl还有一种类型的选项,就是需要传递一些参数。比如,如果想在一个HTTP POST中向服务器传递一个字符串:

$ curl -d arbitrary http://example.com

同样,也可以使用相应的长形式:

$ curl --data arbitrary http://example.com

4. 参数有空格?

在上面的例子中,我们的参数arbitrary是一个连续的字符串,但是当我们需要传递一个有空格的参数怎么办?比如Are you OK?,这时我们需要使用引号把参数括起来:

$ curl -A "Are you OK?" http://example.com

如果不加引号的话:

$ curl -A Are you OK? http://example.com

那么curl只会把Are当做用户的参数,剩下的字符,youOK?会被curl当做额外的URL处理,因为这里并没有用-指定这是一个选项。

但是如果参数本身有引号的时候怎么办?这在使用JSON传递参数的时候尤其常见,我们可以使用单引号把参数括起来(不过在Windows中不管用):

$ curl -d '{"name":"fool"}' http://example.com

当数据很多时,我们可以指定一个文件,来传递给curl:

$ curl -d @params.json http://example.com

5. Say No

对于像-t-L之类的标志选项,我们可以在长形式的前面加上no-前缀来指定关闭相应的特征,比如关闭verbose模式:

$ curl --no-verbose http://example.com

1.2 URL

curl支持在一个命令行中处理多个URL,中间用空格间隔即可。curl会对传进来的URL做简单的验证,而不会去验证URL是否真正有效,因此,这里需要使用者提供有效的URL。

前面说过,curl首先解析整个命令行,将得到选项应用于所有的URL上。如果想对每一个URL使用不同的选项,那么可以使用--next来指定。比如:

$ curl --location http://example.com/1 
    --next --data sendthis http://example.com/2 
    --next head http://example.com/3

1. 配置文件

如果选项过多,导致命令很难输入,或者超过了系统命令最大长度的限制,我们可以使用配置文件(config file)来指定curl的选项。

通过使用-K--config选项来告诉curl从指定的文件中读取选项,比如:

$ curl -K curl.options http://example.com

在文件curl.options中,列出所有需要的选项:

# ask curl to follow redirects
--location
# ask curl to do a HEAD request
--head

和在命令行中一样,在配置文件中也可以使用长形式或短形式,甚至在配置文件中对于长形式可以省略那两个中划线(--):

# ask curl to follow redirects
location
# ask curl to do a HEAD request
head

对于使用参数的选项,同样可以使用配置文件:

# ask curl to change the User-Agent in HTTP header
user-agent "something-is-an-agent"

既然叫做配置文件,那么上面的选项也可以写作:

# ask curl to change the User-Agent in HTTP header
user-agent = "something-is-an-agent"

甚至可以省略没有空格的参数的引号:

# ask curl to change the User-Agent in HTTP header
user-agent = something-is-an-agent

当然,如果参数中有空格的话就不能省略引号了。

2. 开始使用curl

在前面我们简单地介绍了什么是curl以及一些基础的命令行知识。我们通过命令行的方式将需要处理的URL交给curl去处理。

在这里,我们开始着手使用curl,了解curl能做什么以及如何去做。

2.1 Verbose模式

如果curl得到的结果不是期望的结果,我们可以使用-v--verbose进入Verbose模式获取更多的信息。

1. 查看通信过程

在Verbose模式中,curl会得到更多的对话式信息,帮助我们了解发生了什么。curl会在每一个信息前面加上*进行标识。在下面的例子中,我们将百度的首页保存下来(使用-o选项并指定参数baidu):

$ curl -v www.baidu.com -o baidu

我们可以得到如下的信息:

* About to connect() to www.baidu.com port 80 (#0)
*   Trying 14.215.177.39... connected
* Connected to www.baidu.com (14.215.177.39) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: www.baidu.com
> Accept: */*
>
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
< Connection: Keep-Alive
< Content-Length: 2381
< Content-Type: text/html
< Date: Fri, 14 Sep 2018 09:55:18 GMT
< Etag: "588604dd-94d"
< Last-Modified: Mon, 23 Jan 2017 13:27:57 GMT
< Pragma: no-cache
< Server: bfe/1.0.8.18
< Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
<
{ [data not shown]
103  2381  103  2381    0     0   113k      0 --:--:-- --:--:-- --:--:--  232k* Connection #0 to host www.baidu.com left intact

* Closing connection #0

下面的信息是建立一个链接:

* About to connect() to www.baidu.com port 80 (#0)
*   Trying 14.215.177.39... connected
* Connected to www.baidu.com (14.215.177.39) port 80 (#0)

然后就是本次的HTTP请求:

> GET / HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: www.baidu.com
> Accept: */*
>

接下来是数据的传输过程。然后就是响应:

< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
< Connection: Keep-Alive
< Content-Length: 2381
< Content-Type: text/html
< Date: Fri, 14 Sep 2018 09:55:18 GMT
< Etag: "588604dd-94d"
< Last-Modified: Mon, 23 Jan 2017 13:27:57 GMT
< Pragma: no-cache
< Server: bfe/1.0.8.18
< Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
<

连箭头都不一样了。

2. 更详细的信息

如果觉得使用-v的信息还不够的话,还可以使用--trace [filename]选项来讲完整的流保存到filename中。比如:

$ curl --trace dump www.baidu.com

之后,就可以发现一个新的文件dump,里面保存着刚才那个会话的所有信息:

== Info: About to connect() to www.baidu.com port 80 (#0)
== Info:   Trying 14.215.177.39... == Info: connected
== Info: Connected to www.baidu.com (14.215.177.39) port 80 (#0)
=> Send header, 166 bytes (0xa6)
0000: 47 45 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a GET / HTTP/1.1..
0010: 55 73 65 72 2d 41 67 65 6e 74 3a 20 63 75 72 6c User-Agent: curl
0020: 2f 37 2e 31 39 2e 37 20 28 78 38 36 5f 36 34 2d /7.19.7 (x86_64-
0030: 72 65 64 68 61 74 2d 6c 69 6e 75 78 2d 67 6e 75 redhat-linux-gnu
0040: 29 20 6c 69 62 63 75 72 6c 2f 37 2e 31 39 2e 37 ) libcurl/7.19.7
0050: 20 4e 53 53 2f 33 2e 32 37 2e 31 20 7a 6c 69 62  NSS/3.27.1 zlib
0060: 2f 31 2e 32 2e 33 20 6c 69 62 69 64 6e 2f 31 2e /1.2.3 libidn/1.
0070: 31 38 20 6c 69 62 73 73 68 32 2f 31 2e 34 2e 32 18 libssh2/1.4.2
0080: 0d 0a 48 6f 73 74 3a 20 77 77 77 2e 62 61 69 64 ..Host: www.baid
0090: 75 2e 63 6f 6d 0d 0a 41 63 63 65 70 74 3a 20 2a u.com..Accept: *
00a0: 2f 2a 0d 0a 0d 0a                               /*....
<= Recv header, 17 bytes (0x11)
0000: 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d HTTP/1.1 200 OK.
0010: 0a                                              .
<= Recv header, 22 bytes (0x16)
0000: 41 63 63 65 70 74 2d 52 61 6e 67 65 73 3a 20 62 Accept-Ranges: b
0010: 79 74 65 73 0d 0a                               ytes..

文件的前21行如上所示。每一个发送和接收的数据以十六进制的形式保存起来了,方便以后的分析。

如果觉得十六进制没啥帮助的话,可以使用--trace-ascii [filename]选项:

$ curl --trace-ascii dump www.baidu.com

结果如下:

== Info: About to connect() to www.baidu.com port 80 (#0)
== Info:   Trying 14.215.177.38... == Info: connected
== Info: Connected to www.baidu.com (14.215.177.38) port 80 (#0)
=> Send header, 166 bytes (0xa6)
0000: GET / HTTP/1.1
0010: User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7
0050:  NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
0082: Host: www.baidu.com
0097: Accept: */*
00a4:
<= Recv header, 17 bytes (0x11)
0000: HTTP/1.1 200 OK
<= Recv header, 22 bytes (0x16)
0000: Accept-Ranges: bytes
<= Recv header, 76 bytes (0x4c)
0000: Cache-Control: private, no-cache, no-store, proxy-revalidate, no
0040: -transform
<= Recv header, 24 bytes (0x18)
0000: Connection: Keep-Alive
<= Recv header, 22 bytes (0x16)
0000: Content-Length: 2381

上面就是前21行的输出。

3. Silence

verbose模式的对立模式,就是silence,可以使用-s--silence选项来告诉curl不输出任何程序的信息或者错误信息,但也会输出响应的结果。

如果需要在有错误的时候输出错误信息,可以使用-S--show-error来指定。

2.2 浏览器到curl

别人使用浏览器发起了一个请求之后,如果自己想用curl再来一次同样的请求,这里日常的工作中是比较常见的一个操作。在curl中,有没有什么比较简便快捷的方式来获得curl命令呢?

Chrome浏览器和Firefox浏览器都实现了复制成curl的工具,可以将浏览器的请求快速复制成curl命令,非常方便快捷。

1. Chrome

在Chrome中,打开More tools->Developer模式,选择Network选项卡,然后就可以看到所有的请求,选中相应的请求,右键就有Copy as cURL选项,单击就可以了。

2. Firefox

在Firefox中,打开Web Developer->Network工具,然后右键想要复制的链接,就有一个Copy as cURL的选项,单击就可以了。

3. HTTP与curl

与curl一起使用的协议,最多的还是HTTP,这里就将介绍如何有效地使用curl来发送HTTP请求。

3.1 HTTP方法

在每一个HTTP请求中,都有一个对应的方法,常用的方法有:GETPOSTHEADPUT

如果在一个curl命令中不指定具体的方法,那么默认的就是使用GET方法。对于其它的方法,可以在curl命令中指定:

method option
POST -d-F
HEAD -I
PUT -T

3.2 Header

在curl中,使用-i选项可以显示Response的Header信息,连同Body数据:

$ curl -i www.baidu.com

结果:

HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
Connection: Keep-Alive
Content-Length: 2381
Content-Type: text/html
Date: Mon, 17 Sep 2018 10:26:42 GMT
Etag: "588604dd-94d"
Last-Modified: Mon, 23 Jan 2017 13:27:57 GMT
Pragma: no-cache
Server: bfe/1.0.8.18
Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/

<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta
http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=styleshee
t type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就
知道</title></head> <body link=#0000cc> ... </body> </html>

使用-I选项可以只显示Response的Header信息:

$ curl -I www.baidu.com

3.3 POST

POST是HTTP中向服务端提交数据的一种方法。在浏览器中,但在表单中填写完数据后,浏览器就会默认将填写的数据使用key=value串的形式进行转化。在curl中,我们可以使用-d--data选项来指定具体的数据:

$ curl -d key1=value1&key2=value2 http://example.com

我们也可以使用多个-d选项来指定多组数据,curl会自动把这些数据连接起来,因此上面的例子还可以这样:

$ curl -d key1=value1 -d key2=value2 http://example.com

当然,如果数据过多,我们还可以把数据放在一个文件中:

$ curl -d @filename http://example.com

1. Content-Type

当使用POST方法提交数据时,对于提交的数据主要有如下四种形式:

  • application/x-www-form-urlencoded:默认的形式,即key1=value1&key2=value2的形式;
  • multipart/form-data:使用表单上传文件时使用这个形式;
  • application/json:提交JSON格式的数据;
  • text/xml:提交XML格式的数据。

Content-Type是一个Header,如果不指定的话,那么默认就是使用application/x-www-form-urlencoded形式传输数据,当需要使用别的形式进行数据传输的话,那么就需要指定这个Header:

$ curl -d '{I Am A JSON FORM}' -H 'Content-Type: application/json' http://example.com

其中,-H就是用来指定一个具体的Header的选项,值就是key=value 的形式。当需要指定其它的Header,可以使用-H选项。

2. POST一个二进制数据

在curl中,我们也可以提交一个文件,可以使用--data-binary选项来指定一个文件:

$ curl --data-binary @filename http://example.com

3. 转化成一个GET

使用-G-get选项,可以把一个POST请求转化成一个GET请求。如果有-d选项指定的参数,那么curl就会把-d后面的数据添加到URL的后面,用?连接。比如:

$ curl -d "key1=value1" -G http://example.com

得到的请求URL就是:

http://example.com/?key1=value1

4. URL编码

如果使用的数据没有编码,那么可以指定curl来帮助自己进行编码。这时可以使用--data-urlencode选项来指定。比如:

$ curl --data-urlencode "name=Alan Walker" http://example.com

5. multipart formposts

如果一个HTTP POST具有如下形式的表单:

<form action="submit.cgi" method="post" enctype="multipart/form-data">
    Name: <input type="text" name="person"><br>
    File: <input type="file" name="secret"><br>
    <input type="submit" value="Submit">
</form>

用户可以在Name中填写名字,在File中选择一个文件,然后单击Submit按钮提交数据。

为了可以在curl中模拟这个请求,我们可以使用-F--form选项来指定数据:

$ curl -F person=annonymous -F secret=@filename http://example.com/submit.cgi

在上面的表单中,action指定了这个请求发送到哪里;method指定这是一个POST请求;而enctype指定了这是一个multipart formpost。

当执行上面的curl命令后,curl会产生如下的请求头:

POST /submit.cgi HTTP/1.1
Host: example.com
User-Agent: curl/7.46.0
Accept: */*
Content-Length: 313
Content-Type: multipart/form-data; boundary=------------------------d74496d66958873e

其中Content-Type是和enctype一致的。

当使用-F选项时,默认的Content-Type就是multipart/form-data,不过,我们也可以使用-H进行指定:

$ curl -F 'name=Dan' -H 'Content-Type: multipart/magic' https://example.com

6. -d vs -F

在前面我们介绍了使用-d构造一个基本的POST请求,和-F构造一个multipart formpost请求。那么这两个选项有啥区别以及什么时候使用呢?

这两个选项都是把指定的数据发送到服务器上,区别在于数据传递的格式。大多数时候,接收端来指定希望客户端发送数据的格式,客户端不能随意自己指定格式。

  • HTML表单

当使用HTML表单时,会使用<form>标签指定一个表单,这会让浏览器使用POST方法。如果标签中含有enctype=multipart/form-data,这意味着使用multipart formpost方式,在curl中就是使用-F选项。一个典型的场景就是表单中含有<input type=file>标签。

  • 不用HTML表单

POST方法不一定非要在HTML中,在好多的service、APIs中,也可以使用POST请求。

如果这些service期望使用JSON或者其它类似的格式的数据,那么这就是一个普通的POST请求。在curl中就可以使用-d选项。不过要注意-d的默认Content-Type是不是期望的格式,如果不是的话,可以使用-H进行更改。

3.4 HTTP重定向(redirect)

重定向是HTTP协议中的一个基础部分。在重定向中,服务器给客户端的并不是客户端想要的内容,而是一个车具体的指令,告诉客户端如果想获取想要的数据,应该到哪里去请求。

但不是所有的重定向都一样。重定向之后的请求使用什么方法呢?重定向多久呢?

所有的重定向都会返回Location:的Header,来指定一个新的URL。

1. curl:redirect

在curl中,默认不会重定向,可以使用-L--location选项来告诉curl重定向:

$ curl -L http://example.com

2. GET还是POST

第一次请求后,服务器会告诉客户端下一次请求需要使用的方法。关于重定向的响应码如下:

Method Permanent Temporary
切换到GET 301 302和303
使用第一次请求的方法 308 307

我们可以指定curl在重定向时使用什么方法。如果我们第一次请求使用的不是GET方法,重定向后也不希望curl默认使用GET方法,那么我们可以使用--post301,--post302--post303选项来指定。

3.5 修改HTTP请求

每一个请求都有一个请求行、一些请求头和可选的请求体,这里我们看看在curl中可以具体修改的部分,包括请求行和请求头。

1. 请求方法

在请求行中包含这次请求所使用的方法。我们使用下面的简单命令就可以进行一个GET方法:

$ curl http://example.com/file

这会生成如下的请求行:

GET /file HTTP/1.1

HTTP方法中我们可以通过具体的选项指定使用什么方法。这里我们也可以使用-X选项来进行指定:

$ curl -X POST http://example.com

2. 修改请求头

在curl中,我们可以使用-H--header选项来指定Header。之前我们就使用-H指定了Content-Type,其实Header就是一个key: value对:

$ curl -H "HeaderName: HeaderValue" http://example.com

3. Referer

我们还可以在curl通过--referer选项来指定我们是从哪里跳转过来的:

$ curl --referer http://fromexample.com http://toexample.com

4. User Agent

这个字段是用来表示客户端的设备信息的,服务器会根据这个字段,针对不同的设备,返回不同格式的网页。在curl中,可以使用--user-agent选的来指定:

$ curl --user-agent "[User Agent]" http://example.com

3.6 Cookies

HTTP是一种无状态的协议,为了在会话中保存一些状态,可以使用Cookies。服务器通过Set-Cookie:来设置Cookie,客户端就可以在下一次请求中携带这些数据。

1. 设置Cookie

我们可以使用--cookie选项来设置一个Cookie:

$ curl --cookie "CookieName=CookieValue" http://example.com

2. 从文件中读取Cookies

curl默认不会记住服务器设置的Cookie,也不会在下一次请求中携带Cookie。除非用户通过选项自己设置。

我们可以把之前的Cookies保存到一个文件,然后在下一次请求中指定curl读取文件中的Cookies:

$ curl -b cookies.txt http://example.com

-b选项指定curl去给定的文件中读取Cookies。

不过要主要,这里仅仅是读取Cookies,如果这次请求中服务器修改了Cookie,那么curl是不会进行保存的,除非我们手动指定。

3. 写Cookies到文件

我们可以使用-c选项指定curl保存这次请求中服务器设置的Cookies:

$ curl -c cookie.jar.txt http://example.com

有时,我们既需要从文件中读取Cookies,也需要保存服务器设置的Cookies。那么就可以同时使用-b-c选项:

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,099评论 18 139
  • 转载自:http://www.cnblogs.com/txw1958/archive/2013/01/19/286...
    php_bruce阅读 1,693评论 1 5
  • 从上海回到湖南老家,做漫长的火车,旁边大叔一个劲儿打呼噜,缩在狭小的车床上,盯着外面忽明忽暗变换不同的场景,天亮了...
    捣蛋派诗人阅读 235评论 2 1
  • 学习儿童礼仪是教会孩子一种生活方式,一种生活习惯,是培养孩子的感恩心恭敬心❤️尊重心,你看到的不止是行为的变化,而...
    阿连格格阅读 837评论 0 0
  • 我是来自深圳的一个不知名的小编剧。当时入这行,也纯属误打误撞:在我做第一份游戏文案工作3年之后,我决定要找一份能原...
    203031186c1c阅读 4,866评论 29 77