浏览器层面缓存 Etag & If-None-Match等详解

Expires、Cache-Control、Last-Modified和If-Modified—Since、Etag和If-None-Match 1.Expires 页面的初次访问者会进行很多HTTP请求,但是通过使用一个长久的Expires头,可以使这些组件被缓存,下次访问的时候,就可以减少不必要的HTPP请求,从而提高加载速度。 Web服务器通过Expires头告诉客户端可以使用一个组件的当前副本,直到指定的时间为止。例如: Expires: Fri, 18 Mar 2016 07:41:53 GMT Expires缺点: 它要求服务... 阅读详情

一、概述 
缓存通俗点,就是将已经得到的‘东东’存放在一个相对于自己而言,尽可能近的地方,以便下次需要时,不会再二笔地跑到起始点(很远的地方)去获取,而是就近解决,从而缩短时间和节约金钱(坐车要钱嘛)。Web缓存,也是同样的道理,说白了,就是当你第一次访问网址时,将这个东东(representations),如html页面、图片、JavaScript文件等,存在一个离你较近的地方,当你下次还需要它时,不用再一次跋山涉水到服务器(origin servers)去获取。继而,web缓存的优势也就很明显了:

  1、 减少了网络延迟,加快了页面响应速度,增强了用户体验嘛。(因为我是就近获取的,路程缩短了,所以响应速度当然比到遥远的服务器去获取快哦);

  2、 减少了网络带宽消耗嘛。(就近获取);

  3、 通过缓存,我们都不用到服务器 (origin servers)去请求了,从而也就相应地减轻了服务器的压力。

那web缓存将这些东东放在哪儿呢?下面我就看看有哪些缓存种类,从而了解放在哪吧。

二、Web缓存的种类

--数据库缓存--:

当web应用关系复杂,数据表蹭蹭蹭往上涨时,可以将查询后的数据放到内存中进行缓存,下次再查询时,就直接从内存缓存中获取,从而提高响应速度。

--CDN缓存--:

CDN通俗点,就是当我们发送一个web请求时,会先经过它一道手,然后它帮我们计算路径,去哪得到这些东东(representations)的路径短且快。这个是网站管理员部署的,所以他们也可以将大家经常访问的representations放在CDN里,这样,就响应就更快了。

--代理服务器缓存--:

代理服务器缓存,其实跟下面即将讲的浏览器缓存性质差不多,差别就是代理服务器缓存面向的群体更广,规模更大而已。即,它不只为一个用户服务,一般为大量用户提供服务,同一个副本会被重用多次,因此在减少相应时间和带宽使用方面很有效。

--浏览器缓存--:

简而言之,就是,每个浏览器都实现了 HTTP 缓存,我们通过浏览器使用HTTP协议与服务器交互的时候,浏览器就会根据一套与服务器约定的规则进行缓存工作。当我们点击浏览器上‘后退’或者‘前进’按钮时,显得特别有用。

三、Web缓存的执行机制

所谓机制就是一些双方的约定,清晰地告诉对方,什么时候该做什么事。web缓存也一样,你总得告诉我(请求)什么时候到缓存中去获取,什么到服务器去获取representations吧。So,也得有一套相应的机制,web 缓存机制分为两大部分http协议(HTTP1.0和HTTP1.1)和网站管理人员制定的协议。抛开网站内部制定的协议,我们来看看http协议中定义的缓存机制。

By the way,我们可以在HTML文档中的<head>中通过<meta>来缓存,如下:

<meta http-equiv="Pragma" content="no-cache"/>

但,它只有部分浏览器可以用,并且代理服务器也不会鸟它。(因为meta在html中,代理服务器几乎不回去读它滴)。

--http缓存机制--

1、 Expires

http缓存机制主要在http响应头中设定,响应头中相关字段为Expires、Cache-Control、Last-Modified、If-Modified-Since、Etag。

HTTP 1.0协议中的。简而言之,就是告诉浏览器在约定的这个时间前,可以直接从缓存中获取资源(representations),而无需跑到服务器去获取。

