查看原文
其他

【第2131期】网络应用如何工作:HTTP和服务器

飘飘 前端早读课 2021-04-22

前言

周末愉快。今日前端早读课由@飘飘翻译,@Mark Erikson授权分享。

正文从这开始~~

概述Web应用程序中使用的概念、术语和数据流:HTTP协议和HTTP服务器应用程序。

Web开发是一个包含大量概念、术语、工具和技术的巨大领域。对于刚开始从事web开发的人来说,这一领域常常令人困惑——不清楚这部分中的大多数是什么,更不用说它们是如何组合在一起的。

本系列文章概述了基本的web开发概念和技术,这些概念和技术是什么,为什么需要它们,以及它们之间是如何相互关联的。它并不是对web开发的所有内容的全面引用,也不是“如何构建应用程序”指南。相反,它是一个区域的地图,旨在让你了解这个领域的情况,以及足够的信息,如果需要的话,让你可以更深入地研究这些术语和主题。

其中一些描述将更侧重于使用JavaScript开发现代客户端应用程序,但大多数主题都足够基础,也适用于以服务器为中心的应用程序。

本系列的其他文章将涉及更多的主题,例如。

  • 客户端开发和部署

  • 浏览器、HTML和CSS

  • JavaScript和DOM

  • AJAX、API和数据传输

新的术语将以斜体标出。我将链接其中一些术语的参考文献,但鼓励你自己搜索定义。此外,一些描述将被简化,以避免占用太多空间或处理边缘情况。

MDN网络技术文档是一个很好的资源,可以回顾这里列出的许多具体术语和概念的细节。

HTTP

浏览器实际上只能用一种语言来请求信息:超文本传输协议(HTTP)。这意味着浏览器必须与同样使用HTTP的服务器对话,并能响应浏览器的信息请求。

HTTP网络

软件层面的所有计算机网络都是基于一个叫做套接字Socket的抽象概念。套接字Socket代表与另一台计算机的特定开放连接。服务器开始监听传入的连接,客户端则要求打开一个新的连接。连接使用数字点分隔的IP地址(如192.168.0.255)和附加的数字端口号(如8080)来定义。一个单独的IP地址段可以是0-255,端口号的范围是0-65535。把一个IP地址看作是一栋公寓楼的街道地址,而端口号则是该楼的一个特定房间。任何数据都可以通过套接字发送--二进制或文本--都只是字节。

普通HTTP是一个未加密的协议。HTTP Secure (HTTPS)是相同的内容,但在发送前进行了加密。

标准化的网络协议使用特定的知名端口号。普通HTTP默认为80端口,HTTPS默认为443端口。因此,http://192.168.0.255 的地址意味着端口 80,https://192.168.0.255 则意味着端口 443。根据项目设置的不同,还经常使用其他端口。例如,在开发环境中,许多Web服务器应用程序在8080或3000端口上进行监听,因此在这种情况下,您将使用像http://192.168.0.255:3000这样的地址作为HTTP连接的目标。

由于数字网址很难记住,而且一个网站可能由许多系统共同组成,域名系统(DNS)将文本名称网址(如www.google.com)映射到一组特定的数字IP地址。

像http://www.example.com:8080/some-page?id=42#intro这样的URL可以拆成多块。

  • 协议:http://

  • 子域:www

  • 域名:example.com

  • 端口: 8080

  • 路径:某页

  • 查询参数: id=42

  • 片段。#intro

HTTP请求和响应

HTTP是一种纯文本的、"无状态"的、基于请求/响应的协议。换句话说。

  • 客户端应用程序必须打开与服务器的连接 并使用基于文本的格式发送请求。

  • 然后,服务器解析请求,运行代码来处理和适当地处理请求,并发回一个基于文本的响应。一旦发送了响应,它就会关闭开放的套接字连接。

  • 每一个请求/响应都是客户端和服务器之间的独立事务,默认情况下,不同的请求之间不存在关联性

请求和响应也都可能包含二进制数据,但请求和响应中与HTTP相关的内容是可读的文本指令。

一个典型的HTTP请求是这样的。

  1. GET https://www.example.com/description.html HTTP/1.1

  2. Host: www.example.com

  3. User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) Chrome/16.0.912.75 Safari/535.7

  4. Accept: text/html,application/xhtml+xml

  5. Referer: http://www.google.com/url?&q=example

  6. Accept-Language: en-US,en

