左宜同学 发表于 2023-12-6 14:40:02

第二单元 Http 概述

1. C/S 与 B/S

C/S结构系统是什么
Client/Server结构(C/S结构)是大家熟知的客户机和服务器结构。它是软件系统体系结构,通过它可以充分利用两端硬件环境的优势,将任务合理分配到Client端和Server端来实现,降低了系统的通讯开销
 
B/S结构系统是什么
B/S结构(Browser/Server,浏览器/服务器模式),是WEB兴起后的一种网络结构模式,WEB浏览器是客户端最主要的应用软件。这种模式统一了客户端,将系统功能实现的核心部分集中到服务器上,简化了系统的开发、维护和使用。客户机上只要安装一个浏览器,就可以使用B/S结构的系统。其实B/S结构的系统也可以看做是一种特殊的C/S结构。
 
C/S结构的优点
1.能充分发挥客户端的处理能力,可控性强 2.形式多样,可以充分满足客户自身的个性化要求 3.容易保证安全性
 
C/S结构的缺点
1.用户群固定。由于程序需要安装才可使用,因此不适合面向一些不可知的用户 2.维护成本高
 
B/S结构的优点
1.分布性强,客户端零维护。只要有网络、浏览器,就可以随时随地进行使用系统 2.业务扩展简单方便,维护简单方便。只需要在服务器端做相应的修改,客户端就会在下次访问获取最新版本
 
B/S结构的缺点
1.个性化特点明显降低,无法实现具有个性化的功能要求。集成诸如指纹仪、摄像头、调用播放器变得困难 2.在跨浏览器上,BS架构不尽如人意 3.请求/响应模式带来的性能问题 4.安全性上需要花费巨大的设计成本。因为B/S客户端是基于浏览器的,通过简单修改URL参数、篡改POST字段值就会产生安全性方面的问题。
 
常用的B/S应用程序
1.MVC 2.WebForms 3.WebAPI
 
常用的C/S应用程序
1.Windows窗体应用程序 2.WPF应用程序 3.控制台应用程序
 
2. 为什么需要Http协议


 
 
 
 
客户端与服务端之间的通讯是否也需要某种协议?
答:http 协议. http协议是一种未进行加密处理,由服务器传输超文本到本地浏览器传输协议。
特点:

[*]基于TCP/IP的高级协议 (Socket)
[*]默认端口号:80
[*]基于请求/响应模型的:一次请求对应一次响应
[*]无状态的:每次请求之间相互独立,不能交互数据
 
 
3. Http的前世今生


 
1960年美国人Ted Nelson构思了一种通过计算机处理文本信息的方法,并称之为超文本(hypertext),这成为了HTTP超文本传输协议标准架构的发展根基。Ted Nelson组织协调万维网协会(World Wide Web Consortium)和互联网工程工作小组(Internet Engineering Task Force )共同合作研究,最终发布了一系列的RFC,其中著名的RFC 2616定义了HTTP 1.1。

[*]HTTP协议在应用层
[*]最初的目的是为了提供一种发布和接收HTML页面的方法
[*]规定了客户端和服务器之间通信格式
 
4. Http 的通信流程


[*]建立TCP连接
[*]客户端向服务端发送命令
[*]客户端发送请求头信息
[*]服务器应答
[*]服务器发送应答头信息
[*]服务器向客户端发送数据(静态资源,html/css/js)
[*]服务器关闭TCP连接
 
一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码: Connection:keep-aliveTCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。
 
5. URL与URI的区别

什么是URI
URI 统一资源标识符(Uniform Resource Identifiers, URI),用来唯一识别一个资源,可以把它理解为你的身份证号。
作用:Web上可用的资源如HTML文档,图像,视频等都是以URI来定位的。
 
什么是URL
URL 统一资源定位符( Uniform Resource Locator ),可以把它理解为你身份证上地址。 是互联网上用来标识某一处资源的地址。
 
作用:

[*]可以用来标识一个资源,而且还指明了如果定位这个资源
[*]URL是internel 上用来描述信息资源的字符串,主要用在各种www 程序上。
 
区别

[*]URI是一种抽象的,高层次概念定义统一资源标识
[*]每个URL都是一个URI,但每一个URI并不一定是URL,因为URI 还包括另外一个子类URN(统一资源命名),它命名资源但不负责定位资源(姓名+你的地址 = 你的身份证号)
[*]URL就是通过定位的方式来实现URI的。
 
6. 消息数据格式

请求消息格式


[*]请求行
请求方式 请求url 请求协议/版本
GET /login.html HTTP/1.1 
 
[*]请求方式:
HTTP协议有8中请求方式:
① GET:请求获取Request-URI所标识的资源。
② POST:在Request-URI所标识的资源后附加新的数据。
③ HEAD:请求获取由Request-URI所标识的资源的响应消息报头。
④ PUT:请求服务器存储一个资源,并用Request-URI作为其标识。
⑤ DELETE:请求服务器删除Request-URI所标识的资源。
⑥ TRACE:请求服务器回送收到的请求信息,主要用于测试或诊断。
⑦ CONNECT:HTTP 1.1协议中预留给能够将连接改为管道方式的代理服务器。
⑧ OPTIONS:请求查询服务器的性能,或者查询与资源相关的选项和需求。
 
常用的有2种

[*]GET:

[*]请求参数在请求行中,在url后。
[*]请求的url长度有限制的
[*]不太安全
[*]可被收藏到书签,也可被缓存

[*]POST:

[*]请求参数在请求体中
[*]请求的url长度没有限制的
[*]相对安全


[*]请求头:客户端浏览器告诉服务器一些信息
请求头名称: 请求头值  
常见的请求头:

[*]User-Agent:浏览器告诉服务器,我访问你使用的浏览器版本信息
作用: 可以在服务器端获取该头的信息,解决浏览器的兼容性问题
[*]Referer:http://localhost/login.html ,告诉服务器,我(当前请求)从哪里来。
作用:

[*]防盗链:
[*]统计工作:


[*]请求空行 :就是用于分割POST请求的请求头,和请求体的。
[*]请求体(正文): 封装POST请求消息的请求参数的
字符串格式:
POST /login.html HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101
Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://localhost/login.html
Connection: keep-alive
Upgrade-Insecure-Requests: 1
username=zhangsan 
 
响应数据格式

响应消息:服务器端发送给客户端的数据
响应行
协议/版本 响应状态码 状态码描述响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态,状态码都是3位数字。
 
分类

[*]1xx:服务器接收客户端消息,但没有接受完成,等待一段时间后,发送1xx状态码
[*]2xx:成功。代表:200
[*]3xx:重定向。代表:302(重定向),304(访问缓存)
[*]4xx:客户端错误。代表:404(请求路径没有对应的资源)405:请求方式没有对应的方法, 401 未授权,403?
[*]5xx:服务器端错误。代表:500(服务器内部出现异常),503:网关出现问题
 
响应头
格式:
头名称: 值常见的响应头:

[*]Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式
[*]Content-disposition:服务器告诉客户端以什么格式打开响应体数据值
[*]attachment;filename=xxx:以附件形式打开响应体。文件下载
 
响应体
服务器返回的数据
 
响应字符串格式:
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: 101
Response对象
Date: Wed, 06 Jun 2018 07:08:42 GMT
<html>
<head>
<title></title>
</head>
    <body>
      hello , response
    </body>
</html>
 
 
 
7. HTTP 各版本简介

HTTP 1.0: 规定浏览器与服务器只保持短暂的连接,浏览器的每次请求都需要与服务器建立一个TCP连接,服务器完成请求处理后立即断开TCP连接,服务器不跟踪每个客户也不记录过去的请求 。连接无法复用
HTTP1.1:

[*]复用连接(keep-alive)
[*]缓存处理
[*]身份认证, 状态管理
HTTP 1.1状态代码及其含义
状态代码有三位数字组成,第一个数字定义了响应的类别,且有五种可能取值:
1xx:指示信息--表示请求已接收,继续处理
2xx:成功--表示请求已被成功接收、理解、接受
3xx:重定向--要完成请求必须进行更进一步的操作
4xx:客户端错误--请求有语法错误或请求无法实现
5xx:服务器端错误--服务器未能实现合法的请求
HTTP2.0:

[*]多路复用 (Multiplexing)
即连接共享,即每一个request都是是用作连接共享机制的。一个request 对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的id将request再归属到各自不同的服务端请求里面。多路复用原理和keep alive区别如下图:

 
[*]二进制分帧
HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。
[*]首部压缩(Header Compression)
如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份headerfields表,既避免了重复header的传输,又减小了需要传输的大小。
[*]服务端推送(Server Push)
服务端推送是一种在客户端请求之前发送数据的机制。在 HTTP/2 中,服务器可以对客户端的一个请求发送多个响应。Server Push 让 HTTP1.x 时代使用内嵌资源的优化手段变得没有意义;如果一个请求是由你的主页发起的,服务器很可能会响应主页内容、logo 以及样式表,因为它知道客户端会用到这些东西。这相当于在一个 HTML 文档内集合了所有的资源,不过与之相比,服务器推送还有一个很大的优势:可以缓存!也让在遵循同源的情况下,不同页面之间可以共享缓存资源成为可能。
 
HTTP 3.0 HTTP3.0,也称作HTTP over QUIC。HTTP3.0的核心是QUIC(读音quick)协议,由Google在 2015年提出的SPDY v3演化而来的新协议,传统的HTTP协议是基于传输层TCP的协议,而QUIC是基于传输层UDP上的协议,可以定义成:HTTP3.0基于UDP的安全可靠的HTTP2.0协议。
QUIC 协议针对基于TCP和TLS的HTTP2.0协议解决了下面的问题。

[*]1.1 减少了TCP三次握手及TLS握手时间 不管是HTTP1.0/1.1还是HTTPS,HTTP2.0,都使用了TCP进行传输。HTTPS和HTTP2还需要使用TLS协议来进行安全传输。这就出现了两个握手延迟,而基于UDP协议的QUIC,因为UDP本身没有连接的概念,连接建立时只需要一次交互,半个握手的时间。区别如下图:
[*]1.2 多路复用丢包的线头阻塞问题 QUIC保留了HTTP2.0多路复用的特性,在之前的多路复用过程中,同一个TCP连接上有多个stream,假如其中一个stream丢包,在重传前后的stream都会受到影响,而QUIC中一个连接上的多个stream之间没有依赖。所以当发生丢包时,只会影响当前的stream,也就避免了线头阻塞问题。
[*]1.3 优化重传策略 以往的TCP丢包重传策略是:在发送端为每一个封包标记一个编号(sequence number),接收端在收到封包时,就会回传一个带有对应编号的ACK封包给发送端,告知发送端封包已经确实收到。当发送端在超过一定时间之后还没有收到回传的ACK,就会认为封包已经丢失,启动重新传送的机制,复用与原来相同的编号重新发送一次封包,确保在接收端这边没有任何封包漏接。这样的机制就会带来一些问题,假设发送端总共对同一个封包发送了两次(初始+重传),使用的都是同一个sequence number:编号N。之后发送端在拿到编号N封包的回传ACK时,将无法判断这个带有编号N的ACK,是接收端在收到初始封包后回传的ACK。这就会加大后续的重传计算的耗时。QUIC为了避免这个问题,发送端在传送封包时,初始与重传的每一个封包都改用一个新的编号,unique packet number,每一个编号都唯一而且严格递增,这样每次在收到ACK时,就可以依据编号明确的判断这个ACK是来自初始封包或者是重传封包。
[*]1.4 流量控制 通过流量控制可以限制客户端传输资料量的大小,有了流量控制后,接收端就可以只保留相对应大小的接收 buffer ,优化记忆体被占用的空间。但是如果存在一个流量极慢的stream ,光一个stream就有可能估用掉接收端所有的资源。QUIC为了避免这个潜在的HOL Blocking,采用了连线层(connection flow control)和Stream层的(streamflow control)流量控制,限制单一Stream可以占用的最大buffer size。
[*]1.5 连接迁移 TCP连接基于四元组(源IP、源端口、目的IP、目的端口),切换网络时至少会有一个因素发生变化,导致连接发生变化。当连接发生变化时,如果还使用原来的TCP连接,则会导致连接失败,就得等原来的连接超时后重新建立连接,所以我们有时候发现切换到一个新网络时,即使新网络状况良好,但内容还是需要加载很久。如果实现得好,当检测到网络变化时立刻建立新的TCP连接,即使这样,建立新的连接还是需要几百毫秒的时间。QUIC的连接不受四元组的影响,当这四个元素发生变化时,原连接依然维持。QUIC连接不以四元组作为标识,而是使用一个64位的随机数,这个随机数被称为Connection lD,对应每个stream,即使IP或者端口发生变化,只要Connection ID没有变化,那么连接依然可以维持。
8 . 什么是HTTPS

