ASP.NET的状态管理详解以及总结

一、 状态管理的基本概念

1、 什么是“状态管理”?——数据缓存数据传递

所谓“状态管理”,是指使用ASP.NET中的ViewStateCookieSessionApplication等对象实现页面数据缓存和传递的技术。

2、  http协议本质上是“无状态的”。ASP.NET是一种无状态的网页连接机制,服务器处理客户端请求的网页后,与该客户端的连接就中断了。此外,到服务器端的每次往返都将销毁并重新创建网页,因此,如果超出了单个网页的生存周期,网页中的信息将不复存在。也就是说,在默认情况下,服务器不会保存客户端再次请求页面和本次请求之间的关系和相关数据。

3、 C/S架构的应用程序中,使用全局变量即可很好地解决这个问题,而在ASP.NET环境中则需要使用与状态管理相关的对象来保存用户数据。

传统的C/S架构比如数据库,

4、 为什么要进行“状态管理”?

B/SC/S两种架构的有着完全不同的运行机制。C/S基本上所有的软件功能都在客户端(Client)中实现(所以C/S也称为胖客户端架构),服务器端(Server)只提供基础服务,最为典型的是数据库服务提供数据服务。而B/S架构主要的功能在服务端(Server)实现,客户端的浏览器(Browser)只接受用户输入和数据展现。C/S架构的功能都在客户端的一个进程中完成,客户端与服务器端的连接基本上保持同步,只需要记录两者的连接状态而不存在其他的状态要管理。
     
而在B/S架构中,客户端和服务之间的连接并不保持同步。我们来看看B/S架构客户端与服务器端的交互方式。客户端浏览器每次将网页发送到服务器时,服务器都会创建网页类的一个新的实例。在传统的Web编程中(指那些没有提供内置的状态管理机制的B/S开发平台),这通常意味着在每一次往返过程中,与该页及该页上的控件相关联的信息都会丢失。如用户将信息输入到页面的文本框中,该信息从浏览器传到服务器的往返过程中将丢失,所以状态管理机制成为B/S架构的一个重点。如果我们要深入学习B/S结构,研究其状态管理机制成为必经之路而无法逾越,本文就来探讨当前市场最为流行的B/S架构——ASP.NET框架的状态管理机制。

 

二、 状态管理的分类

状态管理主要分为以下8个类型:

1、 基于客户端(浏览器Browser的状态管理选项。基于客户端的状态管理选项只在页中或客户端计算机上存储信息,在各往返行程间不会在服务器上维护任何信息

 

视图状态——ViewState

控件状态——ControlState

隐藏域  ——Hidden-input

Cookie  ——

查询字符串——QueryString

 

2、 基于服务器Server的状态管理选项。

应用程序状态——Application

会话状态    ——Session

配置文件属性——Profile

总结:“状态管理”

虽然分为8个类型,但从总的来说,它主要还是从三个级别来实现的,即应用程序级别(Application)、会话级别(Session)、页面级别(Page)。

 

三、 每一种状态管理的详细介绍

1、视图状态(ViewState
   
视图状态提供一个字典对象,用于在对同一页的多个请求之间保留值。这是页用来在往返行程之间保留页和控件属性值的默认方法。在处理页时,页和控件的当前状态会散列为一个字符串,并在页中保存为一个隐藏域或多个隐藏域。当将页回发到服务器时,页会在页初始化阶段分析视图状态字符串,并还原页中的属性信息。

1)主要特点

n  ViewState对象是ASP.NET状态管理中常用的一个对象,它通常被用来保存Web页信息及所含控件的值。

n  使用ViewState对象可以十分方便地在这一过程中保留当前页面中所包含的数据。

n  Web页面默认是具有“formrunat=server”特性的,ASP.NET会自动在输出时给页面添加一个隐含字段。打开一个aspx页面后,在浏览器中右击,在弹出的快捷菜单中单击“查看源”,即可看到经服务器转换成HTML格式后的页面源代码。

2)注意事项

使用ViewState(视图状态)对象可以带来很多方便,但仍需要注意以下问题。

n  ViewState对象仅能提供当前页面对象的状态信息,而且这些信息不能跨页使用。

n  ViewState对象被序列化为XML的形式,然后再进行Base64编码。