请求的第一行被分解成三部分。

  • HTTP方法或动词,如GET或POST。

  • 请求的路径

  • HTTP协议版本

之后,请求可能会包含额外的头信息。每个头都是客户端可以包含的元数据,以便服务器更好地了解请求的信息种类。

请求还可以在标题后包含一个包含附加数据的主体。

响应遵循同样的结构。

  1. HTTP/1.1 200 OK

  2. Content-Type: text/html; charset=utf-8

  3. Server: Apache/2.4

  4. Date: Sun, 01 Nov 2020 16:38:23 GMT

  5. Content-Length: 17151


  6. <html>

  7. <head>

  8. <title>Page Title Here</title>

  9. </head>

  10. <body>

  11. Content here

  12. </body>

  13. </html>

第一行包含。

  • HTTP协议版本

  • HTTP状态码,以数字的形式出现。

  • HTTP状态的原因,以文本形式出现

之后是附加的头行,包含响应的元数据,包括正文的总字节大小。最后,响应包括实际的正文内容。

HTTP头文件

有几十个头字段可以通过HTTP发送,客户端和服务器都可以发送他们定义的额外的任意头。一些最常见的头信息有

请求
  • User-Agent:描述客户端具体类型和版本的字符串。

  • Cookie:服务器之前附加的一小段数据,以帮助跟踪该客户。

  • Referer。点击链接的前一个页面的网址

响应
  • Content-Length:响应的大小,以字节为单位。

  • Location:将请求重定向到一个新的URL

  • Set-Cookie:创建一个新的Cookie值。

两者

Content-Type:请求/响应体中使用的格式名称。

HTTP方法

HTTP协议规定了几种可能的方法或动词。每一种HTTP方法都代表不同类型的请求意图。

  • GET: 客户端想要检索信息。

  • POST:客户端要在服务器上创建或更新数据。

  • PUT:客户端要更新或替换服务器上的数据。

  • DELETE:客户端要删除服务器上的一些数据。

每种HTTP方法在请求的格式上有不同的变化,以及什么时候/为什么应该被客户端使用。此外,有些类型的请求可以被视为 "幂等的"(可以做很多次而不会引起额外的变化)。

GET请求

GET请求用于根据特定的URL从服务器上检索信息。GET请求不包含请求主体。然而,客户端可以将附加数据作为附加在主URL上的查询参数选项。查询参数以"?"开头,格式为key=value对,用安培符号分隔。/endpoint?a=1&b=stuff。URL中的空格和特殊字符可能需要进行URL编码,其中原始值由%和数字代替:?a=Some Value可能变成?a=Some%20Value。

由于GET请求只用于检索数据,服务器不应该更新数据以响应GET。这意味着多次进行相同的GET请求应该是安全的,不会造成副作用。

POST请求

POST请求用于告诉服务器更新一些数据或处理一些信息。POST通常在请求主体中包含所有相关信息,很少包含查询参数。

POST请求体通常使用几种常见的格式。

  • "Form-encoded":与查询参数的key=value结构相同,但在POST的正文中。

  • "Multi-part form data":将正文分成若干部分的分隔格式。

  • "JSON":JavaScript数据结构如对象和数组的字符串表示。

PUT请求

PUT请求与POST请求非常相似。两者都涉及向服务器发送数据,目的是为了更新。意图上的区别是,PUT的目的是创建或替换一个值,而POST的目的是创建或更新一个值。从概念上讲,PUT应该是安全的,可以连续做多次,而POST很可能会导致每个请求分别发生一些事情。

PATCH请求

PATCH请求也类似于PUT请求,但其目的是发送一个项目的部分表示,而PUT是为了发送一个项目的完整表示。

DELETE请求

DELETE请求用于要求服务器删除一些数据。从概念上讲,多次提出DELETE请求应该是安全的--如果一个值已经消失了,服务器应该忽略这个请求。

HTTP状态码

所有HTTP响应都包含一个数字状态码,以及描述状态码值的文本 "reason"字符串。状态代码是3位数字,分为以下范围。

  • 1xx: 信息性

  • 2xx:成功

  • 3xx: 重定向

  • 4xx: 客户端错误

  • 5xx: 服务器错误

