通过一个具体的例子讲解使用 errorhandler 处理 HTTP 错误,代码如下:from flask import Flask, render_templateapp = Flask(__name__)@app.route('/')def index(): print('execute request') return 'Hello World'@app.errorhandler(404)def errorhandler(e): print('error_handler(404)') print(e) return '404 Error'if __name__ == '__main__': app.run()在第 10 行,使用 @errorhandler(404) 注册一个钩子函数 errorhandler(e),当发生 HTTP 404 错误(页面不存在)时,调用钩子函数,该函数的第一个参数是错误对象。
在命令行中运行:$ ruby -e "p 'Hello World'"它会把 e 后面的字符串当作脚本来执行。Tips:在后面的代码示例中,我大多会采用两种方式来给出示例,一种是脚本执行的模式,我会标注出# ---- 输出结果 ----的文字注释,注释以上是要运行的代码,以下则是输出的结果。另一种是使用irb的形式,运行代码前有一个>,返回值结果我会使用=>来标出。示例:脚本形式:puts "Hello World"# ---- 输出结果 ----Hello Worldirb 形式:> puts "Hello World"Hello World=> nil
net/http 包主要提供 HTTP 相关的服务,可以使用这个包开发一个 webapp 的服务端。
我们在上篇文章中就说过 Kotlin 中的 List 并不是 Java 中的 List, 因为 Kotlin 中的 List 是个只读的 List 不具备修改集合中元素的操作方法。Java 的 List 实际上相当于 Kotlin 中的 MutableList 具有各种读和写的操作方法。Kotlin 中的 List<out E> 实际上就是协变的例子,用它来说明分析协变最好不过了,还记得上篇文章说过的学习泛型步骤二吗,就是通过分析源码来验证自己的理解和结论。通过以下源码均可验证我们上述所说的结论。//通过泛型类定义可以看出使用out修饰符 修饰泛型类型形参Epublic interface List<out E> : Collection<E> { override val size: Int override fun isEmpty(): Boolean override fun contains(element: @UnsafeVariance E): Boolean//咦! 咦! 咦! 和说的不一样啊,为什么还能出现在这个位置,还出来了个@UnsafeVariance 这个是什么鬼? 告诉你,稳住,先不要急,请听我在后面慢慢说来,先暂时保留神秘感 override fun iterator(): Iterator<E>//这里明显能看出来E处于out协变点位置,而且还是泛型类型Iterator<E>出现的,正好验证我们上述所说的协变的变种类型(E为类型实参的泛型类型) override fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean public operator fun get(index: Int): E//函数返回值的类型E,这里明显能看出来E处于out协变点位置,正好验证我们上述所说的协变的标准类型(E直接为返回值的类型) public fun indexOf(element: @UnsafeVariance E): Int public fun lastIndexOf(element: @UnsafeVariance E): Int public fun listIterator(): ListIterator<E>//(E为类型实参的泛型类型),为out协变点 public fun listIterator(index: Int): ListIterator<E>//(E为类型实参的泛型类型),为out协变点 public fun subList(fromIndex: Int, toIndex: Int): List<E>//(E为类型实参的泛型类型),为out协变点}源码分析完了,是不是感觉还是有点迷惑啊?就是 E 为啥还能在其他的位置上,还有 @UnsafeVariance 是个什么东西呢?这些疑问先放一放,但是上述至少证明了泛型协变 out 协变的位置是返回值的类型以及只读属性的类型 (这点源码中没有表现出来,但是实际上却是如此啊,这里可以自行查阅其他例子)。
如果你是一个后端开发人员的话,可能你常常会使用类似 Postman 这种类型的工具来发送 HTTP 请求来测试后端接口,其实这个功能在 PyCharm 中就可以直接完成,只需要安装 Http Client 插件即可。显示下面的页面,输入相应的url,path 及相关参数:执行后,点击 Response 可以看到返回结果:Tips:这个插件还支持一次运行多个请求(Open HTTP Requests Collection),也能像 Postman 一样查看历史请求。
Http 是一种协议,它制定了浏览器客户端和 Web 服务端信息传递的规则,所有的浏览器厂商和所有的 Web 后台服务都是遵从这个规则来实现信息的传递的。Web 服务的信息传递从最早期单一的文字,到后来有了图片,音频视频,而 HTTP 协议随着传递信息的变化也经历了以下几个版本的变化:HTTP/0.9:于 1990 年问世,用于简单文字的交换;HTTP/1.0:被正式列为 www 传输的标准,纳入 RFC1945;HTTP/1.1:在 1.0 的基础上新增了连接方式的规约;HTTP/2.0:随着网络的应用越来越广,内容也越来越丰富。HTTP2.0 在安全,性能,交互场景都有了很大的改进。目前市场上主流的还是 HTTP/1.1 ,随便打开一个浏览器 F12 查看下网络,基本上都可以看到 版本:HTTP/1.1。但是 HTTP/2.0 作为下一代的网络协议,前景可期。因为我们现在的网络都在追求加速,在硬件不断叠加,程序不断优化,CDN资源加速,之后如果继续优化的话那就只有 HTTP 协议本身了。2.0 因为有了多路复用,协议压缩等功能,所以性能上面是要比 1.1 高出不少的,普及只是时间问题。
Syntax: http { ... }Default: —Context: main可以看到 http 指令是指令块形式,属于主环境 main,它里面的指令是用于设置 http 相关参数的。比如设置 server 配置等,配置连接超时时间等。...# 主位置,最左边http { #指令或者指令块 ...} ...
** 面试官提问:** 为什么有了 HTTP 协议后还出现了 HTTPS 协议?HTTPS 协议解决了什么问题?题目解析:在研究 HTTPS 协议之前,我们先总结下 HTTP 协议的优点和缺点:优点缺点通信方式简单:基于请求和响应,客户端发起请求,服务器端返回响应明文通信:信息明文传输,安全性低。无需维护状态:HTTP 是无状态协议,不识别客户端。没有状态:例如对于需要保持登录状态的网站,需要依靠其他外部方式(Cookie、Session)维护状态。速度快,效率高。如上表所示,HTTP 协议牺牲了安全性,换来了效率,但是在某些安全性要求高的场景,使用 HTTP 协议是不合适的。HTTP 协议的全称是 Hypertext Transfer Protocol,HTTPS 协议的全程是 Hypertext Transfer Protocol Secure,多了一个 Secure(安全)的限制词。从协议上看,HTTPS 协议基于 HTTP 协议,使用 SSL/TLS 协议对传输内容进行加密,从公式上定义:HTTP + SSL(TLS) = HTTPS。HTTPS 协议将 HTTP 协议的通信部分由 SSL 或者 TLS 协议替代,网络模型划分如下: (HTTP 和 HTTPS 模型图) 除了 SSL 协议以外,HTTPS 协议还涉及几个重要的概念:CA 证书、混淆加密方式,以及 HTTPS 协议具体的工作流程,下面我们拆分解释。
在 HTTP 请求头里有个 Referer 字段,用于表明请求的来源地址。以向银行发出转账请求为例,说明 Referer 字段。1. 用户正常发出转账请求用户登录银行网站成功后,在网站首页 http://www.bank.com/,存在一个转账按钮;用户通过点击页面上的转账按钮进行转账,向银行发出转账请求 http://www.bank.com/transfer,该请求的信息如下:属性描述请求的地址 http://www.bank.com/transfer 请求的 Refererhttp://www.bank.com/可以看出:请求的地址和请求的 Referer 是属于相同的域名。2. 攻击者通过 CSRF 攻击发出转账请求如果用户被诱导进入恶意网站 http://www.malicious.com,在恶意网站的页面中向银行发起转账请求,请求的属性如下:属性描述请求的地址 http://www.bank.com/transfer 请求的 Refererhttp://www.malicious.com/可以看出:请求的地址和请求的 Referer 是属于不同的域名。
面试官提问: 什么是 HTTP 报文?什么是 HTTP 报文?题目解析: 首先,我们给出 HTTP 的定义:HTTP(HyperText Transfer Protocol,超文本传输协议)是一个请求 - 响应(Request to Response)协议,位于网络模型的应用层,基于传输层的 TCP 协议。 (HTTP 请求 - 响应模型)其次,HTTP 报文是在客户端和服务器端传输的数据报文,由起始行(Start Line)、请求头部(Request Header)和请求主体(Request Body)构成,从类型上分为请求报文(Request Message)和响应报文(Response Message)。 (HTTP 报文格式)
nginx 七层方向代理处理的是 http 请求,对应的是 http 协议。如果只是转发 http 请求,可以简单使用 proxy_pass 指令即可。这和我们之前简单的反向代理示例一致。# 在转发 http 请求时,URL必须以 http 或者 https 开头 Syntax: proxy_pass URL;Default: —Context: location, if in location, limit_except在使用 prxoy_pass 指令对 http 或者 https 协议进行反向代理时,需要注意一下问题:在 URL 不携带 URI 时,会将对应的 URL 直接转发到上游服务器在 URL 携带 URI 时,会将 location 参数中匹配上的那一段替换为该URL看下面的示例配置:...http { server { listen 8000; location /test { proxy_pass http://ip:port/xyz; } } server { listen 9000; location /test { proxy_pass http://ip:port; } } }... 在代理 http 请求 http://主机ip:8000/test/abc 时,由于 proxy_pass 指令后面的 URL 带了 /xyz 这样的路径,所以转发的请求是 http:// ip:port/xyz/abc,其中匹配的 /test 已经被替换掉了。而对于第二个 http 请求 http://主机ip:9000/test/abc 时,由于 proxy_pass指令后面直接是上游服务器地址,没有带 URI,所以转发的请求为 http://ip:port/test/abc,其中匹配到的/test并没有被替换。
HTTP 协议,即超文本传输协议,是一个客户端终端(用户)和服务器端(网站)请求和应答的标准。这也是 Web 开发基础。因为大部分网站或者 Web 服务的前后端交互几乎都是走 HTTP 请求。HTTP 协议定义 Web 客户端如何从 Web 服务器请求 Web 页面,以及服务器如何把 Web 页面传送给客户端。HTTP 协议采用了请求 / 响应模型。客户端向服务器发送一个请求报文,请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。服务器以一个状态行作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。HTTP 协议有如下特点:简单快速:客户向服务器请求服务时,只需传送请求方法和路径。由于 HTTP 协议简单,使得 HTTP 服务器的程序规模小,因而通信速度很快;灵活:HTTP 允许传输任意类型的数据对象;无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间;无状态:HTTP 协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。1.1.1 HTTP 常见请求在 HTTP/1.1 协议中共定义了八种方法(也叫 “动作”)来以不同方式操作指定的资源,目前我们比较常见和常用的有以下四个:GET 请求:向指定的资源发出 “显示 “请求。使用 GET 方法应该只用在读取数据,而不应当被用于产生 “副作用” 的操作中。一般在浏览器中直接敲击 URL 并按回车键是执行的 GET 请求;POST 请求:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和 / 或已有资源的修改;PUT 请求:从客户端向服务器传送的数据取代指定的文档的内容;DELETE 请求:请求服务器删除指定的页面。这四种请求和数据的增删改查(CRUD) 可以看成是相对应的,一般在设计 URL 接口时,也会默认使用这样特性,让 GET 请求对应查询数据、POST 请求对应数据的新增等等,这样的接口设计出来才会具备良好的 Restful 风格。1.1.2 HTTP 状态码HTTP 请求通常会返回一个状态码,常见的 HTTP 状态码有:2xx:正确类。表示用户请求被正确接收、理解和处理;200 - 请求成功;3xx:重定向类。表示没有请求成功,必须采取进一步的动作;301 - 资源(网页等)被永久转移到其它 URL;302 - 资源临时移动,资源只是临时被移动,客户端应继续使用原有 URI ;4xx:客户端错误。表示客户端提交的请求包含语法错误或不能正确执行;400 - 往往是 Bad Request 错误。是指请求的方法不对;401 - 用户没有访问权限,需要进行身份认证;403 - 禁止访问;404 - 资源不存在,Not Found 错误;5xx:服务端错误。一般是说明服务器出现了问题;503 - 服务端错误,一般是服务器内部处理异常。实操: 用 curl 命令模拟发送 HTTP 请求。[root@server ~]# curl -I -XGET http://www.baidu.com/index.htmlHTTP/1.1 200 OKAccept-Ranges: bytesCache-Control: private, no-cache, no-store, proxy-revalidate, no-transformConnection: keep-aliveContent-Length: 2381Content-Type: text/htmlDate: Sun, 08 Mar 2020 14:36:01 GMTEtag: "588604c8-94d"Last-Modified: Mon, 23 Jan 2017 13:27:36 GMTPragma: no-cacheServer: bfe/1.0.8.18Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
Http 协议除了我们的请求和响应参数,还包含了很多首部字段,这些字段使 Http 在满足基本接口的请求接收参数之余,还有更多高级丰富的扩展。这些首部字段可以分为3类:通用首部字段 / 请求首部字段 / 响应首部字段。本节我们将学习的是通用首部字段。Http 报文结构
TCP 三次握手之后,客户端和服务器端成功建立了连接,之后浏览器会向服务器特定端口发送HTTP请求。(https://imooc.com URL的请求报文)以 Chrome 浏览器为例,按下 F12 即可进入开发者模式,Network 一栏查看HTTP请求的具体报文。一个 HTTP 报文由请求行(Request Line)、请求头部(Request Headers)、空行(Blank Line)以及请求体(Request Body)构成,请求行中规定了请求方法、URI 以及 HTTP 的版本,关于每个字段的详细解释,之前的小节已经进行了阐述。
HTTP 是我们进行爬虫的一个基础之一,我们通过 HTTT 协议才能顺利爬取到 HTML 里面的内容,HTTP 相当于是一条道路,HTML 等网页是道路的终点,我们必须遵守 HTTP 的交通规则,才能高效的在这条道路上运输数据。
通常我们访问网站的时候是直接输入域名,比如:mybank.example.com,此时默认情况下浏览器会访问 http://mybank.example.com。HTTP 协议由于信息不加密,无法保证访问目标的真实性,容易受到中间人攻击。增加 Strict-Transport-Security 响应头,可以强制将访问协议转换成 Https。
HTTP 协议是一个简单的请求-响应协议,它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以 ASCII 码形式给出;而消息内容则具有一个类似 MIME 的格式。浏览器向 Web 服务器发出请求时,它向服务器传递了一个请求信息,HTTP 请求信息由 3 部分组成:请求行;请求头;请求正文。以下是一个 HTTP 请求消息的例子:GET / HTTP/1.1Host: www.imooc.comConnection: keep-aliveUser-Agent: Mozilla/5.0 AppleWebKit/537.36 Chrome/81.0Accept: text/html,application/xml;image/webp,image/png,*/*;...省略...第一行是请求行,用来说明请求方法,要访问的资源,以及所使用的 HTTP 版本。在这个例子中,请求方法是 GET,要访问的资源是 /,HTTP 版本是 1.1,表示要获取网站首页 / 的内容。紧接着请求行之后的部分是请求头部,用来说明服务器要使用的附加信息。例如:Host 指出请求的主机名,这里是 www.imooc.com。请求头部之后是请求正文,在请求正文中可以添加任意的其他数据,这个例子的请求正文为空。
服务器不支持请求中所指明的HTTP版本。
上一个小节中我们创建了第一个 Flask 程序,并且在浏览器中成功看到了 Flask 程序给我们返回的 Hello World。但是你有没有注意到,我们看到的内容只是在后台进行写死的,也就是说无论你怎么请求,看到的永远是 Hello World,无法根据自己的需求让 Flask 程序返回给你想要的内容?这该怎么办呢?不要急,我们有 HTTP 协议,HTTP 协议提供了多种方法访问服务端资源,本小节讲解常用的 HTTP 方法。并通过一个个具体的例子,说明如何在 Flask 中使用不同的 HTTP 方法。Tips:本节课所有代码已经上传到了 Github,可以点击这里进行下载。
在你浏览互联网的时候,绝大多数的数据都是通过 HTTP 协议获取到的,也就是说如果你想要实现一个能上网的 App,那么就一定会和 HTTP 打上交道。当然 Android 发展到现在这么多年,已经有很多非常好用,功能非常完善的网络框架了,比如 Volley、OkHttp、retrofit等,但是底层逻辑都是一样的。本节我们来学习 Android 原声支持的 HTTP 接口,相比那些第三方框架,它的封装更好,也更适合我们了解底层原理。
对于资源的具体操作类型,由 HTTP 动词表示。常用的 HTTP 动词有下面 4 个(括号里是对应的 SQL 命令)。GET(SELECT):从服务器取出资源(一项或多项)POST(CREATE):在服务器新建一个资源PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)DELETE(DELETE):从服务器删除资源还有 3 个不常用的 HTTP 动词。PATCH(UPDATE):在服务器更新(更新)资源(客户端提供改变的属性)HEAD:获取资源的元数OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的下面是一些例子。GET /classes:列出所有班级POST /classes:新建一个班级(上传文件)GET /classes/ID:获取某个指定班级的信息PUT /classes/ID:更新某个指定班级的信息(提供该班级的全部信息)PATCH /classes/ID:更新某个指定班级的信息(提供该班级的部分信息)DELETE /classes/ID:删除某个班级GET /classes/ID/students:列出某个指定班级的所有学生DELETE /classes/ID/students/ID:删除某个指定班级的指定学生
正如 HTTP 请求包含四部分一样,HTTP 的响应同样包含四个部分。分别是状态行,消息报头,空行,以及响应正文。下面这个是我们访问慕课网后,返回的信息:HTTP/1.1 200 OKServer: nginxContent-Type: text/html; charset=UTF-8Content-Length: 49930Connection: keep-alive<HTML>....第一行为状态行,同样的,需要标明 HTTP 协议的版本,然后后面是一个状态码。这里的 200 表示请求成功。OK 是用来表明请求的状态消息,通过消息我们可以对请求的错误进行定位。常见的状态码如下表所示:状态码解释200 OK请求成功400 Bad Request请求消息有语法错误。401 Unauthorized未经授权,必须和WWW-Authenticate报头域一起使用。403 Forbidden拒绝提供服务404 Not Found请求的资源不存在,或者被移除了500 Internal Server Error服务器端错误,通常是服务器端代码逻辑错误造成的503 Server Unavailable服务不可用,有可能需要等一段时间进行恢复Tips:总结一下规律,2 开头的意味着成功,3 开头的大部分是重定向,4 开头是客户端错误,5 开头的是服务器端的错误。第二行的 Server: nginx表明了服务器端采用的服务器名称。Content-Length: 49930 是消息报头表明了服务器返回的 HTML 内容的长度。Connection: keep-alive 是空行,空行必须要有,不可省略。最后是响应正文,一般会是一个 HTML 文档。
上一个小节中,我们简单的介绍了 HTTP 协议,但是,并没有针对 HTTP 的请求和响应进行更详尽的描述。但是,分析请求和响应信息是我们进行爬虫工作中的重要步骤,因此,有必要详细的介绍这两个步骤。我们还是复用之前的访问慕课网的例子进行 HTTP 协议的解析。关于怎么获取请求头和响应头的信息的内容,我们会在后面讲解第一个爬虫的时候进行讲解。使用 get 方法请求慕课网的请求信息如下:GET / HTTP/1.1Host: www.imooc.comConnection: keep-aliveCache-Control: max-age=0HTTP 请求主要有四部分组成。分别是请求行,请求头部,空行和请求数据。
上面我们已经大概了解了什么是 HTTP 协议,简单来说就是客户端与服务端用来通信的协议,HTTP 协议中规定和很多 HTTP 方法来让我们根据不同的需求向服务端发起请求。下面我们通过一个具体的例子,说明如何在 Flask 中使用不同的 HTTP 方法:from flask import Flask, requestapp = Flask(__name__)首先,导入类 flask.Flask 和 flask.request,request 封装了请求消息,可以获取请求的各种参数。@app.route('/login', methods = ['GET'])def login(): return '''<form action="/check_login" method="POST"> <p><input type="text" name="name"/></p> <p><input type="password" name="password"/></p> <p><input type="submit" value="submit"/></p></form>'''定义处理路径 /login 的函数 login,装饰器 @app.route(’/login’, methods = [‘GET’]) 表示使用 GET 方法处理路径 /login 的请求。函数 login 返回一段用于登录的 HTML 表单,表单包括 2 个字段: name 和 password。在第 4 行,指定使用 POST 方法提交表单给服务端的 /check_login 页面。@app.route('/check_login', methods = ['POST'])def check_login(): name = request.form['name'] password = request.form['password'] if name == 'guest' and password == '123': return 'Login succeed' else: return 'Login failed'定义处理路径 /check_login 的函数 check_login,装饰器 @app.route(’/check_login’, methods = [‘POST’]) 表示使用 POST 方法处理路径 /check_login 的请求。函数 check_login 根据请求的参数 name 和 password 是否正确,返回给用户相应的信息。在第 3 行,提取参数 name 的值,在第 4 行,提取参数 password 的值。如果 name 是 guest,password 是 123,则返回登录成功消息,否则返回登录失败消息。if __name__ == '__main__': app.run()调用 app.run() 运行程序。用户在浏览器中输入 http://localhost:5000/login,浏览器显示:用户输入正确的 name 和 password 后,浏览器跳转到页面 http://localhost:5000/check_login,显示:
Http 底层基于 TCP 协议,分为请求和响应。请求和响应分别有各自的 Header 和 Body 组成。Header 里面通常是本次请求 / 响应的描述信息,比如版本号、长度、UA、Content-Type 等等,而 Body 里面通常就是我们要传递的业务数据了,下面分别浏览一下请求和响应的内容。
假如我们在浏览器输入慕课网地址:https://www.imooc.com/HTTP 的工作流程如下:首先需要传输层建立连接,这个是 HTTP 的运作的基础之一,因为 HTTP 是应用层,它是建立在传输层之上的。然后发送一个 HTTP 的请求报文如下:GET / HTTP/1.1Host: www.imooc.comConnection: keep-aliveCache-Control: max-age=0...服务端会返回一段报文信息如下:HTTP/1.1 200 OKServer: nginxContent-Type: text/html; charset=UTF-8Content-Length: 49930Connection: keep-alive...浏览器渲染服务器返回的信息,并关闭连接或者重用之前的连接。以上就是 HTTP 的基本工作原理。关于请求与响应的具体解释,我们下一个小节会深入讲解。
可以使用 Request 对象中的 header() 方法获取当前 HTTP 请求的头信息,代码如下: public function getInfo(Request $request){ halt($request->header()); }执行结果如下图所示:
根据 HTTP 标准,常用的 HTTP 请求如下所示:请求方法功能GET请求指定 URL 的页面,这是最常见的方法HEAD类似于 GET 请求,但返回的响应中没有具体的内容,用于获取报头POST向指定 URL 的页面提交数据进行处理请求,例如提交表单或者上传文件PUT从客户端向服务器传送数据,取代指定 URL 的页面DELETE请求服务器删除指定 URL 的页面
Http 协议是前后端 API 接口交互的桥梁,不管你是前端开发,移动端开发,后端开发,它都是你工作必不可少的部分。在如今的生产环境中,为了快速实现逻辑业务的开发,往往这部分是被框架封装得很完整了,初学者就更加容易忽视它,以至于遇到问题无从下手。为什么我资源更新了,界面还是旧的,可能是 Http 缓存了;请求的参数该放路径还是 Body 中;接口报错了,返回的状态码看不懂;接口文档如何编写才是规范的;让 Http 协议规范自己的软件开发;虽然 Http 工具封装得很好用,但我必须搞懂它,要做工具的主人而不是奴隶,必要的时候自己造工具。
本节我们将借助 Socket 实现服务的端口监听并根据 Http 协议的请求和响应结构,实现一个简单的 Web 服务器,加深体验 Web 服务和 Http 协议的原理。