n  ③ 若页面中包含有一些用于分页显示数据的控件(如GridView),最好不要使用ViewState对象。

n  ViewState对象的MaxPageStateFieldLength属性用来指定隐含字段的最大长度。

3启用或禁用ViewState

n  启用或禁止ViewState保存某控件的信息,可以通过设置控件的EnableViewState属性来实现。该属性值指示服务器控件是否向发出请求的客户端,保持自己的视图状态以及它所包含的任何子控件的视图状态。如果允许控件维护自己的视图状态,则应设置为true(默认值);否则,应设置为false

n  仅当EnableViewState属性设置为true时,页面或控件的ViewStateMode属性才起作用。如果EnableViewState属性设置为false,则即使ViewStateMode属性设置为Enabled,视图状态也将关闭。

4)使用ViewState对象

n  ViewState对象以“键/值对”的方式保存控件的名称和对应的值,以便在回发时还原控件的原始状态。对于控件的值保存和回发恢复,可由系统自动完成,一般不必为此编写专门的代码。若用户希望将一些特殊的数据保存到ViewState对象中,则可以使用ViewState对象的Add()方法。其语法格式为:

n  ViewState.Add(键名称, )

n  ViewState中读取值的语法格式为:

n  ViewState[键名称]

5)本质定义

ViewState属性是定义在Control类中的一个字典属性,原型如下:

//获取状态信息的字典,这些信息使您可以在同一页的多个请求间保存和还原服务器控件的视图状态。

protectedvirtualStateBag ViewState { get; }  //它返回的是StateBag类型,该类型定义如下

publicsealedclassStateBag : IStateManager, IDictionary, ICollection, IEnumerable

{……}

//由此可见,为什么说它是一个字典,因为它实现了上面的这些接口。

 

2、Cookie

Cookie是一些少量的数据,这些数据或者存储在客户端文件系统的文本文件中,或者存储在客户端浏览器会话的内存中。Cookie包含站点的信息,这些信息是随页一起由服务器发送到客户端的。Cookie可以是临时的(具有特定的过期时间和日期),也可以是永久的。
   
可以使用Cookie来存储有关特定客户端、会话或应用程序的信息。Cookie保存在客户端上,当浏览器请求某页时,客户端会将Cookie中的信息连同请求信息一起发送。服务器可以读取Cookie并提取它的值。一项常见的用途是存储标记,以指示该用户已经在Web应用程序中进行了身份验证。
   
浏览器只能将数据发送回最初创建该Cookie的服务器。但是,恶意用户可通过多种方法访问Cookie并读取其中的内容。建议不要将敏感信息(如用户名或密码)存储在Cookie中。可以在Cookie中存储一个标识用户的标记,然后使用该标记在服务器上查找敏感信息。

(1)Cookie的理解

n  Cookie是由服务器发送给客户机,并保存在客户机中的一些记录用户数据的文本文件。当用户访问网站时,Web服务器会发送一小段资料存放在客户机中,它会把用户在网站上所打开的网页内容、在页面中进行的选择或者操作步骤逐一记录下来。当用户再次访问同一网站时(可能并不是相同的网页),Web服务器会首先查找客户机中是否存在有上次访问网站时留下的Cookie信息。若有,则会根据具体Cookie信息发送特定的网页给用户。

n  在保存用户信息和维护浏览器状态方面,使用Cookie无疑是一种很好的方法。例如,可以将用户的登录信息(用户名、密码、是否登录成功的状态等)存放在Cookie中,方便应用程序对用户的合法性进行快速检查。

2创建Cookie