常见的HTTP状态码包括

  • 200("OK"):成功响应

  • 301("已永久移动"):该URL的资源已被移动到一个新的URL。

  • 304("未修改")。自上次询问后,该URL的值没有改变。

  • 400("坏请求"):请求的格式不正确。

  • 401("未授权"):未通过认证,无法查看此内容。

  • 403("禁止"):不允许查看此内容。

  • 404 ("Not Found"):服务器无法识别这个URL。

  • 500("内部服务器错误"):在处理这个问题时,服务器上出了一些问题。

  • 503("服务不可用"):部分服务器宕机,无法响应。

Cookies

HTTP在默认情况下是 "无状态 "的,但服务器通常希望通过某种方式在多个请求之间关联特定客户的身份。例如,如果我登录一个论坛网站,服务器应该在我浏览不同的页面时记住我是登录的。

HTTP规范允许服务器在响应中包含特定键和值的Set-Cookie头。

  1. HTTP/2.0 200 OK

  2. Content-Type: text/html

  3. Set-Cookie: yummy_cookie=choco

  4. Set-Cookie: tasty_cookie=strawberry

每当客户将来向该服务器发出请求时,cookie值将自动包含在请求中。

  1. GET /sample_page.html HTTP/2.0

  2. Host: www.example.org

  3. Cookie: yummy_cookie=choco; tasty_cookie=strawberry

通常情况下,服务器会设置一个 "会话cookie",其中包含一些唯一的ID值,并在内部将这个唯一的ID映射到额外的数据上(比如 "会话ID 12345其实是用户mark")。这样,每次提出请求时,服务器就可以查找处理该特定用户所需的附加数据("请求是为了/messages,会话数据说该用户是mark--查询数据库中发给用户mark的所有消息")。

服务器

"服务器 "这个词根据上下文有多种相关含义。

  • 打开一个套接字来监听传入请求的应用程序

  • 一个专门处理HTTP请求并发送响应的应用程序。

  • 运行该服务器应用程序的物理或虚拟机。

在这里,我们要重点介绍一下 "HTTP请求处理应用 "的含义。

基本的HTTP请求处理

每一个HTTP服务器应用程序开始处理请求时,都会接受一个传入的套接字连接,并将请求内容解析成一些内部数据结构。然后服务器会检查HTTP请求路径和方法,以确定请求的意图。

根据服务器的编写和配置方式,它将通过以下一些组合来处理请求。

  • 从磁盘读取文件

  • 连接到数据库并加载/更新数据。

  • 内部更新会话跟踪信息

  • 运行开发者指定的逻辑

最终,服务器应用程序将根据所有的处理结果填写一个HTTP响应消息,将其写入socket,并关闭连接。

路由

路由是指根据请求URL来决定运行什么代码。服务器通常使用HTTP请求方法和请求URL路径的组合来决定应该用什么代码来处理一个请求。例如,GET /users可能由一个特定的函数处理,POST /users由第二个函数处理,GET /images/header.png由另一个函数处理。

服务器通常使用的惯例是,如果/some-folder/index.html存在,它将被用作GET /some-folder/请求的响应内容。这种模式也延续到了许多其他领域。例如,使用PHP语言编写的服务器可能会使用/some-folder/index.php来处理请求,而Node.js的CommonJS模块格式将通过尝试打开some-folder/index.js来处理加载require('some-folder')的,如果它存在的话。

静态文件

许多HTTP请求都是要求服务器返回一个特定文件的副本,比如GET /images/header.png或GET /app/index.html。服务器通常会通过将URL映射到磁盘上的一组特定的文件夹来处理这些请求,并检查该名称的文件是否存在于特定的文件夹路径中。如果存在,服务器将立即处理请求,从磁盘上读取整个文件,并将其内容作为响应的主体返回。

由于磁盘上的这些文件通常不会经常变化,所以它们被称为静态文件。服务静态文件是所有HTTP服务器应用都能做到的基本能力,大多数Web服务器框架都提供了一个内置的机制,可以从指定的文件夹中自动服务文件。有很多网站和工具只托管和服务静态文件--用户只需要上传文件,网站就已经配置好了自动服务文件的功能。

动态服务器应用逻辑