HTTP协议传输的数据都是未加密的.为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。现在的HTTPS都是用的TLS协议,但是由于SSL出现的时间比较早,并且依旧被现在浏览器所支持,因此SSL依然是HTTPS的代名词。
HTTPS 默认端口号是443.(http协议默认端口号是80)
HTTPS与HTTP的一些区别

[*]HTTPS协议需要到CA申请证书,一般免费证书很少,需要交费。
[*]HTTP是超文本传输协议,信息是明文传输,HTTPS则是具有安全性的TLS加密传输协议。
[*]HTTP和HTTPS使用的是完全不同的连接方式,用的默认端口也不一样,前者是80,后者是443.
[*]HTTPS的连接很简单,HTTPS协议是由TLS+HTTP协议构建的 可进行加密传输、身份认证的网络协议,比HTTP协议安全。
 
9. HttpContext上下文

1. 什么是应用程序上下文

我们先举个例子:
小张,我现在有点口渴,帮我去倒杯水来。
虽然只有短短几个字,却清楚的交待了请求的“来龙” 与 “去脉” 。口渴是你的上文,小张把水给你递来了就是你的下文。如果你只是说“小张,我现在有点口渴” 你只交待了上文却没有了下文,或者 你突然来了句 “帮我去倒杯水来” 没有交待上文,只有下文也是不对的,没有上文确实会让人觉得很懵B的。
在此跟各位吐槽一下没有上文的苦水,属实让人很难受(5555..。。。。)。。。。
很多同学找我帮忙找bug的时候,直接给我截了一张报错的图给我,然后报错的图也不全,然后就没有然后了。小伙子真把我们当会算命的神了,看一眼报错就知道哪儿报错。首先你得交待一下你的上文是啥(干嘛用的),就是代码的来龙去脉,最好把相关的代码都截图给我们看,这样我们才能有把握解决掉这个bug.
 
所谓的应用程序的上下文:其实就是交待了当前请求的环境信息,当前上下文中包含了你的请求信息(Request) 与 请求响应(Response) 。 也就是当前请求的来龙去脉
 
2. IHttpContextAccessor 接口

提供对当前 HttpContext的访问权限(如果有)。 应谨慎使用此接口。 它依赖于 AsyncLocal 对异步调用产生负面影响的性能。 它还会创建一个依赖于“环境状态”的依赖项,这使得测试更加困难。
解决办法,将 IHttpContextAccessor 设置为单例。
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// 或者
builder.Services.AddHttpContextAccessor(); // 同样也是单例 
属性
HttpContext获取或设置当前 HttpContext。 如果没有活动HttpContext状态,则返回 null 。  HttpContext 上下文中包含了一些非常重要的对象信息:

[*]Request 属性
HttpRequest获取此请求的对象, 表示单个 HTTP 请求的传入端。
[*]RequestServices
获取或设置 IServiceProvider 提供对请求的服务容器的访问权限。
[*]Response 对象
HttpResponse获取此请求的对象,表示单个 HTTP 请求的传出端。
[*]Session 对象
获取或设置用于管理此请求的用户会话数据的对象。
[*]User 对象
获取或设置此请求的用户。
 
3. HttpRequest 请求

表示单个 HTTP 请求的传入端。
Body获取或设置请求正文 Stream。BodyReader获取请求正文 PipeReader。ContentLength获取或设置 Content-Length 标头。ContentType获取或设置 Content-Type 标头。Cookies获取此请求的 Cookie 集合。Form获取或设置请求正文作为Form表单。HasFormContentType检查Form表单类型的 Content-Type 标头。Headers获取请求标头。Host获取或设置 Host 标头。 可以包含端口。HttpContext获取 HttpContext 此请求。IsHttps如果 RequestScheme 为 https,则返回 true。Method获取或设置 HTTP 方法。Path获取或设置 RequestPath 中的请求路径。PathBase获取或设置请求的基本路径。 路径基不应以尾部斜杠结尾。Protocol获取或设置请求协议 (,例如 HTTP/1.1) 。Query获取从 Request.QueryString 分析的查询值集合。QueryString获取或设置用于在 Request.Query 中创建查询集合的原始查询字符串。RouteValues获取此请求的路由值的集合。Scheme获取或设置 HTTP 请求方案。<form method="get" action="/home/query">
    <label for="username">姓名:</label><input name="username" id="username"/>
    <label for="studentNo">学号:</label><input name="studentno" id="studentNo"/>
    <input type="submit" value="查询"/>
</form> 

 
public IActionResult Query()
{
    var username = HttpContext.Request.Query["username"];
    var studentNo = HttpContext.Request.Query["studentNo"];
    _logger.LogInformation($"姓名:{username}");
    _logger.LogInformation($"学号:{studentNo}");

    // 获取所有的请求头
    foreach (var header in Request.Headers)
    {
      Console.WriteLine($"头名:{header.Key},值:{header.Value}");
    }

    return Content("查询完毕");
}
info: WebApplication2.Controllers.HomeController
      姓名:任我行
info: WebApplication2.Controllers.HomeController
      学号:1310734881
      
头名:Accept,值:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
头名:Host,值:localhost:7096
头名:User-Agent,值:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36 Edg/103.0.1264.77
头名::method,值:GET
头名:Accept-Encoding,值:gzip, deflate, br
头名:Accept-Language,值:zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
头名:Cookie,值:.AspNetCore.Session=CfDJ8JezZrvMRBVHqdn1GbTI%2B3bdZuPW2P971ifEekAO%2BfcIEIYo4vpUwD5bHRtEspZHmgyzMYyNAp8u5r8PZaPdwiij2jjPmksoigF8yIwuEuJBGe5zmq1zN0gqgGwSaYmIBw328xN5fzrxbkl92Xo5te4cOHy6GRwKKZMd4YjAbRlk,.AdventureWorks.Session=CfDJ8JezZrvMRBVHqdn1GbTI%2B3ZqwisKw1CqoPT5%2Fbb6V8VD%2BYE%2B7ytbOjCy1%2BB%2BqkuaWL3%2B4GpuM2%2BACge3ahqhKRfp7utYMtYdsICYKzEM7o5qzNQkdv1U5JnLbbvZlJM2MDp6GFkjfeAQFae%2FB29PeYYM3tfhIGu0wNmAtdDZTIkg
头名:Referer,值:https://localhost:7096/
头名:Upgrade-Insecure-Requests,值:1
头名:sec-ch-ua,值:" Not;A Brand";v="99", "Microsoft Edge";v="103", "Chromium";v="103"
头名:sec-ch-ua-mobile,值:?0
头名:sec-ch-ua-platform,值:"Windows"
头名:sec-fetch-site,值:same-origin
头名:sec-fetch-mode,值:navigate
头名:sec-fetch-user,值:?1
头名:sec-fetch-dest,值:document 
 