另:Expires因为是对时间设定的,且时间是Greenwich Mean Time (GMT),而不是本地时间,所以对时间要求较高。

2、 Cache-Control

HTTP1.1协议中的,因为有了它,所以可以忽略上面提到的Expires。因为Cache-Control相对于Expires更加具体,细致。

且,就算同时设置了Cache-Control和Expires,Cache-Control的优先级也高于Expires。

下面就来看看,Cache-Control响应头中常用字段的具体含义:

  (1)、max-age:用来设置资源(representations)可以被缓存多长时间,单位为秒;

  (2)、s-maxage:和max-age是一样的,不过它只针对代理服务器缓存而言;

  (3)、public:指示响应可被任何缓存区缓存;

  (4)、private:只能针对个人用户,而不能被代理服务器缓存;

  (5)、no-cache:强制客户端直接向服务器发送请求,也就是说每次请求都必须向服务器发送。服务器接收到请求,然后判断资源是否变更,是则返回新内容,否则返回304,未变更。这个很容易让人产生误解,使人误以为是响应不被缓存。实际上Cache-Control: no-cache是会被缓存的,只不过每次在向客户端(浏览器)提供响应数据时,缓存都要向服务器评估缓存响应的有效性。

  (6)、no-store:禁止一切缓存(这个才是响应不被缓存的意思)。

3、 Etag & If-None-Match

复制代码

HTTP/1.1 200 OK
Date: Fri, 30 Oct 1998 13:19:41 GMT
Server: Apache/1.3.3 (Unix)
Cache-Control: max-age=3600, must-revalidate
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Last-Modified: Mon, 29 Jun 1998 02:28:12 GMT
ETag: "3e86-410-3596fbbc"
Content-Length: 1040
Content-Type: text/html

复制代码

Etag是属于HTTP 1.1属性,它是由服务器生成返回给前端,

当你第一次发起HTTP请求时,服务器会返回一个Etag,

并在你第二次发起同一个请求时,客户端会同时发送一个If-None-Match,而它的值就是Etag的值(此处由发起请求的客户端来设置)。

然后,服务器会比对这个客服端发送过来的Etag是否与服务器的相同,

如果相同,就将If-None-Match的值设为false,返回状态为304,客户端继续使用本地缓存,不解析服务器返回的数据(这种场景服务器也不返回数据,因为服务器的数据没有变化嘛)

如果不相同,就将If-None-Match的值设为true,返回状态为200,客户端重新解析服务器返回的数据

说白了,
ETag 实体标签: 一般为资源实体的哈希值
即ETag就是服务器生成的一个标记,用来标识返回值是否有变化。

且Etag的优先级高于Last-Modified。

3xx
301 Move Permanently
302 Found
304 Not Modified

4、 Last-Modified & If-Modified-Since

Last-Modified与Etag类似。不过Last-Modified表示响应资源在服务器最后修改时间而已。与Etag相比,不足为:

  (1)、Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间;

  (2)、如果某些文件会被定期生成,当有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存;

  (3)、有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形。

然而,Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。

四、扩展阅读

[1]、"Caching Tutorial"

RFC 7232              HTTP/1.1 Conditional Requests            June 2014

   This method relies on the fact that if two different responses were
   sent by the origin server during the same second, but both had the
   same Last-Modified time, then at least one of those responses would
   have a Date value equal to its Last-Modified time.  The arbitrary
   60-second limit guards against the possibility that the Date and
   Last-Modified values are generated from different clocks or at
   somewhat different times during the preparation of the response.  An
   implementation MAY use a value larger than 60 seconds, if it is
   believed that 60 seconds is too short.