然而,服务器还需要通过运行开发人员编写的附加逻辑来响应传入的请求。用于定义服务器逻辑的确切语法和技术在不同的语言和服务器应用框架中差异很大,但它们都有一些共同的方面。一个典型的Web应用服务器框架将允许开发人员。

  • 指定正在处理的HTTP方法

  • 指定正在处理的URL路由

  • 读取一个代表解析后的HTTP请求的对象,该对象将包含所有头信息、其他元数据和正文内容的字段。

  • 与一个代表正在进行中的HTTP响应的对象进行交互,该对象将包含有助于生成最终响应内容的字段和方法。

许多Web应用框架还实现了某种形式的中间件,这些中间件是单独的逻辑块,它们被组合在一起。每个单独的中间件通常都有一个特定的目的,例如根据cookie向请求添加会话信息。然后,这些组合起来的中间件形成了一个预处理步骤的管道,在请求被特定的应用逻辑处理之前,这些预处理步骤会在每个传入的请求上执行。

然后,请求处理代码可以使用任何它想要的逻辑来动态地生成响应。处理逻辑可能会

  • 检查URL和HTTP方法

  • 检查cookie的会话ID,并在内部查找用户的详细信息。

  • 读取查询参数

  • 从请求主体中提取数据

  • 连接到数据库以检索信息

  • 进行一些计算

  • 根据计算结果或请求内容更新数据库。

  • 构建一个HTML文档或JSON数据结构。

  • 将该内容作为响应体发送回来

这里是一个使用Node.js运行时的Express HTTP库的(虚构的!)典型例子。

  1. const express = require('express');

  2. const session = require('express-session');


  3. const db = require('./dbConnection');


  4. const app = express();


  5. // Add middleware to populate req.session

  6. app.use(session({

  7. secret: 'keyboard cat'

  8. }));


  9. // Add middleware for processing incoming request contents

  10. app.use(bodyParser.json())

  11. app.use(bodyParser.urlencoded({extended: true}))


  12. // Add middleware to serve static files

  13. // URLs starting with '/static' will be mapped to files in the './public' folder

  14. app.use('/static', express.static('public'))


  15. // Define a request handler function that receives objects for request and response

  16. function countViews(req, res){

  17. // Build up an HTML body string as we go through

  18. let body = '';

  19. // Session middleware added data to the request object

  20. if (req.session.views) {

  21. ++req.session.views;

  22. } else {

  23. req.session.views = 1;

  24. body += '<p>First time visiting? view this page in several browsers :)</p>';

  25. }


  26. // Send back HTML content in the response

  27. res.send(body + '<p>viewed <strong>' + req.session.views + '</strong> times.</p>');

  28. }


  29. async function addBook(req, res) {

  30. // JSON body contents were processed into plain JS objects

  31. const {author, title} = request.body;;


  32. // Send update request to DB and wait for it to complete

  33. await db.query(

  34. 'INSERT INTO books (author, title) VALUES ($1, $2)',

  35. [author, title]

  36. );


  37. // Send a specific HTTP status message and JSON-formatted response

  38. res.status(201).json({status: 'success', message: 'Book added.'})

  39. }


  40. // Define a handler for GET requests to the '/' URL path.

  41. app.get('/', countViews);


  42. // Define a handler for POST requests to the '/books' URL path

  43. app.post('/books', addBook);


  44. if (!module.parent) {

  45. // Use port 3000 to listen for incoming request socket connections

  46. app.listen(3000);

  47. console.log('Express started on port 3000');

  48. }

这些步骤的确切语法因编程语言和网络框架而异,但所有框架都以某种形式共享这些相同的基本概念和功能。

响应格式

服务器可以在响应中发回任何他们想要的数据,但大多数响应都是以几种常见格式之一发回的。

  • HTML:描述网页结构和内容的标准标记语言。

  • CSS:标准样式语言,用于定义网页的外观。

  • JSON:一种基于JavaScript对象、数组和基元值语法的文本数据格式。

  • XML:一种基于嵌套标签的可定制的文本数据格式,类似于HTML。

  • 实际的静态文件(图片、JavaScript文件等)。

对于数据,JSON已经成为事实上的标准数据传输格式。

关于本文 作者:@Mark Erikson 原文:https://blog.isquaredsoftware.com/2020/11/how-web-apps-work-http-server/

为你推荐


【第1944期】HTTP/3原理与实践


【第2107期】在Ant Design 4.0里,我们如何追求快乐的工作?


欢迎自荐投稿,前端早读课等你来


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存