4. HttpResponse 响应

表示单个 HTTP 请求的传出端。
Body获取或设置响应正文 Stream。BodyWriter获取响应正文 PipeWriterContentLength获取或设置 Content-Length 响应标头的值。ContentType获取或设置 Content-Type 响应标头的值。Cookies获取可用于管理此响应的 Cookie 的对象。HasStarted获取一个值,该值指示是否已将响应标头发送到客户端。Headers获取响应标头。HttpContextHttpContext获取此响应。StatusCode获取或设置 HTTP 响应代码。一、Header属性

属性备注例如Access-Control-Allow-Origin该站点可以被哪些网站进行 跨域资源共享Access-Control-Allow-Origin: http://example.com:8080 http://foo.example.com Access-Control-Allow-Origin:*Accept-Ranges服务器是否支持资源范围请求,资源范围请求:指按byte为单位,请求资源的某一段数据例如请求一个文件的200byte—400byte的数据 Accept-Ranges:bytes 表示该资源支持byte形式资源范围请求 Accept-Ranges:none则表示不支持Content-Range如果当前这个响应数据是整个资源的一部分时,是具体的哪一部分(从第几byte到第几byte)。在请求中,客户端可以通过设定”Range”头域来通知服务器其只想请求整个资源中某一段数据,而对应的,当服务器响应这种请求,并发送某一段数据到客户端的时候,必须通过Content-Range头来告诉客户端当前的响应数据是整个资源的第几byte到第几byte。这个在资源的分段下载和续点下载应用中很有用。Content-Range:500-900Allow一个资源允许哪些HTTP方法进行请求Allow: GET, HEAD Allow:*Connection连接方式Connection:keep-alive Connection:closeContent-Encoding服务器对响应数据的编码方式,但这里的编码方式不同于编码字符集(GB2312,UTF-8等),而是(通常)指压缩方式Content-Encoding:gzipContent-Language响应数据的自然语言Content-Language:ZH-CN、 en-USContent-Length响应数据的数据长度,单位是byteContent-Length:1024Content-Disposition当客户端请求的资源是一个可下载的资源(这里的“可下载”是指浏览器会弹出下载框或者下载界面)时,对这个可下载资源的描述(例如下载框中的文件名称)就是来源于该头域。Content-Disposition: attachment; filename=”some_app.exe”Server服务器的名称Server: KestrelContent-Type服务器告诉浏览器它发送的数据属于什么文件类型,也就是响应数据的MIME类型Content-Type: text/html; charset=utf-8,让浏览器把接收到的实体内容以HTML格式解析 Content-Type: text/plain; charset=utf-8,让浏览器把接收到的实体内容以普通文本解析 octet-stream 响应流date响应消息发送的GMT格式日期Date: Tue, 15 Nov 1994 08:12:31 GMT 

[*]设置响应语言
HttpContext.Response.Headers.Add("Content-Language","zh-CN");  
[*]下载文件
public async Task Download()
{
    // HttpUtility 为了解决中文文件名问题
    Response.Headers.Add("Content-Disposition",
                         $"attachment;filename={HttpUtility.UrlEncode("文件下载",Encoding.Default)}.txt");
    Response.ContentType = "application/octet-stream;"; // MIME 类型为下载流

    var buffer = Encoding.UTF8.GetBytes("响应头下载测试");
    await Response.Body.WriteAsync(buffer); // 将内容写入
    await Response.Body.FlushAsync();

}
<br><br><br>
 
配套视频链接:什么是Mvc (cctalk.com)

来源:https://www.cnblogs.com/xuyubing/archive/2023/12/06/17878416.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 第二单元 Http 概述