2.3. ETag

   The "ETag" header field in a response provides the current entity-tag
   for the selected representation, as determined at the conclusion of
   handling the request.  An entity-tag is an opaque validator for
   differentiating between multiple representations of the same
   resource, regardless of whether those multiple representations are
   due to resource state changes over time, content negotiation
   resulting in multiple representations being valid at the same time,
   or both.  An entity-tag consists of an opaque quoted string, possibly
   prefixed by a weakness indicator.

     ETag       = entity-tag
     entity-tag = [ weak ] opaque-tag
     weak       = %x57.2F ; "W/", case-sensitive
     opaque-tag = DQUOTE *etagc DQUOTE
     etagc      = %x21 / %x23-7E / obs-text
                ; VCHAR except double quotes, plus obs-text
      Note: Previously, opaque-tag was defined to be a quoted-string
      ([RFC2616], Section 3.11); thus, some recipients might perform
      backslash unescaping.  Servers therefore ought to avoid backslash
      characters in entity tags.
   An entity-tag can be more reliable for validation than a modification
   date in situations where it is inconvenient to store modification
   dates, where the one-second resolution of HTTP date values is not
   sufficient, or where modification dates are not consistently
   maintained. 

  Examples:
     ETag: "xyzzy"
     ETag: W/"xyzzy"
     ETag: ""

Spring MVC 中实现:

The ETag or entity tag is part of HTTP, the protocol for the World Wide Web. It is one of several mechanisms that HTTP provides for web cache validation, which allows a client to make conditional requests. This allows caches to be more efficient, and saves bandwidth, as a web server does not need to send a full response if the content has not changed. ETags can also be used for optimistic concurrency control,[1] as a way to help prevent simultaneous updates of a resource from overwriting each other.

An ETag is an opaque identifier assigned by a web server to a specific version of a resource found at a URL. If the resource representation at that URL ever changes, a new and different ETag is assigned. Used in this manner ETags are similar to fingerprints, and they can be quickly compared to determine whether two representations of a resource are the same.
https://en.wikipedia.org/wiki/HTTP_ETag
 

17.13 ETag support
An ETag (entity tag) is an HTTP response header returned by an HTTP/1.1 compliant web server used to determine change in content at a given URL. It can be considered to be the more sophisticated successor to the Last-Modified header. When a server returns a representation with an ETag header, the client can use this header in subsequent GETs, in an If-None-Match header. If the content has not changed, the server returns 304: Not Modified.

Support for ETags is provided by the Servlet filter ShallowEtagHeaderFilter. It is a plain Servlet Filter, and thus can be used in combination with any web framework. The ShallowEtagHeaderFilter filter creates so-called shallow ETags (as opposed to deep ETags, more about that later).The filter caches the content of the rendered JSP (or other content), generates an MD5 hash over that, and returns that as an ETag header in the response. The next time a client sends a request for the same resource, it uses that hash as the If-None-Match value. The filter detects this, renders the view again, and compares the two hashes. If they are equal, a 304 is returned. This filter will not save processing power, as the view is still rendered. The only thing it saves is bandwidth, as the rendered response is not sent back over the wire.

You configure the ShallowEtagHeaderFilter in web.xml:

<filter>
  <filter-name>etagFilter</filter-name>
    <filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</filter-class>
</filter>

<filter-mapping>
  <filter-name>etagFilter</filter-name>
  <servlet-name>petclinic</servlet-name>
</filter-mapping>

https://docs.spring.io/spring-framework/docs/3.2.0.M2/reference/html/mvc.html#mvc-exceptionhandlers

 

Restful If-Modified-Since Last-Modified Http缓存、本地缓存 Etag 浏览器发送HTTP请求时,把If-Modified-Since一起发到服务器去,服务器会把这个时间与服务器上实际文件的最后修改时间进行比较。响应200,http头有标签:Last-Modified(date),即服务器本地记录的文件最后修改时间。如果时间不一致,就返回HTTP状态码200和新的文件内容,客户端接到之后,会丢弃旧文件,把新文件缓存起来,并显示到浏览器中。浏览器第二次请求时,携带If-Modified-Since(date),请求: 携带If-Modifie-since。 阅读详情

相关推荐

利用ETag和If-None-Match实现高效资源缓存

