Web开发之ASP.Net:(5)WebForm(.aspx) (一)

作者:陆金龙    发表时间:2016-08-21 15:00   


使用html ashx方式,每次输出网页用一般处理程序(ashx)将html替换在输出处理太麻烦了,所以一般生成html的时候都直接创建aspx(Web窗体,WebForm)

aspx 对context做了进一步的封装,context.Request和context.Response 也被封装到Page类中,可以直接用Request和Response了。

 

关于Render方法 (输出给控件浏览器)

这不是事件;在处理的这个阶段,Page 对象会在每个控件上调用此方法。所有 ASP.NET Web 服务器控件都有一个用于写出发送给浏览器的控件标记的 Render 方法。

 

如果创建自定义控件,通常要重写此方法以输出控件的标记。不过,如果自定义控件只合并标准的 ASP.NET Web 服务器控件,不合并自定义标记,则不需要重写 Render 方法。

1. WebForm技术简介

1.1 WebForm处理程序文件

WebFrom页面处理程序分为以.aspx和aspx.cs为后缀的两个文件。

.aspx

aspx是页面模板,是页面描述文件,就是html js css的内容,和aspx.cs结合得更好,不用像一般处理程序那样程序员自己去输出HTML字符串或读取填充模板。

控件都是定义在aspx中,内联的JavaScript、Css也是写在aspx中的。

前台页面上的@Page是指令集。

.aspx.cs

服务端的C#代码是定义在aspx.cs中。aspx控制页面显示和样式,aspx.cs控制程序逻辑,这种“前aspx后cs”的方式就被称为CodeBehind(代码后置)。

后台页面可以把Page_Load看成是WinForm里的Load事件("最先运行")。

1.2 后台向前台输出内容

   (1)直接在后台通过Response.Write("内容")

缺点:全都输出在页面的最上面。(可能会破坏页面布局)

    aspx中也可以主动访问cs中定义的非私有的成员

   (2)使用<%%>在前台页面特定位置指定输出。

      可以编写复杂的C#代码, for等所有C#代码都可以写在aspx中(不推荐)

      例如:<%=UserName %> <%=SayHello(); %> <%if (UserName == "aaa") { UserName = "bbb"; } %>    

1.3 aspx、cs、dll之间的关系

     反编译可以看到 codebehind.aspx生成了_codebehind_aspx类,继承了_codebehind_aspx.cs类

     _codebehind_aspx.cs继承了Page类,实现了IHttpHandler接口,因为SysTem.Web.UI.Page : TemplateControl, IHttpHandler

   网站,在运行的时候都会被编译成程序集(.dll文件)。页面会被编译成类。

   在编译后的程序集位置,使用Reflector打开这个临时dll,查看其内部原理:aspx生成的代码是cs类的子类

     Response.Write(this.GetType() "<br/>");

     

  通过以下这行代码可以将程序集所在位置打印出来。

Response.Write(this.GetType().Assembly.Location "<br/>");

反编译代码的部分重要方法如下:

1.3.1 _codebehind_aspx类的重要方法:__BuildControlTree()

页面类的BuildControlTree()方法创建了页面上的控件,放到Controls集合中,此时还没有输出。

页面控件的输出是在ProcessRequest()方法中输出的。

[DebuggerNonUserCode]

private void __BuildControlTree(_codebehind_aspx __ctrl)

{

    this.InitializeCulture();

    IParserAccessor accessor = __ctrl;

 

   //添加解析过的对象,将从字符串得到的LiteralControl控件放到controls集合中-设定了其在页面的位置(类似WinForm中将控件拖到窗体某位置,会在Designer.cs中定义控件的变量,InitialComponent初始化时会创建控件的对像和设置控件的属性,并将控件加到窗体的controls集合中,比如:this.Controls.Add(this.TextBox1))

    accessor.AddParsedSubObject(new LiteralControl("\r\n\r\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n"));

  

    //因为<head runat="server">带着runat="server",在服务器把Head标签作为对象来处理

    HtmlHead head = this.__BuildControl__control2();

    accessor.AddParsedSubObject(head);

    //<body> 标签没有加runat="server",于是作为普通标签,创建字符串控件LiteralControl

    accessor.AddParsedSubObject(new LiteralControl("\r\n<body>\r\n    "));

    // <form id="form1" runat="server"> 标签加了runat="server",同Head,作为对象处理

    HtmlForm form = this.__BuildControlform1();

    accessor.AddParsedSubObject(form);

   //普通标签,创建字符串控件LiteralControl

    accessor.AddParsedSubObject(new LiteralControl("\r\n</body>\r\n</html>\r\n"));

}

 