n  浏览器负责管理客户机中的CookieCookie需要通过Response对象发送到浏览器中,发送前需要将其添加到Cookie集合中。(Response对象参见后面的讲述

n  Cookie3个重要的参数:名称、值和有效期。如果没有设置Cookie的有效期,它仍可被创建,但不会被Response对象发送到客户端,而是将其作为用户会话的一部分进行维护,当用户关闭浏览器(会话结束)时该Cookie将被释放。这种非永久性Cookie十分适合用来保存只需要短暂保存或由于安全原因不能保存在客户机中的信息。

n  创建Cookie的语法格式如下:

n  Response.Cookies["名称"].Value = ;

下面是一个创建Cookie并设置有效期的实例

例如,创建一个名为“MyCookie”Cookie并为其赋值“OK”,语句如下:

Response.Cookies["MyCookie"].Value= "OK";

设置Cookie有效期的语法格式如下:

Response.Cookies["名称"].Expires= 到期时间;

例如,设置名为“MyCookie”Cookie有效期为1天,语句如下:

Response.Cookies["MyCookie"].Expires= DateTime.Now.AddDays(1);

 

3读取Cookie

n  使用Request对象的Cookies属性可以读取保存在客户机中指定Cookie的值,其语法格式如下(Request对象将会在后面讲到):

n  变量 = Request.Cookies["名称"].Value;

例如,将名为“MyCookie”Cookie值读出,并赋给变量GetCookie,语句如下:

string GetCookie= ""  //声明一个字符串变量

if(Request.Cookies["MyCookie"] != null)   //判断目标Cookie是否存在

{

GetCookie =Request.Cookies["MyCookie"].Value; //读取指定Cookie的值,赋给变量

}

4使用多值Cookie

n  前面介绍过对同一网站,客户端存储的Cookie数量不能超过20个。若需要存储较多的数据,可考虑使用多值Cookie

n  例如,创建一个名为“Person”Cookie集合,其中包含有3个子属性(对于浏览器来说,只相当于一条Cookie),语句如下:

Response.Cookies["Person"]["P_Name"].Value= "zhangsan";

Response.Cookies["Person"]["P_Email"].Value= "zs@163.com";

Response.Cookies["Person"]["P_Home"].Value= "北京";

n  使用下列语句可从上述多值Cookie中读取数据:

yr_name =Request.Cookies["Person"]["P_Name"].Value;

yr_email =Request.Cookies["Person"]["P_Email"].Value;

yr_home =Request.Cookies["Person"]["P_Home"].Value;

string yr_name =Request.Cookies["Person"].Values[0];

string yr_name =Request.Cookies["Person"].Values[1];

string yr_name =Request.Cookies["Person"].Values[2];

 

 

3、控件状态(ControlState)

视图状态,控件状态,二者是类似,在页面中表现为一个hidden-input元素:

<input type="hidden"name="__VIEWSTATE" id="__VIEWSTATE"value="......................" />

控件状态是ASP.NET 2.0中引入,与视图状态相比,它不允许关闭。由于它们使用方式一致,而且视图状态是基于控件状态的实现逻辑,所以我就不区分它们了。

有时,为了让控件正常工作,需要按顺序存储控件状态数据。如,如果编写了一个自定义控件,其中使用了不同的选项卡来显示不同的信息。为了让自定义控件按预期的方式工作,该控件需要知道在往返行程之间选择了哪个选项卡。可以使用ViewState属性来实现这一目的,不过,开发人员可能会在页级别关闭视图状态,从而使控件无法正常工作。为了解决此问题,ASP.NET页框架在ASP.NET中公开了一项名为控件状态的功能。ControlState属性允许您保持特定于某个控件的属性信息,且不能像ViewState属性那样被关闭。

4、隐藏域(Hidden-Input

ASP.NET中可以将信息存储在HiddenField控件中,此控件将呈现为一个标准的HTML隐藏域。隐藏域在浏览器中看不到,但在开发过程中可以像对待标准控件一样设置属性。当向服务器提交页时,隐藏域的内容将在HTTP窗体集合中随同其他控件的值一起发送。隐藏域可用作一个储存库,可以将希望直接存储在页中的任何信息放置到其中。
   
但恶意用户可以很容易查看和修改隐藏域的内容。所以不要在隐藏域中存储任何敏感信息或保障应用程序正确运行的信息。
     HiddenField
控件在其Value属性中只存储一个变量,并且必须通过显式方式添加到页上。为了在页处理期间能够使用隐藏域的值,必须使用HTTP POST命令提交相应的页。如果在您使用隐藏域的同时,为了响应某个链接或HTTP GET命令而对页进行了相应处理,隐藏域将不可用。

一般说来,我通常使用hidden-input来保存一些中间结果,用于在多次提交中维持一系列状态,或者用它来保存一些固定参数用来提交给其它页面(或网站)。在这些场景中,我不希望用户看到这些数据,因此,使用hidden-input是比较方便的。

ASP.NET WebForm框架中,我们可以使用HiddenField控件来创建一个hidden-input控件,并可以在服务端操作它,还可以直接以手写的方式使用隐藏域,例如:

<input type="hidden"name="hidden-1" value="aaaaaaa"/>

<input type="hidden"name="hidden-2" value="bbbbbbb"/>

<input type="hidden"name="hidden-3" value="ccccccc"/>

另外,我们还可以调用ClientScript.RegisterHiddenField()方法来创建隐藏域:

ClientScript.RegisterHiddenField("hidden-4","ddddddddd");

输出结果:

<input type="hidden"name="hidden-4" id="hidden-4"value="ddddddddd" />

这三种方法对于生成的HTML代码来说,主要差别在于它们出现位置不同:
1. HiddenField控件:由HiddenField的出现位置来决定(在form内部)。
2. RegisterHiddenField方法:在form标签的开头位置。
3. hidden-input:你写在哪里就是哪里。

优点:
1. 不需要任何服务器资源:隐藏域随页面一起发送到客户端。
2. 广泛的支持:几乎所有浏览器和客户端设备都支持具有隐藏域的表单。
3. 实现简单:隐藏域是标准的 HTML 控件,不需要复杂的编程逻辑。

缺点:
1. 不能在多页面跳转之间维持状态。
2. 用户可见,保存敏感数据时需要加密。

 

 

 

5、查询字符串(QueryString)

查询字符串是在页URL的结尾附加的信息。下面是一个典型的查询字符串示例:http://www.test.com/test.aspx?name=book&price=100
   
在上面的URL路径中,查询字符串以问号(?)开始,并包含两个属性/值对:一个名为“name”,另一个名为“price”。
   
查询字符串提供了一种维护状态信息的方法,这种方法很简单,但有使用上的限制。例如,利用查询字符串可以很容易地将信息从一页传送到另一页。例如,将产品号从一页传送到将处理该产品号的另一页。但是,大多数浏览器和客户端设备会将URL的最大长度限制为2083个字符。
   
在查询字符串中传递的信息可能会被恶意用户篡改。不要依靠查询字符串来传递重要的或敏感的数据。此外,用户可以创建URL的书签或将URL发送给其他用户,从而将这些信息与URL一起传递。
   
若要在页处理期间可以使用查询字符串的值,必须使用HTTP GET命令提交页。也就是说,如果为了响应HTTP
  
POST命令而对页进行了相应处理,则不能利用查询字符串。

优点:
1. 不需要任何服务器资源:查询字符串的数据包含在每个URL中。
2. 广泛的支持:几乎所有的浏览器和客户端设备均支持使用查询字符串传递参数值。
3. 实现简单:在服务端直接访问Request.QueryString[]可读取数据。
4. 页面传值简单:<a href="url">或者 Response.Redirect(url) 都可以实现。

缺点:
1. 有长度限制。
2. 用户可见,不能保存敏感数据。

 

下面是服务器端的状态管理方式:

6、会话状态(Session

ASP.NET使用会话状态保存每个活动的Web应用程序会话的值,会话状态是HttpSessionState类的一个实例。
   
会话状态与应用程序状态相似,不同的只是会话状态的范围限于当前的浏览器会话。如果有不同的用户在使用同一个Web应用程序,则每个用户会话都将有一个不同的会话状态。此外,如果同一用户在退出后又返回到应用程序,第二个用户会话的会话状态也会与第一个不同。
   
会话状态采用键/值字典形式的结构来存储特定于会话的信息,这些信息需要在服务器往返行程之间及页请求之间进行维护。
   
可以使用会话状态来完成以下任务:
1.    
唯一标识浏览器或客户端请求,并将这些请求映射到服务器上的单独会话实例。
2.    
在服务器上存储特定于会话的数据,以用于同一个会话内的多个浏览器或客户端请求。
3.    
引发适当的会话管理事件。此外,可以利用这些事件编写应用程序代码。
   
一旦将应用程序特定的信息添加到会话状态中,服务器就会管理该对象。根据指定的选项的不同,可以将会话信息存储在Cookie、进程外服务器中或运行Microsoft SQL Server的计算机中。

CookieViewState不同,Session对象没有对存储数据量的限制,其中可以保存更复杂的数据类型,甚至可以在Session中保存一个DataSet(离线数据集)等。与Cookie对象一样,保存在Session中的数据可以跨网页使用,因此它常用来在不同网页中传递数据。此外,Session是一个存储在服务器端的对象集合,避免了Cookie信息保存在客户端的不安全因素,非常适合用户保存用户名、密码等敏感信息。

ASP.NET中使用Session对象时,必须保证页面的@Page指令中EnableSessionState属性的值被设置为true(默认)或ReadOnly,并且在web.config文件中对Session进行了正确的设置(默认设置为开启Session)。

1Session的工作原理

n  当用户请求一个ASP.NET页面时,系统将自动创建一个Session(会话);当退出应用程序或关闭服务器时,该会话撤销。系统在创建会话时将为其分配一个长长的字符串(SessionID)标识,以实现对会话进行管理和跟踪。该字符串中只包含URL中所允许的ASCII字符。SessionID具有的随机性和唯一性保证了会话不会冲突,也不会被怀有恶意的人利用新SessionID推算出现有会话的SessionID

n  通常,SessionID会存放在客户端的Cookie内,当用户访问ASP.NET网站中任何一个页面时,SessionID将通过Cookie传递到服务器端,服务器根据SessionID的值对用户进行识别,以返回对应于该用户的Session信息。通过配置应用程序,可以在客户端不支持Cookie时将SessionID嵌套在URL中,服务器可以通过请求的URL获得SessionID值。

n  Session信息可以存放在ASP.NET进程、状态服务器或SQL Server数据库中。在默认情况下,Session的生存周期为20分钟,可以通过SessionTimeout属性更改这一设置。在Session的生存周期内,Session是有效的,超过了这个时间Session就会过期,Session对象将被释放,其中存储的信息也将丢失。

2 Session对象的常用属性及方法

 

3Session对象有以下两个事件。

n  Start事件:在创建会话时发生。

n  End事件:在会话结束时发生。需要说明的是,当用户在客户端直接关闭浏览器退出Web应用程序时,并不会触发Session_End事件,因为关闭浏览器的行为是一种典型的客户端行为,是不会被通知到服务器端的。Session_End事件只有在服务器重新启动、用户调用了Session_Abandon()方法或未执行任何操作达到了Session.Timeout设置的值(超时)时才会被触发。

(4)     5.4.3  使用Session对象

ASP.NET中使用Session对象的核心技术就是如何将数据存入Session及如何从Session中读取数据。

1. 将数据保存到Session对象中

n  Session对象中存入数据的方法十分简单,下面的语句使用户单击按钮时将两个字符串分别存入两个Session对象中:

protected void Button1_Click(object sender, EventArgse)

{

   Session["MyVal1"] = "这是Session中保存的数据1";

    string Val2= "这是Session中保存的数据2";

   Session["MyVal2"] = Val2;

}

2. Session对象中取出数据

当目标页面装入时,从Session对象中取出数据的语句如下:

protectedvoid Page_Load(object sender, EventArgs e)

{

Label1.Text= (string)(Session["MyVal1"]);

        Label2.Text =(string)(Session["MyVal2"]);

}

 

7、应用程序状态(Application

1应用程序状态(Application)
     ASP.NET
使用应用程序状态来保存每个活动的Web应用程序的值,应用程序状态是HttpApplicationState类的一个实例对象。应用程序状态是一种全局存储机制,可在Web应用程序中的所有页面访问到。因此,应用程序状态可用于存储需要在服务器往返行程之间及页请求之间维护的信息。
   
应用程序状态存储在一个键/值字典中,在每次请求一个特定的URL期间就会创建这样    一个字典。可以将特定于应用程序的信息添加到此结构以在页请求期间存储它。一旦将应用程序特定的信息添加到应用程序状态中,服务器就会管理该对象。

(2)     5.5.1  Application对象与Session对象的区别

Application对象和Session对象都可在服务器端保存数据或对象,它们的使用方法和常用属性、事件、方法也基本相同。但Application对象中保存的信息是为所有来访的客户端浏览器共享的,而Session对象保存的数据则是仅为特定的来访者使用的。

例如,在河南的A用户和在河北的B用户同时访问某一服务器,若A用户修改了Application对象中存放的信息,B用户在刷新页面后就会看到修改后的内容;但若A用户修改了Session对象中存放的数据,则B用户是感觉不到的。此时只有A用户可以看到和使用这些数据。也就是说,Session对象中存放的是专用信息

35.5.2  Application对象的属性、方法和事件

n  Application对象的常用属性、方法和事件与前面介绍过的Session对象的属性、方法和事件基本相同。

1. Application对象的常用属性和方法

n  由于Application对象中存放的信息是共有的,有可能发生在同一时间内多个用户同时操作同一个Application对象的情况。为了避免此类问题导致的出错,Application对象增加了Lock()方法和UnLock()两个方法,用于在使用set方法更改Application对象值时将其锁定,在更改完毕后再解除锁定。

2. Application事件的常用事件

Application对象的常用事件有如下两个。

n  Start事件:该事件在应用程序启动时被触发。当第一次启动应用程序时会触发Session_Start事件,不过Application_Start事件在Session_Start事件之前发生。它在应用程序的整个生命周期中仅发生一次,此后除非Web服务器重新启动才会再次触发该事件。

n  End事件:Application_End事件在应用程序结束时被触发,即Web服务器关闭或重新启动时被触发。同样,在应用程序结束时也会触发Session_End事件,但Application_End事件发生在Session_End事件之后。在Application_End事件中,常放置用于释放应用程序所占资源的代码段

3  使用Application对象

使用Application对象的属性和方法可方便地读取、写入或修改对象中保存的共享数据。

Application对象写入数据

在向Application对象中保存数据时可使用如下语法格式:

Application["对象名"]= 对象值;

Application.Add("对象名",);

4. 修改Application对象中的数据

修改已存在Application对象中的数据,需要使用Set方法并配合Lock()UnLock()方法,即修改数据前“锁定”对象,修改数据后再“解锁”对象。例如,下列语句实现了对名为“Test”Application对象中保存数据的自身加1操作:

Application.Lock(); //锁定Application对象,以防止其他用户对其进行操作

Application.Set("Test",Application("Test") + 1); //修改已存在Application对象Test的值为自身+1

Application.UnLock(); //解锁Application对象

5. 读取Application对象中的数据

读取Application对象中数据的方法如下:
string User;

User =Application("UserName").ToString();

注意:Application("对象名")的返回值是一个Object类型的数据,操作时应注意数据类型的转换。

 

8配置文件属性(Profile
     ASP.NET
提供了一个称为配置文件属性的功能,可用来存储特定于用户的数据。此功能与会话状态类似,不同的是,在用户的会话过期时,配置文件数据不会丢失。配置文件属性功能使用ASP.NET配置文件,此配置文件以持久的格式存储,并与某个用户关联。ASP.NET配置文件可以用来轻松地管理用户信息,而无需创建和维护自己的数据库。此外,配置文件使用了一个强类型API,开发者可以在应用程序中的任何位置访问该API,从而使用用户信息。开发者可以在配置文件中存储任何类型的对象。ASP.NET配置文件功能提供了一个通用存储系统,使开发者能够定义和维护几乎任何类型的数据,同时仍可用类型安全的方式使用数据。
   
若要使用配置文件属性,必须对配置文件提供程序进行配置。ASP.NET包括一个SqlProfileProvider类,使开发者能够将配置文件数据存储到SQL数据库中,但开发者也可以创建自己的配置文件提供程序类,用于以自定义格式将配置文件数据存储到自定义存储机制,如XML文件或Web服务。
   
因为放置在配置文件属性中的数据没有存储到应用程序内存中,所以这些数据在Internet信息服务(IIS)重新启动或辅助进程重新启动后仍能得到保留,而不会丢失。此外,配置文件属性可以跨多个进程得到保持,例如在网络场或网络园中。

 

总结

ASP.NET中的功能和工具比ASP中更多,使我们能够更有效和高效地管理网页的状态。具体选择哪种方法与你的应用程序有关,在选择时可以考虑下面的问题:

·1、需要存储多少信息?如果信息量很大,数据库将使我们能够存储大量的与Web应用程序中的状态相关的信息,有时,用户会使用唯一的ID频繁地访问数据库,我们可以将它存储在数据库中,在对网站中网页的多次请求中使用。
2·客户端接受持久的还是内存中的cookie
  3、希望在客户端还是在服务器端存储信息?
    4、要存储的信息需要保密吗?
5、希望你的网页的性能如何?

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值