• 学习笔记 ASP.NET状态管理


    这篇是以前写的, 觉得配置文件(个性化数据那块) 和 缓存那里不够详细, 一直没发. 既然决定要专门整理一篇缓存笔记. 现在, 就直接发布下吧

    ##############################################

    先别管ASP.NET的状态管理是什么? 首先回想一下浏览器和服务器的请求通信过程: 浏览器和服务器之间采用了HTTP协议作为共有约定来进行通信. 当在浏览器的地址栏中输入一个网址(IP地址)并按回车时(不考虑网络设备向DNS请求IP地址的过程), 浏览器首先发送一个GET请求到服务器, 服务器接收请求后根据模板页(default.aspx)和后台CS文件(default.aspx.cs)生成页面并回送给浏览器, 于是浏览器接收回送的消息并经浏览器编译解释后呈现给用户, 这时用户就可以看到页面上的各种元素了. 当用户希望执行某项操作时, 如: 点击button按钮, 这时浏览器将再次向服务器发送请求(Post请求)并附加上请求参数, 其中请求参数包含各种页面元数据的信息及状态参数; 当服务器接收到请求后, 将再次根据模板页和后台cs文件生成页面, 随后将请求参数中状态参数赋给相应的控件, 在页面事件的Load事件 --- LoadComplete事件之间, 执行控件事件, 即执行button的click事件的处理方法. 当页面事件处理完后, 将生成的页面再次返回到客户端浏览器. 这样, 看起来像是点击了button按钮, 执行了button的click事件绑定的方法.

    而实际上, 服务器在接受到GET请求并回送给客户端浏览器后, 将直接销毁掉刚才创建的页面对象, 也就是说服务器生成页面的过程都是”一次性”服务, 再把生成好的页面发给浏览器后便销毁掉刚才创建的页面对象, 服务器无法为客户端保存任何信息. 当客户端浏览器发送POST请求时, 由客户端浏览器提供自己的参数并传到服务器端, 服务器做的工作和第一次GET请求时并没有区别, 只是客户端通过某种手段将自己的状态告诉了服务器, 而这种手段就是状态管理, 由客户端维护状态的状态管理就是客户端状态管理. 而随着技术的发展, 服务器端也可以记录客户的状态信息, 称之为服务器状态管理. 总之, 状态管理的目的是保存客户端的参数或状态, 例如: 用户的登陆状态, 由于HTTP协议时无状态的, 当我们第二次请求服务器时, 服务器已经丢掉了之前的页面对象, 服务器无法判断用户是已经登陆的用户还是为登陆的用户, 状态管理就为我们提供了这么一种手段, 用来告诉服务器用户时登陆过的, 还是未登录的.

    状态管理的手段有9种: 见下图:

    状态管理有基于客户端和基于服务器的两类状态管理, 其中基于客户端的状态管理是指状态管理信息存储在浏览器上, 在各次请求中不会再服务器上维护任何信息; 而基于服务器的状态管理将由服务器存储和维护客户端的状态信息.

    基于客户端的状态管理(5种): 隐藏域、视图状态、控件状态、cookie、查询字符串, 其中控件状态多用于控件开发, 网站很少应用.

    ViewState: 隐藏域和视图状态常用于同一页面的不同次请求之间的状态管理:

    Name属性对客户端控件很重要, 添加了”name”属性后, name的值将会发送到服务器. 在Get或Post请求中, 将以"name属性的值"="value属性的值"发送, 多个名值对用&隔开.

    //Calendar.aspx

    View Code
    <%@ Page Trace="false" Language="C#" AutoEventWireup="true" CodeFile="Calendar.aspx.cs"
    Inherits
    ="Calendar"%>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
    <title>My Calendar</title>
    <style type="text/css">
    .currentdate
    {
    border
    : solid 1px Black;
    background-color
    : Aqua;
    color
    : Fuchsia;
    }
    table
    {
    border-collapse
    : collapse;
    border
    : solid 1px Green;
    }
    td
    {
    padding
    : 15px;
    text-align
    : center;
    border
    : solid 1px Green;
    border-collapse
    : collapse;
    }
    .haveEvent
    {
    background-image
    : url(images/tips.gif);
    background-repeat
    : no-repeat;
    background-position
    : right bottom;
    }
    </style>
    </head>
    <body>
    <form id="form_calendar" runat="server">
    <div>
    <div style="float:left">
    <asp:Button ID="btn_previous" runat="server" Text="上一月" OnClick="Btn_Previous_Click"/>
    <asp:Label ID="lbl_currentdate" runat="server" Text="当前日期"></asp:Label>
    <asp:LinkButton ID="lbtn_today" runat="server" OnClick="lbtn_today_Click">今天</asp:LinkButton>
    <asp:Button ID="btn_next" runat="server" Text="下一月" OnClick="Btn_Next_Click"/>
    <br />
    <br />
    <asp:Table ID="tb_calendar" runat="server">
    </asp:Table>
    <br />
    标题:
    <asp:TextBox ID="txt_title" Width="280px" runat="server"></asp:TextBox><br /><br/>
    内容:
    <asp:TextBox ID="txt_content" Width="280px" runat="server" TextMode="MultiLine"
    Height
    ="100px"></asp:TextBox>
    <br /><br />
    <asp:LinkButton ID="lbtn_add" runat="server" onclick="lbtn_add_Click">添加</asp:LinkButton>
    </div>
    <div style="float:right; 299px;">
    事件列表:
    <asp:BulletedList ID="btl_Title" runat="server" Height="22px" Width="256px">
    </asp:BulletedList>
    <asp:ListBox ID="lst_content" runat="server" Height="100px" Width="301px"></asp:ListBox>
    </div>
    </div>
    <asp:HiddenField ID="hid_currentdate" runat="server"/>
    </form>
    </body>
    </html>

    //Calendar.aspx.cs

    View Code
    using System;
    using System.Collections;
    using System.Configuration;
    using System.Data;
    using System.Linq;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Xml.Linq;

    publicpartialclass Calendar : System.Web.UI.Page
    {
    #region 全局变量
    //private DateTime _current;
    public DateTime CurrentDate
    {
    #region 普通属性
    /*
    get
    {
    return this._current;
    }
    set
    {
    this._current = value;
    }
    *
    */
    #endregion
    #region 属性直接存取ViewState
    get
    {
    if (this.IsPostBack)
    {
    return (DateTime)this.ViewState["currentdate"];
    }
    else
    {
    return DateTime.Now;
    }
    }
    set
    {
    this.ViewState["currentdate"] = value;
    }
    #endregion
    }
    #endregion

    #region 页面事件
    protectedvoid Page_PreInit(object sender, EventArgs e)
    {
    #region 隐藏域
    /*
    string currentdate = this.Request[this.hid_currentdate.ClientID]; //直接用隐藏域的ID值("hid_currentdate")做索引也可
    //隐藏域最早可以在PreInit事件中处理(从Request中取), 但如果通过隐藏域的.Value属性取值必须在InitComplete之后
    if (!string.IsNullOrEmpty(currentdate))
    {
    this.CurrentDate = DateTime.Parse(currentdate);
    }
    else
    {
    this.CurrentDate = DateTime.Now;
    }
    */
    #endregion
    }

    protectedvoid Page_PreLoad(object sender, EventArgs e)
    {
    /*
    #region ViewSate
    if (this.IsPostBack)
    {
    this.CurrentDate = (DateTime)this.ViewState["currentdate"];
    }
    else
    {
    this.CurrentDate = DateTime.Now;
    }
    #endregion
    */
    }

    protectedvoid Page_Load(object sender, EventArgs e)
    {
    if (!this.IsPostBack)
    {
    //画表格
    //this._current = DateTime.Now;
    this.CurrentDate = DateTime.Now;
    //ShowCalendar();
    //this.lbl_test.Text = Day1ofMonth.ToString();
    }
    ShowCalendar();
    }



    #endregion

    #region 通过属性存取视图状态, 不再需要PreRenderComplete事件
    /*
    //保存数据岛隐藏域
    protected void Page_PreRenderComplete(object sender, EventArgs e)
    {
    //this.hid_currentdate.Value = this.CurrentDate.ToShortDateString();
    this.ViewState["currentdate"] = this.CurrentDate;
    }
    #endregion
    *
    */
    #endregion

    privatevoid ShowCalendar()
    {
    //清楚数据
    this.tb_calendar.Rows.Clear();
    this.lbl_currentdate.Text =this.CurrentDate.ToShortDateString(); //lbl始终显示当前日期
    //this.txt_title.Text = "";
    //this.txt_content.Text = "";
    this.btl_Title.Items.Clear();
    this.lst_content.Items.Clear();

    //日历数据
    int day1ofMonth = (int)new DateTime(this.CurrentDate.Year, this.CurrentDate.Month, 1).DayOfWeek; //每月1号星期几, 返回值为枚举需强转
    int dayofMonth = DateTime.DaysInMonth(this.CurrentDate.Year, this.CurrentDate.Month); //每月有多少天

    //画日历
    string[] weekname =newstring[7] { "", "", "", "", "", "", "" };
    int rows = (day1ofMonth + dayofMonth +6) /7;
    for (int i =0; i <= rows; i++)
    {
    TableRow tr
    =new TableRow();
    this.tb_calendar.Rows.Add(tr);

    if (i ==0) //第一行, 填入星期几
    {
    for (int j =0; j < weekname.Length; j++)
    {
    TableCell tc
    =new TableCell();
    tr.Cells.Add(tc);
    tc.Text
    = weekname[j];
    if (j ==0|| j ==6)
    {
    tc.ForeColor
    = System.Drawing.Color.Red;
    }
    else
    {
    //nothing to do here!
    }
    }
    }
    else//填入空白格
    {
    for (int j =0; j <7; j++)
    {
    TableCell tc
    =new TableCell();
    tr.Cells.Add(tc);
    //以下三行为每次的清理操作
    //tc.ForeColor = System.Drawing.Color.Black; //每次清除前景色, 可以用样式表实现
    //tc.BackColor = System.Drawing.Color.White; //每次清楚背景色
    tc.Text ="&nbsp;"; //每次画表格前, 清除原先的数字
    }
    }
    }

    //填数据
    string virtualPath =this.Server.MapPath("~/App_Data");
    int rowindex;
    int colindex;
    for (int i =1; i <= dayofMonth; i++)
    {
    rowindex
    = (day1ofMonth + i -1) /7+1; //取得行索引
    colindex = (day1ofMonth + i -1) %7; //取得列索引
    TableCell tc =this.tb_calendar.Rows[rowindex].Cells[colindex];
    //tc.Text = i.ToString();
    LinkButton lbtn =new LinkButton();
    lbtn.ID
    =string.Format("lbtn{0}",i);
    lbtn.Text
    = i.ToString();
    lbtn.Click
    +=new EventHandler(lbtn_Click); //添加天按钮的事件
    //用命令参数, 可以获得选中按钮所对应的日期是什么
    //lbtn.CommandArgument = new DateTime(this.CurrentDate.Year, this.CurrentDate.Month, i).ToShortDateString();
    tc.Controls.Add(lbtn);
    if (i ==this.CurrentDate.Day)
    {
    //tc.ForeColor = System.Drawing.Color.Magenta;
    //tc.BackColor = System.Drawing.Color.Cyan;
    //lbtn.CssClass = "currentdate"; //引用Css样式, 在aspx中添加.currentdate{}样式
    lbtn.CssClass ="currentdate";

    }
    else
    {
    //nothing to do here!
    }

    //产看文件夹中是否有记录事情的文件, 有的话则改变图标
    string filedir = System.IO.Path.Combine(virtualPath, new DateTime(this.CurrentDate.Year,this.CurrentDate.Month,i).ToShortDateString()); //this.CurrentDate一定有值
    System.IO.DirectoryInfo dirinfo =new System.IO.DirectoryInfo(filedir);
    if (dirinfo.Exists)
    {
    System.IO.FileInfo[] files
    = dirinfo.GetFiles();
    if (files.Length >0)
    {
    tc.CssClass
    ="haveEvent";
    }
    else
    {
    this.btl_Title.Items.Clear();
    }
    }
    else
    {
    //nothing to do here!
    }

    }
    this.lbl_currentdate.Text =this.CurrentDate.ToShortDateString(); //填充当前日期
    //产看当日的文件夹中是否有记录事情的文件, 有的话则显示
    string filedir2 = System.IO.Path.Combine(virtualPath, this.CurrentDate.ToShortDateString()); //this.CurrentDate一定有值
    System.IO.DirectoryInfo dirinfo2 =new System.IO.DirectoryInfo(filedir2);
    if (dirinfo2.Exists)
    {
    System.IO.FileInfo[] files
    = dirinfo2.GetFiles();
    if (files.Length >0)
    {
    string title ="";
    string content ="";
    System.IO.StreamReader sr
    =null;
    foreach (System.IO.FileInfo fi in files)
    {
    sr
    =new System.IO.StreamReader(fi.FullName);
    title
    = sr.ReadLine();
    content
    = sr.ReadLine();
    sr.Close();
    this.btl_Title.Items.Add(title);
    this.lst_content.Items.Add(content + Environment.NewLine);
    }
    }
    else
    {
    this.btl_Title.Items.Clear();
    }
    }
    else
    {
    //nothing to do here!
    }
    }

    #region 按钮事件
    protectedvoid Btn_Previous_Click(object sender, EventArgs e)
    {
    this.CurrentDate =this.CurrentDate.AddMonths(-1);
    ShowCalendar();
    }
    protectedvoid Btn_Next_Click(object sender, EventArgs e)
    {
    this.CurrentDate =this.CurrentDate.AddMonths(1);
    ShowCalendar();
    }
    void lbtn_Click(object sender, EventArgs e)
    {
    LinkButton lbtn
    = sender as LinkButton;
    int day = Convert.ToInt32(lbtn.Text);
    this.CurrentDate =new DateTime(this.CurrentDate.Year, this.CurrentDate.Month, day);
    ShowCalendar();
    }
    protectedvoid lbtn_today_Click(object sender, EventArgs e)
    {
    this.CurrentDate = DateTime.Now;
    ShowCalendar();
    }
    protectedvoid lbtn_add_Click(object sender, EventArgs e)
    {
    string virtualPath =this.Server.MapPath("~/App_Data");
    string filedir = System.IO.Path.Combine(virtualPath, this.CurrentDate.ToShortDateString()); //this.CurrentDate一定有值
    System.IO.DirectoryInfo dirinfo =new System.IO.DirectoryInfo(filedir);
    if (!dirinfo.Exists)
    {
    dirinfo.Create();
    }
    else
    {
    //nothing to do here!
    }

    if (this.txt_title.Text !=""&&this.txt_content.Text !="")
    {
    System.IO.StreamWriter sw
    =new System.IO.StreamWriter(string.Format("{0}/{1}", filedir,System.IO.Path.GetRandomFileName()));
    sw.WriteLine(txt_title.Text);
    sw.WriteLine(txt_content.Text);
    sw.Close();
    this.txt_title.Text ="";
    this.txt_content.Text ="";
    }
    else
    {
    //nothing to do here!
    }
    ShowCalendar();
    }
    #endregion
    }

    Cookie:

    Cookie的使用:

    发送到客户端的Cookie信息需要通过键值对处理, 且键和值的类型均为string.

    大多数浏览器Cookie中保存的Value长度不能超过2K.

    默认情况下, 关闭浏览器后Cookie将一同消失, 如果需要长时间保存Cookie, 可以修改Cookie的过期时间(Expires), 而删除Cookie的操作为设置一个更早的已失效的过期时间.

    //CookieDemo.aspx

    View Code
    //default.aspx

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default"%>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
    <title>Untitled Page</title>
    </head>
    <body>
    <form id="form1" runat="server">
    <div>
    <asp:TextBox ID="txt_input" runat="server"></asp:TextBox>
    <asp:LinkButton ID="lbtn_save" runat="server" onclick="lbtn_save_Click">保存</asp:LinkButton>
    <asp:CheckBox ID="chk_cookie" runat="server" Text="长期有效"/>
    <asp:LinkButton ID="lbtn_del" runat="server" onclick="lbtn_del_Click">删除</asp:LinkButton>
    <hr />
    <asp:TextBox ID="txt_result" runat="server"></asp:TextBox>
    <asp:LinkButton ID="lbtn_get" runat="server" onclick="lbtn_get_Click">获取</asp:LinkButton>
    </div>
    </form>
    </body>

    //CookieDemo.aspx.cs

    View Code
    using System;
    using System.Configuration;
    using System.Data;
    using System.Linq;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Xml.Linq;

    publicpartialclass _Default : System.Web.UI.Page
    {
    protectedvoid Page_Load(object sender, EventArgs e)
    {
    if (!this.IsPostBack)
    {

    }
    }

    protectedvoid lbtn_save_Click(object sender, EventArgs e)
    {
    //创建cookie对象
    HttpCookie cookie =new HttpCookie("CK");
    cookie.Value
    =this.txt_input.Text;

    if (this.chk_cookie.Checked ==true)
    {
    cookie.Expires
    = DateTime.Now.AddDays(7);
    }

    //创建完还要放到Response中去, 生成回应头
    //this.Response.SetCookie(cookie); --更新cookie
    this.Response.Cookies.Add(cookie);
    }
    protectedvoid lbtn_get_Click(object sender, EventArgs e)
    {
    HttpCookie hc
    =this.Request.Cookies["CK"];
    this.txt_result.Text = hc.Value.ToString();
    }
    protectedvoid lbtn_del_Click(object sender, EventArgs e)
    {
    HttpCookie hc
    =this.Request.Cookies["CK"];
    hc.Expires
    =new DateTime(1900, 1, 1);

    this.Response.Cookies.Add(hc);
    }
    }

    查询字符串:

    示例: http://www.cait.com/default..aspx?category=teacher&name=hao

    大多数浏览器允许的URL长度不大于2K

    若使用查询字符串, 必须使用HttpGet命令提交, 注意这种方式不适合传递重要的敏感信息

    //default.aspx, 注意地址栏

    View Code
    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default"%>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
    <title>Untitled Page</title>
    </head>
    <body>
    <form id="form1" runat="server">
    <div>
    栏目:
    <asp:TextBox ID="txt_category" runat="server"></asp:TextBox>
    姓名:
    <asp:TextBox ID="txt_name" runat="server"></asp:TextBox>
    <hr />
    <asp:LinkButton ID="lbtn_submit" runat="server" onclick="lbtn_submit_Click">查看</asp:LinkButton>
    </div>
    </form>
    </body>
    </html>

    //default.aspx.cs

    View Code
    using System;
    using System.Configuration;
    using System.Data;
    using System.Linq;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Xml.Linq;

    publicpartialclass _Default : System.Web.UI.Page
    {
    protectedvoid Page_Load(object sender, EventArgs e)
    {

    }
    protectedvoid lbtn_submit_Click(object sender, EventArgs e)
    {
    if (this.txt_category.Text !=null&&this.txt_name.Text !=null)
    {
    string url =string.Format("default.aspx?category={0}&name={1}",this.txt_category.Text,this.txt_name.Text);
    this.Response.Redirect(url);
    //this.Server.Transfer(url);
    }
    }
    }

    会话状态Session:

    Session是一种将用户状态信息保存在服务器上的机制. 当用户第一次请求(GET)服务器时, 服务器将为客户端创建一个SessionID并发给客户端(通常存在cookie中), 同时在服务器内存上为客户端创建一个字典用来保存客户端信息. 当用户第二次请求时(POST)将自动把SessionID通过请求参数传给服务器, 从而判断用户的状态.

    默认情况下, *.aspx文件支持Session, 而*.ashx不支持Session. 因此ashx要使用Session需要实现System.Web.SessionState.IRequiresSessionState

    Session的实现机制较复杂, 但使用起来确很简单:

    存: this.Session[“键”] = 值;   取: object obje = this.Session[“键”];

    客户端: SessionID可以存在Cookie(默认)或Url中, 若客户端禁用cookie, 那么可以在配置文件中稍作修改就可以将SessionID存于Url中.

    View Code
    <configuration>
    <system.web>
    <sessionState cookieless = “true” />
    </system.web>
    </configuration>

    服务器端: 存储客户端的状态信息, 可采用多种方式存储.

    1.存放在服务器内存中, 也就是存放在为IIS进程所分配的内存中, 如果Web服务器进程崩溃将会丢失会话信息. 因为服务器内存资源有限, 所以服务器会给Session设置一个过期时间, 当过期时间满后如果客户仍然没有再次访问服务器, 则会删除相应的用户字典. 这个机制成为滑动过期机制, 可以再配置文件中修改该时间.

    View Code
    <configuration>
    <system.web>
    <sessionState mode=”Inproc” /><!—mode=”off”表示禁用Session-->
    </system.web>
    </configuration>

    2.存放在StateServer中, 使用该种方式可以在网站崩溃情况下, 保持Session信息. 该种设置需要配置stateConnectionString(默认: tcpip=127.0.0.1:42424), ip地址可以不同, 但是端口号必须相同. 此外, 还需要确保”服务管理器”中的ASP.NET State Service服务必须启动.

    View Code
    <configuration>
    <system.web>
    <sessionState mode=”StateServer” stateConnectionString=”tcpip=127.0.0.1:42424”/>
    </system.web>
    </configuration>

    3.存放在SqlServer中, 使用该种方式, 需要使用Aspnet_regsql.exe(路径: C:\windows\Mcrosoft.NET\Framework\v2.0.50717)工具安装会话状态数据库, 命令操作: “aspnet_regsql –E –ssadd –sstype p”; 手动操作: 在Aspnet_regsql.exe工具目录下, 双击执行并选中sql脚本installsqlstate.sql(临时表)或者installpersistsqlstate(永久表).

    同时还要修改配置文件: 配置连接串, 配置数据库的超时时间(默认30秒), 且需要确保Session中的对象是可序列化的.

    View Code
    <configuration>
    <system.web>
    <sessionState mode=”SQLServer” timeout=”60” sqlConnectionString=”data source=.\;integrated Security=sspi”/>
    </system.web>
    </configuration>

    4. 最后一种为自定义的状态管理机制, 该种机制需要创建一个集成SessionStateStoreProviderBase抽象类的类, 然后在配置文件中指定实现类的类名.

    View Code
    <configuration>
    <system.web>
    <sessionState mode=”Custom” customProvider=”namespace.classname,assembl”/>
    </system.web>
    </configuration>

    //default.aspx

    View Code
    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default"%>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
    <title></title>
    </head>
    <body>
    <form id="form1" runat="server">
    <div>

    <br />
    <br />
    <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
    <asp:LinkButton ID="LinkButton1" runat="server" onclick="LinkButton1_Click">保存</asp:LinkButton>
    <br />
    <br />
    <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
    <asp:LinkButton ID="LinkButton2" runat="server" onclick="LinkButton2_Click">获取</asp:LinkButton>

    </div>
    </form>
    </body>
    </html>

    //default.aspx.cs

    View Code
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;

    publicpartialclass _Default : System.Web.UI.Page
    {
    protectedvoid Page_Load(object sender, EventArgs e)
    {

    }
    protectedvoid LinkButton1_Click(object sender, EventArgs e)
    {
    this.Session["Code"] =this.TextBox1.Text;
    }
    protectedvoid LinkButton2_Click(object sender, EventArgs e)
    {
    this.Label1.Text =this.Session["Code"] asstring;
    }
    }

    Application:

    Application是全局的状态管理手段, 当服务器一启动时Application就会被创建并长期存在于服务器上, Appllication类似于服务器的ViewState, 但只有一份而且没有过期时间, Application是全局的, 而ViewState是页面级的, Application中保存的信息持久有效, 且数据保存在一个全局的字典中. Application允许多线程操作, HttpApplicationState对象提供了Lock()和UnLock()两个方法提供同步的访问. 我们可以通过Global.asax文件中的Application_Start()方法来初始化应用程序状态Application. 注意: 这里的Application不同于HttpApplication类(处理管道).

    Application适合存储全局性的数据, 如: 当前在线人数, 当前系统运行时间等.

    //default.aspx

    View Code
    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default"%>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
    <title>Untitled Page</title>
    </head>
    <body>
    <form id="form1" runat="server">
    <div>
    <asp:Label ID="lbl_time" runat="server" Text="页面执行时间"></asp:Label>
    </div>
    </form>
    </body>
    </html>

    //default.aspx.cs

    View Code
    using System;
    using System.Configuration;
    using System.Data;
    using System.Linq;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Xml.Linq;

    publicpartialclass _Default : System.Web.UI.Page
    {
    protectedvoid Page_Load(object sender, EventArgs e)
    {

    }
    protectedvoid Page_PreRenderComplete(object sender, EventArgs e)
    {
    DateTime old
    = DateTime.Parse(this.Application["AppStartTime"].ToString());
    TimeSpan span
    = DateTime.Now - old;
    this.lbl_time.Text =string.Format("服务器累计运行时间: {0}毫秒! ", span.TotalMilliseconds);
    }
    }

    //Global.asax

    View Code
    <%@ Application Language="C#"%>

    <script runat="server">

    void Application_Start(object sender, EventArgs e)
    {
    // Code that runs on application startup
    this.Application["AppStartTime"] = DateTime.Now;
    }

    void Application_End(object sender, EventArgs e)
    {
    // Code that runs on application shutdown

    }

    void Application_Error(object sender, EventArgs e)
    {
    // Code that runs when an unhandled error occurs

    }

    void Session_Start(object sender, EventArgs e)
    {
    // Code that runs when a new session is started

    }

    void Session_End(object sender, EventArgs e)
    {
    // Code that runs when a session ends.
    // Note: The Session_End event is raised only when the sessionstate mode
    // is set to InProc in the Web.config file. If session mode is set to StateServer
    // or SQLServer, the event is not raised.

    }

    </script>

    配置文件属性: 配置文件中的Profile, 需要配合Sql Server及个性化数据使用(Profile Provider), 也可以自己实现SqlProfileProvider类将数据保存在XML文件或Web服务中.

    ASP.NET 2.0 提供了一个称为个性化数据的功能, 可让您存储特定于用户的数据. 该功能与会话状态类似, 但不同点在: 用户会话过期后, 数据不一定丢失而由应用程序决定. 个性化数据可以让你轻松的管理用户信息, 甚至无需维护自己的数据库, 并且由于个性化数据没有存储到应用程序中, 所以当IIS崩溃、重启等情况时, 数据仍能得到保持.

    请求处理的环境HttpContext: 详情可参见HttpApplication处理管道

    每当一个请求到达ASP.NET网站时, HttpRuntime将会为这次请求创建一系列的对象, 为了方便使用, HttpRuntime创建一个HttpContext上下文对象(环境对象), 该对象是一个容器对象, 其中定义了用来表示处理此次请求所需要的所有数据. 在一次请求处理过程中, HttpContext对象是贯穿整个请求处理过程的, 因此可用来实现一次请求过程中的状态管理. HttpContext中常用的属性有: Request、Response、Server、Session.

    HttpContent对象通过Items属性提供一个基于键值对的状态管理机制, 其中键和值均为Object类型.

    Http只能处理一次请求过程, HttpContext是在HttpApplication处理管道中重要的状态管理手段.

    //default.aspx

    View Code
    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default"%>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
    <title>Untitled Page</title>
    </head>
    <body>
    <form id="form1" runat="server">
    <div>
    <asp:Label ID="lbl_time" runat="server" Text="页面事件事件"></asp:Label>
    </div>
    </form>
    </body>
    </html>

    //default.aspx.cs

    View Code
    using System;
    using System.Configuration;
    using System.Data;
    using System.Linq;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Xml.Linq;

    publicpartialclass _Default : System.Web.UI.Page
    {
    protectedvoid Page_PreInit(object sender, EventArgs e)
    {
    //this.Context.Items["time"] = DateTime.Now;
    HttpContext.Current.Items["time"] = DateTime.Now; //使用HttpContext类的静态属性
    }
    protectedvoid Page_Load(object sender, EventArgs e)
    {

    }
    protectedvoid Page_PreRenderComplete(object sender, EventArgs e)
    {
    TimeSpan span
    = DateTime.Now - DateTime.Parse(this.Context.Items["time"].ToString());
    this.Context.Items["totaltime"] = span.TotalMilliseconds;
    this.lbl_time.Text ="11个页面处理事件共耗时(毫秒):"+this.Context.Items["totaltime"].ToString();
    }
    }

    还有个Cache做状态管理, Cache仅有一份, 并且在有请求时, 我们可以通过HttpContext.Current.Cache获得Cache的引用; 无请求时通过HttpRuntime.Cache获得Cache的引用. 我个人感觉Cache主要用于缓存数据, 提供高性能、高效率的使用体验. 当然Cache也可以作为传递状态的一种方式, 因为Cache首先是一种状态管理的手段, 并且是综合Session和Application二者优点的状态管理手段. 稍后, 关于缓存会写一篇博客, 届时在谈谈个人关于Cache的理解吧.

    //综合练习: 验证码

    //SqlCalendar\DAL\CalendarHelper

    View Code
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace DAL
    {
    publicclass CalendarHelper //为DAL层提供连接串
    {
    privatestaticreadonlystring connectionStr = System.Configuration.ConfigurationManager.ConnectionStrings["MsSql"].ConnectionString;

    publicstaticstring ConnectionString //对外提供连接串
    {
    get
    {
    return connectionStr;
    }
    }

    }
    }

    //SqlCalendar\DAL\CalendarUser

    View Code
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace DAL
    {
    publicclass CalendarUser //DAL层通过SqlHelper访问数据库
    {
    //CalendarUser类提供关于日历用户的功能, 如: 创建用户
    //DateTime是值类型, 而值类型没有与Sql中Null的对应, 加?表示可空类型, 即允许值类型为空
    publicint CreateUser(string username, string userpass, string email, DateTime? birthday)
    {
    int result =-1;

    //定制的参数是根据SqlHelper的方法来选的
    string connectionStr = CalendarHelper.ConnectionString;
    string sqlText =@"insert into T_UserInfo(username, userpass,email,birthday)
    values(@username,@userpass,@email,@birthday)
    ";
    System.Data.CommandType cmdType
    = System.Data.CommandType.Text;

    System.Data.SqlClient.SqlParameter p_username
    =new System.Data.SqlClient.SqlParameter("@username", System.Data.SqlDbType.NVarChar);
    System.Data.SqlClient.SqlParameter p_userpass
    =new System.Data.SqlClient.SqlParameter("@userpass",System.Data.SqlDbType.NVarChar);
    System.Data.SqlClient.SqlParameter p_email
    =new System.Data.SqlClient.SqlParameter("@email", System.Data.SqlDbType.NVarChar);
    System.Data.SqlClient.SqlParameter p_birthday
    =new System.Data.SqlClient.SqlParameter("@birthday",System.Data.SqlDbType.DateTime);

    p_username.Value
    = username;
    p_userpass.Value
    = userpass;
    p_email.Value
    = email;
    p_birthday.Value
    = birthday;

    System.Data.SqlClient.SqlParameter[] paras
    =new System.Data.SqlClient.SqlParameter[] { p_username, p_userpass, p_email, p_birthday };

    result
    = Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteNonQuery(connectionStr, cmdType,sqlText, paras);

    return result;
    }

    publicint CreateUserByProc(string username, string userpass, string email, DateTime? birthday)
    {
    //定制的参数是根据SqlHelper的方法来选的
    string connectionStr = CalendarHelper.ConnectionString;
    string sqlText ="SP_CreateUserProc";

    System.Data.CommandType cmdType
    = System.Data.CommandType.StoredProcedure;

    System.Data.SqlClient.SqlParameter p_username
    =new System.Data.SqlClient.SqlParameter("@username", System.Data.SqlDbType.NVarChar);
    System.Data.SqlClient.SqlParameter p_userpass
    =new System.Data.SqlClient.SqlParameter("@userpass", System.Data.SqlDbType.NVarChar);
    System.Data.SqlClient.SqlParameter p_email
    =new System.Data.SqlClient.SqlParameter("@email", System.Data.SqlDbType.NVarChar);
    System.Data.SqlClient.SqlParameter p_birthday
    =new System.Data.SqlClient.SqlParameter("@birthday", System.Data.SqlDbType.DateTime);
    System.Data.SqlClient.SqlParameter p_return
    =new System.Data.SqlClient.SqlParameter("@returnValue",System.Data.SqlDbType.Int);

    p_username.Value
    = username;
    p_userpass.Value
    = userpass;
    p_email.Value
    = email;
    p_birthday.Value
    = birthday;

    p_return.Direction
    = System.Data.ParameterDirection.ReturnValue;

    System.Data.SqlClient.SqlParameter[] paras
    =new System.Data.SqlClient.SqlParameter[] { p_username, p_userpass, p_email, p_birthday,p_return};

    Microsoft.ApplicationBlocks.Data.SqlHelper.ExecuteNonQuery(connectionStr, cmdType, sqlText, paras);

    //执行完存储过程后, 获取返回值
    return (int)p_return.Value;
    }
    }
    }

    //SqlCalendar\SqlHelper    --- 网上可以下载到

    //SqlCalendar\Validator\Default.aspx

    View Code
    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default"%>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
    <title>Untitled Page</title>
    <style type="text/css">
    .style1
    {
    width
    : 95%;
    height
    : 216px;
    }
    .style5
    {
    }
    .style6
    {
    width
    : 57px;
    }
    </style>
    </head>
    <body>
    <form id="form1" runat="server">
    <div style="text-align:center;">
    <div style=" 500px; height:500px; background-color:Silver; text-align:left; margin-top: 20; padding: 10px 0px 0px 30px; margin-top:4%">

    <table class="style1">
    <tr>
    <td class="style6">
    <label for="txt_username">用户名: </label>
    </td>
    <td class="style5">
    <asp:TextBox ID="txt_username" runat="server" Width="209px"></asp:TextBox>
    </td>
    <td>
    <asp:RequiredFieldValidator ID="validator_rqeField_username" runat="server"
    ControlToValidate
    ="txt_username" Display="Dynamic" ErrorMessage="用户名不能为空"></asp:RequiredFieldValidator>
    <asp:CustomValidator ID="validator_custom_username" runat="server"
    ErrorMessage
    ="用户名已存在" ControlToValidate="txt_username"></asp:CustomValidator>
    </td>
    </tr>
    <tr>
    <td class="style6">
    <label for="txt_password">密 码: </label>
    </td>
    <td class="style5">
    <asp:TextBox ID="txt_password" runat="server" Width="209px" TextMode="Password"></asp:TextBox>
    </td>
    <td>
    <asp:RequiredFieldValidator ID="validator_rqeField_password" runat="server"
    ControlToValidate
    ="txt_password" ErrorMessage="密码不能为空"></asp:RequiredFieldValidator>
    </td>
    </tr>
    <tr>
    <td class="style6">
    <label for="txt_confirm">确 认: </label>
    </td>
    <td class="style5">
    <asp:TextBox ID="txt_confirm" runat="server" Width="209px" TextMode="Password"></asp:TextBox>
    </td>
    <td>
    <asp:RequiredFieldValidator ID="validator_rqe_confirm" runat="server"
    Display
    ="Dynamic" ErrorMessage="确认密码不能为空" ControlToValidate="txt_confirm"></asp:RequiredFieldValidator>
    <asp:CompareValidator ID="validator_compto_conpass" runat="server"
    ControlToCompare
    ="txt_password" ControlToValidate="txt_confirm"
    Display
    ="Dynamic" ErrorMessage="密码不一致"></asp:CompareValidator>
    </td>
    </tr>
    <tr>
    <td class="style6">
    <label for="txt_email">邮 箱: </label>
    </td>
    <td class="style5">
    <asp:TextBox ID="txt_email" runat="server" Width="209px"></asp:TextBox>
    </td>
    <td>
    <asp:RequiredFieldValidator ID="validator_rqeField_email" runat="server"
    ControlToValidate
    ="txt_email" Display="Dynamic" ErrorMessage="邮箱不能为空"></asp:RequiredFieldValidator>
    <asp:RegularExpressionValidator ID="validator_regx_email" runat="server"
    ControlToValidate
    ="txt_email" Display="Dynamic" ErrorMessage="邮件格式错误"
    ValidationExpression
    ="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"></asp:RegularExpressionValidator>
    </td>
    </tr>
    <tr>
    <td class="style6">
    <label for="txt_birth">生 日: </label>
    </td>
    <td class="style5">
    <asp:TextBox ID="txt_birth" runat="server" Width="209px"></asp:TextBox>
    </td>
    <td>
    <asp:RangeValidator ID="validator_range_birth" runat="server"
    ControlToValidate
    ="txt_birth" ErrorMessage="日期格式错误" Type="Date"></asp:RangeValidator>
    </td>
    </tr>
    <tr>
    <td class="style6">
    验证码:
    </td>
    <td class="style5" colspan="2">
    <asp:Image ID="Image1" runat="server" ImageUrl="~/DynamicCode.ashx"/>
    </td>
    </tr>
    <tr>
    <td class="style6">
    输入验证码:
    </td>
    <td class="style5" colspan="2">
    <asp:TextBox ID="txt_dcode" runat="server" Width="210px"></asp:TextBox>
    <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server"
    ErrorMessage
    ="验证码不能为空" Display="Dynamic" ControlToValidate="txt_dcode"></asp:RequiredFieldValidator>
    <asp:CustomValidator ID="Validator_Dcode" runat="server"
    ErrorMessage
    ="验证码错误" ControlToValidate="txt_dcode" Display="Dynamic"
    onservervalidate
    ="Validator_Dcode_ServerValidate"></asp:CustomValidator>
    </td>
    </tr>
    <tr>
    <td colspan="3">
    <asp:LinkButton ID="lbtn_reguser" runat="server" onclick="lbtn_reguser_Click">注 册</asp:LinkButton>
    <asp:LinkButton ID="ltbn_forgetpass" runat="server">忘记密码?</asp:LinkButton>
    </td>
    </tr>
    </table>

    </div>
    </div>
    </form>
    </body>
    </html>

    //SqlCalendar\Validator\Default.aspx.cs

    View Code
    using System;
    using System.Configuration;
    using System.Data;
    using System.Linq;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Xml.Linq;

    publicpartialclass _Default : System.Web.UI.Page
    {
    protectedvoid Page_Init(object sender, EventArgs e)
    {
    this.validator_range_birth.MaximumValue = DateTime.Now.ToShortDateString();
    this.validator_range_birth.MinimumValue = DateTime.Now.AddYears(-150).ToShortDateString();
    }

    protectedvoid Page_Load(object sender, EventArgs e)
    {
    if (this.IsPostBack)
    {
    //this.Validate(); //手动调用验证方法
    }

    }
    protectedvoid lbtn_reguser_Click(object sender, EventArgs e)
    {
    if (this.IsValid) //当所有页面对象验证通过后, 将设置Page的IsValid属性, 支出验证通过
    {
    DAL.CalendarUser calendaruser
    =new DAL.CalendarUser();

    //初始birthday为空, 当串不为空时转换
    DateTime? birthday =null;
    if (!string.IsNullOrEmpty(this.txt_birth.Text))
    {
    birthday
    = DateTime.Parse(this.txt_birth.Text);
    }

    //int a = calendaruser.CreateUser(this.txt_username.Text, this.txt_password.Text, this.txt_email.Text, birthday);
    int exist = calendaruser.CreateUserByProc(this.txt_username.Text, this.txt_password.Text, this.txt_email.Text, birthday);

    if (exist !=0)
    {
    this.validator_custom_username.IsValid =false;
    }
    }
    }
    protectedvoid Validator_Dcode_ServerValidate(object source, ServerValidateEventArgs args)
    {
    //每次复位Validator状态
    args.IsValid =false;
    //this.Validator_Dcode.IsValid = false;

    #region Cookie
    //HttpCookie hc = this.Request.Cookies["Dcode"];
    //if (hc != null)
    //{
    // string rqtcode = hc.Value.ToString();
    ////if(rqtcode == args.Value)
    // if (rqtcode == this.txt_dcode.Text)
    // {
    ////this.Validator_Dcode.IsValid = true; //this.Validator_Dcode.IsValid是Valid控件上的IsValid
    // args.IsValid = true; //这里必须用args.IsValid, 因为args.IsValid是事件参数(临时的), 最终他会根据事件参数的结果修改控件上的IsValid属性, 进而显示错误信息
    // }
    //}
    //else
    //{
    // this.validator_dcode.isvalid = false;
    //}
    #endregion

    #region Session
    if (this.Session["Dycode"] !=null)
    {
    string num =this.Session["Dycode"] asstring;
    if (num ==this.txt_dcode.Text)
    {
    args.IsValid
    =true;
    //this.Validator_Dcode.IsValid = true;
    }
    else
    {
    args.IsValid
    =false;
    //this.Validator_Dcode.IsValid = false;
    }
    }

    #endregion
    }
    }

    //SqlCalendar\Validator\DynamicCode.ashx

    View Code
    <%@ WebHandler Language="C#" Class="DynamicCode"%>

    using System;
    using System.Web;

    //使用Session记得在ashx中, 添加IRequiresSessionState的接口, 而aspx文件默认就是支持的
    publicclass DynamicCode : IHttpHandler,System.Web.SessionState.IRequiresSessionState {

    publicvoid ProcessRequest (HttpContext context) {
    //context.Response.ContentType = "text/plain";
    //context.Response.Write("Hello World");
    context.Response.ContentType ="image/jpeg";

    context.Response.Cache.SetCacheability(HttpCacheability.NoCache);

    //随机数生成器
    Random ran =new Random();
    int rannum = ran.Next(10000, 100000);

    //创建位图文件
    System.Drawing.Bitmap bitmap =new System.Drawing.Bitmap(300, 80);

    //在位图文件上画画, 需要创建与图片画板相关的画图器
    using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmap))
    {
    //用Graphic对象清空背景
    g.Clear(System.Drawing.Color.DarkGreen);

    //花矩形框
    g.DrawRectangle(new System.Drawing.Pen(System.Drawing.Brushes.Blue,3),0,0,bitmap.Width-1,bitmap.Height-1);

    //StringFormat对象, 用来保存数字位置, 在数字矩形的绘图区域中
    System.Drawing.StringFormat sf =new System.Drawing.StringFormat();
    sf.Alignment
    = System.Drawing.StringAlignment.Center;
    sf.LineAlignment
    = System.Drawing.StringAlignment.Center;

    //画数字, RectangleF用来确定显示数字的矩形区域
    g.DrawString(rannum.ToString(),
    new System.Drawing.Font("黑体", 50),
    System.Drawing.Brushes.Black,
    new System.Drawing.RectangleF(0, 0, bitmap.Width, bitmap.Height),
    sf);

    //画横线
    for (int i =0; i <80; i++)
    {
    g.DrawLine(
    new System.Drawing.Pen(System.Drawing.Brushes.Black),
    ran.Next(
    0, bitmap.Width),
    ran.Next(
    0, bitmap.Height),
    ran.Next(
    0, bitmap.Width),
    ran.Next(
    0, bitmap.Height));
    }

    //将数字保存到Cookie中
    //HttpCookie hc = new HttpCookie("Dcode");
    //hc.Value = rannum.ToString();
    //context.Response.Cookies.Add(hc); //保存到cookie中去, 但是没有页面对象, 所以需要通过context

    //将数字保存到Session中
    context.Session["DyCode"] = rannum.ToString();
    }

    //保存图片到response, 注意: 这里是一般处理程序, 没有页面对象, 所以只能用context
    bitmap.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
    }

    publicbool IsReusable {
    get {
    returnfalse;
    }
    }

    }

    /// 补充内容 

    一:页面间通过JS传值的形式大概有三种

     

    1 window.opener传值,子页面能获取父页面的窗口对象。当然就能获取到对象的值了,这种方式不存在传值  的长度限制,不能跨域。

    2 cookies传值,传值长度有限制。

    3 URL传值,值也是有限制,但是能跨域。

     

    二:ASP.NET页面传值

     

    1.  使用QueryString变量(或者称URL)地址后加“?”

    2.  Session传值。 (服务端)

    3.  Applica传值。  (全局的,最好的例子是在线访问量)

    4.  Cookies传值。(客户端保存)

    5    HttpContext  也即是server.transfer()  注意它与redirect的区别

     

     

    三:jS访问后台变量(1、2使用较多)

     

        1、通过页面上隐藏控件访问 <input id="hidencontrol" type="hidden" runat="server"> (注意要runat=“server”)

          2、<%=对象名%>  对象要是pubulic的

          3  后台注册函数"<script language='javascript'> value=" + 变量名+ "</script>"   访问value即可

     

    四 :后台访问JS变量

         1  同样是hidden

         2  cookie或session

     

    五 JS调用后台函数

     

    1   document.getElementById("btn1").click();

    2 var f = "<%=function()%>";  alert(f); 

     

     

    六 后台调用JS

       1 注册JS函数

       2 使用Literal

     

  • 相关阅读:
    图片灰度化,并且resize图片
    C语言学习笔记
    路飞学城14天集训营作业2—三级菜单
    路飞学城14天集训营作业4—员工信息表
    路飞学城14天集训营作业3—购物车
    路飞学城14天集训营作业1—登陆认证
    js钩子函数
    APP2.0后台控件API
    KindEditor 插件API使用说明
    TreeView插件 API
  • 原文地址:https://www.cnblogs.com/cs_net/p/1959084.html
Copyright © 2020-2023  润新知