this.Controls.Count=5 (上面代码中的5个accessor.AddParsedSubObject)  //accessor:存取器

//创建form对象

[DebuggerNonUserCode]

private HtmlForm __BuildControlform1()

{

    HtmlForm form = new HtmlForm();

    base.form1 = form;

    form.ID = "form1";

    form.SetRenderMethodDelegate(new RenderMethod(this.__Renderform1));

    return form;

}

//this.__Renderform1 form对象中的表单元素

private void __Renderform1(HtmlTextWriter __w, Control parameterContainer)

{

    __w.Write("\r\n    <div>\r\n        用户名:<input type=\"text\" name=\"uname\" value=\"");

    __w.Write(base.uname);

    __w.Write("\" /><br/>\r\n        密码:<input type=\"text\" name=\"pwd\" value=\"");

    __w.Write(base.pwd);

    __w.Write("\" /><br/>\r\n        <input type=\"submit\" value=\"登录\" />\r\n    </div>\r\n    ");

}

 

1.3.2 _codebehind_aspx类的重要方法:ProcessRequest(HttpContext context);

调用了基类Page的ProcessRequest();

页面控件最终是在Page类该方法中输出(管道—render — TextWriter())。

[EditorBrowsable(EditorBrowsableState.Never)]

public virtual void ProcessRequest(HttpContext context)

{

    if ((HttpRuntime.NamedPermissionSet != null) && !HttpRuntime.DisableProcessRequestInApplicationTrust)

    {

        if (!HttpRuntime.ProcessRequestInApplicationTrust)

        {

            this.ProcessRequestWithAssert(context);

            return;

        }

        if (base.NoCompile)

        {

            HttpRuntime.NamedPermissionSet.PermitOnly();

        }

    }

    this.ProcessRequestWithNoAssert(context);

}

1.4 Page类的重要属性

1.4.1 Page类中的属性

在aspx.cs中可以直接用Request 和Response而不用写Context.Request Context.Response。

除此之外,还有Application、Session、Cache、Server等在ashx中的HttpContext context参数才有的属性。

这里的Request和Request是该类的属性,从Page类继承过来的。

反编译查看Page,找到了这两个属性-Properties(注意不是字段)

public HttpRequest Request

{

[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]

get;

}

public HttpResponse Response { get; }

1.4.2 Page类中的属性的来源

   Context从哪来的?

   public virtual void ProcessRequest(HttpContext context);//context作为参数传过来了

   方法中调用了this.ProcessRequestWithNoAssert(context)方法

   内部又调用了this.SetIntrinsics(context);