ETag是HTTP响应头之一,用于标识资源的唯一版本。当资源发生变化时,ETag值也会相应改变。而是HTTP请求头,用于携带客户端缓存中的ETag值。服务器通过比较请求头中的ETag和服务器端资源的ETag来判断资源是否发生变化,从而决定是否需要发送完整的响应。与和相比,ETag机制更为通用和高效,因为它不仅仅依赖于资源的最后修改时间,还可以基于资源内容生成唯一的标识符。通过使用ETag和,我们可以有效地控制资源的缓存,减少不必要的网络传输,提高Web应用的性能。

m0_62153576的博客 562

HTTP 协议请求头 If-Match、If-None-Match 和 ETag

在 HTTP 协议中,请求头If-MatchIf-Range主要是为了解决浏览器缓存数据而定义的请求头标准,按照协议规范正确的判断和使用这几个请求头,可以更精准的处理浏览器缓存,从而达到提高系统性能和减少系统带宽的占用的目的。1、 减少了网络交互,加快页面响应速度,增强用户体验;2、 减少了网络带宽消耗,因为没有更新的资源就不需要重复返回了,特别是图片、视频、下载文件这类大响应体请求;当请求中存在上述If-xxx。

小单的博客专栏 7789

当迁移学习遇见室内定位:基于UJIIndoorLoc的跨场景适配实践

本文探讨了迁移学习在跨建筑室内定位中的应用,重点解析了UJIIndoorLoc数据集的特性及其在解决设备差异和环境变化中的关键作用。通过特征工程、迁移学习策略和小样本微调技术,显著提升了定位模型的跨场景适配能力,为实际部署提供了有效解决方案。

weixin_29038155的博客 394

Etag & If-None-Match 专题

一、概述 缓存通俗点讲,就是将已经得到的‘东东’存放在一个相对于自己而言,尽可能近的地方,以便下次需要时,不会再二笔地跑到起始点(很远的地方)去获取,而是就近解决,从而缩短时间和节约金钱(坐车要钱嘛)。Web缓存,也是同样的道理,说白了,就是当你第一次访问网址时,将这个东东(representations),如html页面、图片、JavaScript文件等,存在一个离你较近的地方,当你下次还需要它...

weixin_34007886的博客 737

手把手教你用STM32的ADC读取PT100模块,实现高精度温度采集(附完整代码)

本文详细介绍了如何利用STM32的ADC模块读取PT100温度传感器信号,实现高精度温度采集系统。从硬件选型、ADC配置校准到温度计算算法和软件滤波技术,提供完整的实现方案和优化技巧,帮助工程师快速构建稳定可靠的温度监测系统。

weixin_29185199的博客 347

OSMDroid 切换地图不更新的问题

用OSMDroid加载Google地图的时候 需要有按钮切换不同图层的地图,如果发现调用了mMapView.setTileSource(ITileSource aTileSource)方法的时候发现地图View没有更新的话,可能是因为两个实例化TileSource对象其子类BitmapTileSourceBase这个类的aName成员变量值一样导致的地图在setTileSource出现不更新问题,...

godoge的博客 1039

Etag和if-None-Match

前言 最近在跟后台对接口时,发现请求同一个接口时总是一次成功一次失败这样间隔着来,很是困扰。请求失败的时候报的错时502 Not Modified,这个报错也是百思不得解。在我掌握的知识里502时服务器错误,而Not Modified对应的状态码应该时304,但这样的报错提示是从来没有遇到过。另外这个错误也只是在本地跑的时候会有,放到线上的环境就很正常,第二次请求时也是304而不会报错。得闲我比...

weixin_33941350的博客 2544

手把手教你设计T型和Π型衰减器:从分贝计算到电阻选型全攻略

本文提供了一份T型和Π型衰减器的详细设计指南,涵盖从分贝计算到电阻选型的全过程。文章以50欧姆系统为例,通过公式推导和实例演示,手把手教你计算两种经典拓扑的电阻值,并深入探讨了电阻选型、误差控制及高频应用等关键实践技巧,帮助工程师和爱好者精准实现信号衰减与阻抗匹配。

