第二单元 Http 概述

第二单元 Http 概述 第二单元 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协议是一种未进行加密处理,由服务器传输超文本到本地浏览器传输协议。

特点:

  1. 基于TCP/IP的高级协议 (Socket)

  2. 默认端口号:80

  3. 基于请求/响应模型的:一次请求对应一次响应

  4. 无状态的:每次请求之间相互独立,不能交互数据

 

 

3. Http的前世今生

 

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

  1. HTTP协议在应用层

  2. 最初的目的是为了提供一种发布和接收HTML页面的方法

  3. 规定了客户端和服务器之间通信格式

 

4. Http 的通信流程

  1. 建立TCP连接

  2. 客户端向服务端发送命令

  3. 客户端发送请求头信息

  4. 服务器应答

  5. 服务器发送应答头信息

  6. 服务器向客户端发送数据(静态资源,html/css/js)

  7. 服务器关闭TCP连接

 

一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码: Connection:keep-alive TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。

 

5. URL与URI的区别

什么是URI

URI 统一资源标识符(Uniform Resource Identifiers, URI),用来唯一识别一个资源,可以把它理解为你的身份证号。

作用:Web上可用的资源如HTML文档,图像,视频等都是以URI来定位的。

 

什么是URL

URL 统一资源定位符( Uniform Resource Locator ),可以把它理解为你身份证上地址。 是互联网上用来标识某一处资源的地址。

 

作用:

  1. 可以用来标识一个资源,而且还指明了如果定位这个资源

  2. URL是internel 上用来描述信息资源的字符串,主要用在各种www 程序上。

 

区别

  1. URI是一种抽象的,高层次概念定义统一资源标识

  2. 每个URL都是一个URI,但每一个URI并不一定是URL,因为URI 还包括另外一个子类URN(统一资源命名),它命名资源但不负责定位资源(姓名+你的地址 = 你的身份证号)

  3. URL就是通过定位的方式来实现URI的。

 

6. 消息数据格式

请求消息格式

  1. 请求行

    请求方式 请求url 请求协议/版本 
    GET /login.html HTTP/1.1

     

     

  2. 请求方式:

    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:

      1. 请求参数在请求行中,在url后。

      2. 请求的url长度有限制的

      3. 不太安全

      4. 可被收藏到书签,也可被缓存

    • POST:

      1. 请求参数在请求体中

      2. 请求的url长度没有限制的

      3. 相对安全

  3. 请求头:客户端浏览器告诉服务器一些信息

    请求头名称: 请求头值 

     

    常见的请求头:

    1. User-Agent:浏览器告诉服务器,我访问你使用的浏览器版本信息

      作用: 可以在服务器端获取该头的信息,解决浏览器的兼容性问题

    2. Referer:http://localhost/login.html ,告诉服务器,我(当前请求)从哪里来。

      作用:

      1. 防盗链:

      2. 统计工作:

  4. 请求空行 :就是用于分割POST请求的请求头,和请求体的。

  5. 请求体(正文): 封装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的一些区别

  1. HTTPS协议需要到CA申请证书,一般免费证书很少,需要交费。

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

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

  4. 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 上下文中包含了一些非常重要的对象信息:

  • Request 属性

    HttpRequest获取此请求的对象, 表示单个 HTTP 请求的传入端。

  • RequestServices

    获取或设置 IServiceProvider 提供对请求的服务容器的访问权限。

  • Response 对象

    HttpResponse获取此请求的对象,表示单个 HTTP 请求的传出端。

  • Session 对象

    获取或设置用于管理此请求的用户会话数据的对象。

  • User 对象

    获取或设置此请求的用户。

 

3. HttpRequest 请求

表示单个 HTTP 请求的传入端。

HttpContext获取或设置当前 HttpContext。 如果没有活动HttpContext状态,则返回 null
BodyReader获取请求正文 PipeReaderContentLength获取或设置 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[0]
      姓名:任我行
info: WebApplication2.Controllers.HomeController[0]
      学号: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属性

Body获取或设置响应正文 Stream
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

 

  1. 设置响应语言

    HttpContext.Response.Headers.Add("Content-Language","zh-CN"); 

     

  2. 下载文件

    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();
    
    }
     


 

配套视频链接:Asp.Net Mvc Core 6.0 详细教程 - 网易云课堂 (163.com)

海阔平鱼跃,天高任我行,给我一片蓝天,让我自由翱翔。
评论
  
属性备注例如