this.ProcessRequest();

   在内部又调用了private void SetIntrinsics(HttpContext context, bool allowAsync)

   (Intrinsics是内联函数的意思

   方法里面对Page的字段做了赋值,而这些字段被其对应的属性封装起来了,所以其可以直接用属性了

    this._context = context;

    this._request = context.Request;

    this._response = context.Response;

    this._application = context.Application;

    this._cache = context.Cache;

2. WebForm开发详解

2.1 WebForm(aspx)的代码内置

(1)方法在<script></script>中定义,在<%%>中调用

   <script></script> 中写C#代码,只能定义方法或定义变量,但是不能调用方法。

   <% %> 中写C#代码,可以去定义变量,可以去调用方法,但是不能定义方法。

   可以直接用Response.Write(),不用context了;

(2)<%%>中可以使用另一个<%%>中的变量(在同一个runat = “server”内)    

   如<%= list[1]%>

<%@ Page Language="C#" %>

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

<script runat="server">

    int Add(int num)

    {

        return 2 * num;

    }

</script>

<html xmlns="http://www.w3.org/1999/xhtml">

<head runat="server">

    <title></title>

</head>

<body>

    <form id="form1" runat="server">

    <%

        int num = 2;

        num = Add(num);

         %>

    <%

        Response.Write(num);

        Response.Write("<br/>");

         %>

    <% = 2*num%>

    </form>

</body>

</html>

2.2 WebForm(aspx)的代码后置

   偶尔用一下代码内置,会很方便。

   大多数情况下,还是使用用代码后置的方式,遵循代码分离的原则。

   

   代码后置(CodeBehind)的使用:

   前台代码写到aspx文件,服务器端代码写到cs文件(用到了部分类)

   

//CodeBehind.aspx

//该页面继承了CodeBehind类,所以可以访问其中的protected成员

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

 

<!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>

        用户名:<input type="text" name="uname" value="<% =uname %>" /><br/>

        密码:<input type="text" name="pwd" value="<% =pwd %>" /><br/>

        <input type="submit" value="登录" />

    </div>

    </form>

</body>

</html>

 

//CodeBehind.aspx.cs

public partial class CodeBehind : System.Web.UI.Page

{

    protected string uname;//protected,让子类(aspx)可访问

    protected string pwd;  //protected,让子类(aspx)可访问

    protected void Page_Load(object sender, EventArgs e)

    {

        if (IsPostBack)

        {

           uname = Request.Form["uname"];

           pwd = Request.Form["pwd"];

           if (uname =="admin"&&pwd =="admin")

           {

               Response.Write("OK");

           }

           else

           {

               Response.Write("Failed");

           }

        }

    }

}

2.3 WebForm的Request对象

(1) Request.UrlReferrer 请求的来源,可以根据这个判断从是从本站还是别的站点过来的请求,当请求图片资源时,可基于此实现防下载盗链、防图片盗链。

(2) Request.UserHostAddress获得访问者的IP地址

(3) Request.MapPath(virtulPath)将虚拟路径转换为磁盘上的物理路径,Request.MapPath("./a/b.aspx")就会得到D:\2008\WebSites\WebSite4\a\b.aspx

Server.MapPath里就是调用的Request.MapPath

 

public partial class _05_urlreferrer : System.Web.UI.Page

{

    protected void Page_Load(object sender, EventArgs e)

    {

        Response.Write(Request.UrlReferrer);

        Response.Write(Request.UserHostAddress);

        Response.Write(Request.Browser.Browser "<br />");

        Response.Write(Request.Browser.Platform "<br />");

        Response.Write(Request.Browser.IsMobileDevice "<br />");

    }

}

 

<%@ Page Title="主页" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">

</asp:Content>

<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">

    <a href="http://localhost:1606/sln0910/05-urlreferrer.aspx">http://localhost:1606/sln0910/05-urlreferrer.aspx</a>

</asp:Content>

 

点击上面Default.aspx网页的链接,得到如下结果

http://localhost:1135/0910WebForm/Default.aspx

127.0.0.1

IE

WinXP

False

2.4 WebForm的Response对象

2.4.1 Response对象的成员

响应的缓冲输出:为了提高服务器的性能,ASP.Net向浏览器Write的时候默认并不会每Write一次都会立即输出到浏览器,而是会缓存数据,到合适的时机或者响应结束才会将缓冲区中的数据一起发送到浏览器。

Response对象的主要成员:

(1) Response.Buffer、Response.BufferOutput:经过Reflector反编译,发现两个属性是一样的,Buffer内部就是调用的BufferOutput。这个属性用来控制是否采用响应缓存,默认是true。 BufferOutput默认值为true,这个时候可以借助Flush方法和Clear方法将当前缓冲区的信息输出。

BufferOutput的值为false,则每Write一次就会立即输出。

(2) Response.Flush()将缓冲区中的数据发送给浏览器。这在需要将Write出来的内容立即输出到浏览器的场合非常适用。

(3) Response.Clear()清空缓存区中的数据,这样在缓存区中的没有发送到浏览器端的数据被清空,不会被发送到浏览器。

(4) Response.ContentEncoding输出流的编码。

(5) Response.ContentType 输出流的内容类型,比如是html(text/html)还是普通文本(text/plain)还是JPEG图片(image/JPEG)。

(6) Response.OutputStream 输出流,在输出图片、Excel文件等非文本内容的时候要使用它。

(7) Response.End()  终止响应,将之前缓存中的数据发给浏览器,End()之后的代码不会被继续执行,End方法里调用了Flush()方法。在终止一些非法请求的时候,比如盗链等可以用End()立即终止请求。

public partial class _06_Response : System.Web.UI.Page

{

    protected void Page_Load(object sender, EventArgs e)

    {

        for (int i = 0; i < 10; i )

        {

            Response.Write(i "<br />");

            if (i == 4)

            {

                //把当前缓存中的数据输出到浏览器

                Response.Flush(); //此时输出 i为0 、1、2、 3、 4的情况

                //清除缓冲中的数据

                Response.Clear();

            }

            System.Threading.Thread.Sleep(500);

if(i == 8)

{

         //将缓冲中的数据发送给客户端,停止本页的执行

         Response.End(); //此时输出 i为5 、6、7、8的情况

}

        }

    }

}

2.4.2 Response.WriteFile下载

1) 浏览器默认下载