blue42的博客 1337

Nginx 客户端缓存Etag和If-None-Match

浏览器缓存和Nginx缓存 使用缓存是最有效的提高访问速度的方法,在web服务器的场景当中不仅要考虑nginx作为缓存服务时候的使用方法还得考虑浏览器中的缓存失效的场景,因为浏览器缓存是否生效可以通过nginx指令去控制。浏览器的缓存对用户体验也是最大的。 使用浏览器缓存,即本地缓存,只需要读取本地的磁盘或者内存就可以了。通常是同时使用浏览器和nginx缓存。 Etag头部 Etag是存在于HTTP响应当中的,是Nginx向浏览器返回时添加的,它来标识一个特定版本的资源,Etag的值是通过上次.

小楼一夜听春雨,深巷明朝卖杏花 6075

嵌入式软考必备知识点:嵌入式系统开发中的重要概念和示例代码

在嵌入式软考中,有一些重要的知识点需要掌握,包括嵌入式系统的基本概念、编程语言、实时操作系统和硬件接口等。实时操作系统是一种专门设计用于嵌入式系统的操作系统,可以提供对任务调度、中断处理和资源管理等方面的支持。GPIO(通用输入输出):GPIO是嵌入式系统中常用的硬件接口,用于连接外部设备和嵌入式系统。以上是嵌入式软考中的一些必备知识点,包括嵌入式系统的基本概念、编程语言、实时操作系统和硬件接口。通过配置串口的波特率、数据位数和校验位等参数,可以实现与其他设备的可靠通信。一、嵌入式系统的基本概念。

PixeGO的博客 314

时间序列—显著相关性和滞后性分析_python

本文讲述了两个时间序列(信号)的相关性分析,可以利用相关性分析进行特征筛选。此外本文还讲了怎么判断时间序列的滞后性的方法。

qq_34229228的博客 2万+

0008__浏览器层面缓存 Etag & If-None-Match等详解

浏览器层面缓存 Etag & If-None-Match等详解_if-none-match:_shadow_zed的博客-优快云博客

行潇 545

网络攻防——kali操作系统基本使用

主要介绍如何搭建kali虚拟机,然后介绍了一个简单的攻击方法,切记,网络恶意攻击属于违法行为

weixin_46560512的博客 2万+

HTTP缓存机制的Etag、Last-Modified、If-None-Match和If-Modified-Since、Expires和Cache-Control笔记

http缓存机制:https://blog.youkuaiyun.com/jutal_ljt/article/details/80021545 http://www.cnblogs.com/chenqf/p/6386163.html 相关文章: 闲聊HTTP闲聊HTTP/2.0闲聊HTTPSHTTP缓存机制的Etag、Last-Modified、If-None-Match和If-Modified-...

卓越无关环境,保持空杯心态——靡不有初,鲜克有终 6115

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Http浏览器缓存 ETag,Cache-Control,Expires,Last-Modified,if-none-match,if-modified-since 各自是什么作用

ETag,Cache-Control,Expires,Last-Modified,if-none-match,if-modified-since 各自是什么作用

hongkid的专栏 906

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Etag & If-None-Match专题

一、概述 缓存通俗点讲,就是将已经得到的‘东东’存放在一个相对于自己而言,尽可能近的地方,以便下次需要时,不会再二笔地跑到起始点(很远的地方)去获取,而是就近解决,从而缩短时间和节约金钱(坐车要钱嘛)。Web缓存,也是同样的道理,说白了,就是当你第一次访问网址时,将这个东东(representations),如html页面、图片、JavaScript文件等,存在一个离你较近的地方,当你下次还需要...

u011679955的博客 509

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

php if none match,关于Etag/If-None-Match的用法