如果输出的是*.exe *.avi等这些浏览器处理不了的文件,浏览器才去下载

2)后台代码输出下载代码

      对于html/txt/jpeg等类型的请求,那么浏览器会直接显示。

  下载html/txt/jpeg等类型的文件,则需要添加Header,使用 context.Response.WriteFile实现下载。

attachment表示这是一个附件。

其中filename后为编码后的文件名,filename段为建议的保存文件名。

        string name = HttpUtility.UrlEncode("临时.jpg");

        context.Response.AddHeader("Content-Disposition", "attachment;filename =" name);

        context.Response.WriteFile("img/pig.jpg");

    /// <summary>

    /// 下载文件

/// </summary>

    private void DownLoad (string strPath)

    {

         //获取要下载的文件名

         string strFileName = System.IO.Path.GetFileName(strPath);

         //文件名进行编码

 strFileName =HttpContext.Current.Server.UrlPathEncode(strFileName) ;

         //strFileName =HttpUtility.UrlEncode(strFileName)

         //设置响应报文头

         HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=" strFileName);

         //执行下载

         HttpContext.Current.Response.WriteFile(strPath);

}

注:处理下载时文件名乱码

  Server.UrlPathEncode方法对文件名进行编码,实现文件名的正确显示。

HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment;filename=" HttpContext.Current.Server.UrlPathEncode(strFileName));

3) Response.Write的影响

下载文件Response.WriteFile代码的前面不能有Response.Write,否则文件被损坏。

(因为下载到的文件,是Response.Write的和Response.WriteFile两部分的字节流总和,导致下载得到的数据与文件数据不一致,从而文件无法打开)。

//HttpContext.Current.Response.Write(result);//不能再WriteFile前Write

HttpContext.Current.Response.WriteFile(strPath);

2.4.3 Response. Redirect 跳转

Response .Redirect的原理:向浏览器响应302状态码,浏览器从响应报文获取新的url,然后向新的url发一个Http请求。

跳转之前会先调用Response.Clear();把之前的Response.Write()的内容清除掉了。

内部调用了Response.End()方法,所以之后的Response.Write()也不执行了。

会通知浏览器‘请重新访问url指向的这个网址’

这个跳转页面的方法跳转的速度不快,因为它要走2个来回(2次postback),但他可以跳转到任何页面,没有站点页面限制(如可以由雅虎跳到新浪),同时不能跳过登录保护。但速度慢是其最大缺陷!redirect跳转机制:首先是发送一个http请求到客户端,通知需要跳转到新页面,然后客户端在发送跳转请求到服务器端。需要注意的是跳转后内部空间保存的所有数据信息将会丢失(源页面的所有内容丢失,viewstate等),所以需要用到session。

如何使用redirect方法在查询字符串中使用汉字,因为经常的情况是出现乱码,原因是url不支持汉字。这个时候需要转换:

  string message =Server.UrlEncode("欢迎来到赛跑专栏");

  先转换,再使用查询字符串

  Response.redirect("webform2.aspx?msg=" message);

2.5 WebForm的Server对象 (HttpServerUtility)

   Server是上下文对象context的一个属性,(与Request和Response一样),是HttpServerUtility类的一个对象。  

2.5.1 Server.MapPath

取得文件的物理路径(内部就是调用了Request.MapPath)

这样获取的是网站根的物理路径

Server.MapPath(null)      "根下的数据库路径\数据库名称

2.5.2 Server.Transfer

ASP.NET服务端实现页面跳转的3种方式:Response.Redirect、Server.Transfer、Server.Execute。

Server.Transfer,很大程度上代替Response.Redirect,而且更快,但是不能完全取代Response.Redirect重新定向的功能,因为:

A)Transfer是内部接管,只能用相对地址,因此不能像Redirect那样重定向到外部网站。也不能内部重定向到ashx,否则会报错“执行子请求出错”。

B)不能在url里面跟?a=1&b=2之类的参数

 

执行另外一个页面,执行完成后(最后一次性返回给浏览器的),不会返回第一个页面。

由于浏览器不知道服务器的跳转,浏览器的url地址仍然保留的是原页面的地址。浏览器地址栏不会变化。因为是内部接管,所以在被重定向到的页面中是可以访问到Request、Cookies等这些来源页面接受的参数的,就像这些参数是传递给他的。