关于Last-Modified/If-Modified-Since及Etag/If-None-Match的用法相关代码if (method_exists($controller, $action)) {ob_start();$controller -&gt; {$action}();$content = ob_get_contents();ob_end_clean();$etag = md5($c...

weixin_39543652的博客 936

HTTP 协商缓存 ETag、If-None-Match

(3)服务器再次收到资源请求时,根据浏览器传过来If-None-Match和然后再根据资源生成一个新的ETag,如果这两个值相同就说明资源没有变化,否则就是有变化;与Last-Modified不一样的是,当服务器返回304 Not Modified的响应时,由于ETag重新生成过,response header中还会把这个ETag返回,即使这个ETag跟之前的没有变化。(1)浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone header加上ETag。

小楼一夜听春雨,深巷明朝卖杏花 1281

Etag & If-None-Match 专题

Etag &amp;amp; If-None-Match 专题一、概述缓存通俗点,就是将已经得到的‘东东’存放在一个相对于自己而言,尽可能近的地方,以便下次需要时,不会再二笔地跑到起始点(很远的地方)去获取,而是就近解决,从而缩短时间和节约金钱(坐车要钱嘛)。Web缓存,也是同样的道理,说白了,就是当你第一次访问网址时,将这个东东(representations),如html页面、图片、JavaScri...

xiaoWhite_meng的博客 523

缓存验证:Last-Modified / If-Modified-Since 和 ETag / If-None-Match

特性ETag精度秒级字节级(更精确)唯一性基于时间戳,可能重复唯一标识符,每次变更都会变性能开销较低(只需比较时间)略高(需计算哈希等)兼容性所有 HTTP/1.0 及以上支持需要 HTTP/1.1 或以上适用场景简单静态资源缓存验证动态资源、CDN、负载均衡环境。

求职大学生 596

Last-Modified If-Modified-Since ETag If-None-Match

Last-Modified、If-Modified-Since 有些数据随时都在变化。 CNN.com 的主页经常几分钟就更新。另一方面,Google.com 的主页几个星期才更新一次 (当他们上传特殊的假日 logo,或为一个新服务作广告时)。 Web 服务是不变的:通常服务器知道你所请求的数据的最后修改时间,并且 HTTP 为服务器提供了一种将最近修改数据连同你请求的数据一同发送的...

adb96102的博客 188

缓存验证 Last-Modified & Etag

上篇文章我们知道,当我们给Catch-Control 设置了 no-catch 后,每次浏览器对这个资源的请求时,都会到服务器端进行资源验证,验证完之后,如果确定这个资源可以使用缓存,浏览器才会读取本地的缓存。 下面是浏览器请求数据过程中关于缓存的步骤。 进行数据验证,主要是有两个Header: Last-Modified 与 Etag . Last-Modified 即上次修改时间。 ...

purple_lumpy的博客 1212

http Cache-Control: If-None-Match ETag/If-Modifed-Since Last-Modified

这里最终决定采用chunked是为了和长连接[3]配合使用, 省去slow-start ack/congestion window 增加到正常大小的时间,还有3次握手时间(究竟是多少毫秒?) 对原先代码的改造,逻辑上从上到下分层为 file: >0 -- ok, 0 -- eof, -1 -- err, -11 -- eagain. prepare: 0 -- ok, 0 -- sta

deyangliu的专栏 859

Golang怎么实现ETag缓存控制_Golang如何用ETag和If-None-Match实现HTTP缓存验证【技巧】

理想 ETag 应该是内容强相关:比如 md5(content) 或 sha256(content) 的十六进制摘要若资源大或计算成本高,可用弱 ETag(前缀 W/"..."),例如基于修改时间和大小组合:W/"12345-67890"避免用数据库自增 ID、随机数、UUID——它们和内容无关,无法保证语义一致性Go 标准库里怎么生成和校验 ETag标准库没有自动 ETag 支持,得自己在 handler 里手动处理 If-None-Match 请求头和 ETag 响应头。

kexnjdcncnxjs的博客 211
上一篇: JWT(Json Web Token) 详解 与 java 实战
下一篇: 自定义Java注解处理器
shadow_zed
博客等级 码龄10年 6289粉丝 68原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值