Server.Transfer速度快,只需要一次postback ,但是必须是在同一个站点下,因为它是server的一个方法。另外,他能跳过登录保护。设计一个由页面1到页面2的跳转,正常进入到页面2需要Forms验证的登录,但如果跳转语句使用transfer的话,那就不会弹出登录页面了。

 

public partial class Server_02_Transfer : System.Web.UI.Page

{

    protected void Page_Load(object sender, EventArgs e)

    {

        Response.Write("转向之前");

        //执行另外一个页面,执行完成之后不返回本页面

        Server.Transfer("02-over.aspx");

        Response.Write("转向之后");

    }

}

上述代码执行输出:

 

转向之前

Transfer - over

2.5.3 Server.Execute

执行另外一个页面,执行完成后返回本页面继续执行(最后一次性返回给浏览器的) ,由于浏览器不知道服务器的跳转,浏览器的url地址仍然保留的是原页面的地址。

Server.Execute必须是跳转同一站点下的页面。这个方法是需要将一个页面的输出结果插入到另一个aspx页面的时候使用,大部分是在表格中,将某一个页面类似于嵌套的方式存在于另一页面。

 

public partial class Server_01_Execute : System.Web.UI.Page

{

    protected void Page_Load(object sender, EventArgs e)

    {

        Response.Write("转向之前");

        //执行另外一个页面,执行完成后返回本页面继续执行

        Server.Execute("01-over.aspx");

        Response.Write("转向之后");

    }

}

转向之前

Execute-Over

转向之后

 

//不能转向外部网站或一般处理程序

public partial class Server_01_Execute : System.Web.UI.Page

{

    protected void Page_Load(object sender, EventArgs e)

    {

        Response.Write("转向之前");

        //1 不能转向外部网站

        //2 不能转向一般处理程序

        Server.Execute("Handler.ashx");//会报错     

        Response.Write("转向之后");

    }

}

 

附:页面跳转应用场景小结

超链接

    如果要让用户来决定何时转换页面以及转到哪一个页面,超级链接最适合。

如果要用程序来控制转换的目标,但转换的时机由用户决定,使用Web服务器的HyperLink控件,动态设置其NavigateUrl属性。

 

Response.Redirect

如果要把用户连接到另一台服务器上的资源,使用Response.Redirect。

  如果要把用户连接到非当前服务器页面,或非aspx的资源,例如HTML页面、ashx页面,用Response.Redirect。

如果要将查询字符串作为URL的一部分保留给服务器,使用Response.Redirect。

如果要确保HTML输出合法,请使用Response.Redirect。

 

Server.Transfer

如果要将执行同一Web服务器的aspx页面间的跳转,纯服务端的跳转(不用再进行登录的Forms验证),应当使用Server.Transfer而不是Response.Redirect,因为Server.Transfer能够避免不必要的网络通信,从而获得更好的性能和浏览效果。

 

Server.Execute

如果要捕获一个aspx页面的输出结果,然后将结果插入另一个aspx页面的特定位置,则使用Server.Execute。

 

2.5.4 Server.UrlEncode和Server.UrlDecode

将文本编码成可以在浏览器中可正确浏览的格式

UrlEncode方法和UrlDecode方法—URL编码和解码

UrlEncode是处理超链接中的中文问题, HtmlEncode是处理html代码的。

 

Server.HtmlDecode()、Server.HtmlEncode() Server.UrlEncode()、Server.UrlDecode()是对HttpUtility类中相应方法的一个代理调用。推荐总是使用HttpUtility,因为有的地方很难拿到Server对象,而且Server的存在是为以前ASP程序员习惯而留的。还是推荐用HttpUtility.HtmlEncode。

 

当在url中传递的参数的值含特殊符号时,需要进行url编码

 

public partial class _04_urlEncode : System.Web.UI.Page

{

    protected void Page_Load(object sender, EventArgs e)

    {

        string name = "李&明";

        //Server.UrlEncode—Url编码

        name = Server.UrlEncode(name);

        Response.Write("<br /><a href='04-start.aspx?name=" name "'>aaaa</a>");

    }

}

 

//Server.UrlDecode

public partial class _04_start.aspx : System.Web.UI.Page

{

    protected void Page_Load(object sender, EventArgs e)

    {

        //string s = "蒋&坤";

        //s = Server.UrlDecode(s);

        //Response.Write(s);

 

        //Request.QueryString["name"]内部会进行解码

        string s = Request.QueryString["name"];

 

        Response.Write(s);

    }

}

2.5.5 Server.HtmlEncode和Server.HtmlDecode

public partial class Server_03_htmlEncode : System.Web.UI.Page

{

    protected void Page_Load(object sender, EventArgs e)

    {

        string html = "<font color='red'>传智</font><b>播客</b>";

        //html编码后  把特殊符号转义

        html = Server.HtmlEncode(html);       

        Response.Write(html);

 

        Response.Write("<br />");

        //html解码后  还原符号到转义前

        Response.Write(Server.HtmlDecode(html));

    }

}

2.6 WebForm请求方式判断:IsPostBack

2.6.1 IspostBack的使用

与页面的runat="server"同时用!!!

   (1)判断是否是post请求回传回来的.

   (2)页面上必须有一个runat="server"的form标签 ispostback才可以使用,否则值永远为false。

使用的示例代码如下

//03-Login.aspx

<body>

    <!—页面上必须有一个runat=server的form标签 ispostback才可以使用—>

    <!—改为用runat="server"—>

    <form id="form1" runat="server" >    

        用户名:<input type="text" name="txtName" value="<%= name %>" /><br />

         密码:<input type="text" name="txtPwd" value="<%= pwd %>" /><br />

         <input type="submit" value="登陆" />

    </form>

</body>

 

 

//03-Login.aspx.cs

public partial class _03_Login : System.Web.UI.Page

{

    protected string name = "";

    protected string pwd = "";

    protected void Page_Load(object sender, EventArgs e)

    {

        //判断是否是post请求回传回来的

        if (IsPostBack)

        {

            name = Request.Form["txtName"];

            pwd = Request.Form["txtPwd"];

            if (name == "admin" && pwd == "admin")

            {

                Response.Write("登陆成功");

            }

            else

            {

                Response.Write("登陆失败");

            }

        }

    }

}

2.6.2 IspostBack的原理

   页面加了runat="server"之后,运行之后查看源文件 发现页面中生成了隐藏域__VIEWSTATE

   post提交后,服务器读取到__VIEWSTATE有值,IsPostBack就设为true了,原理跟手写隐藏域一样,只是代码简化了。

<div>

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTExOTQxOTMwMTJkZGdqRHvmXP/dTcNZVsyGTkyK6Xkj3tJMUo1e8ZIwLK/w" />

</div>

 

以下代码通过隐藏域自己实现IsPostBack,说明IsPostBack的原理。

//03-Login.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="03-Login.aspx.cs" Inherits="_03_Login" %>

 

<!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" >  改为不用runat="server"—>

    <form id="form1" method="post" action="03-Login.aspx">

         <input type="hidden" name="_viewstate" value="aa" />

        用户名:<input type="text" name="txtName" value="<%= name %>" /><br />

          密码:<input type="text" name="txtPwd" value="<%= pwd %>" /><br />

          <input type="submit" value="登陆" />

    </form>

</body>

</html>

 

//03-Login.aspx.cs

public partial class _03_Login : System.Web.UI.Page

{

    protected string name = "";

    protected string pwd = "";

    protected void Page_Load(object sender, EventArgs e)

    {

        //判断是否是post请求

    string viewstate = Request.Form["_viewstate"];

        if (!string.IsNullOrEmpty(viewstate))

        {

            name = Request.Form["txtName"];

            pwd = Request.Form["txtPwd"];

            if (name == "admin" && pwd == "admin")

            {

                Response.Write("登陆成功");

            }

            else

            {

                Response.Write("登陆失败");

            }

        }

    }

}

3. 客户端状态保持Cookie 和ViewState

引子:

无状态—Http协议要求请求响应之后,连接Socket关闭—导致了无状态

状态保持—把状态信息记录在某个地方,需要的时候读取出来

 

如很多页面要求用户是登录成功的状态,才允许访问。

 

Http协议的,与语言无关的保存状态的方案:

 

HiddenField(已经用过了post)—保存在浏览器端(有客户端提交给服务器)

QueryString(已经用过了get)—通过Url传参

 

Cookies(重点学习)

 

ASP.NET特有的保存状态的方案:

ViewState(重点学习)

ControlState(了解)

 

比较:默认情况 Cookie的数据是所有页面都可以访问的,ViewState是单前页面可以访问。

      通常不考虑Cookie禁用的情况,互联网前台通常禁用ViewState