码迷,mamicode.com
首页 > Web开发 > 详细

传智播客学习之ASP.net基础第七天

时间:2015-11-23 16:17:32      阅读:203      评论:0      收藏:0      [点我收藏+]

标签:

图片下载系统

需求:用户表增加一个级别字段。只有登录用户才能下载Images下的图片文件(Session中标识是否登录),如果用户没有登录则首先重定向到登录界面让用户登录,用户登录成功则跳转到下载列表页面,下载链接固定写好即可。如果登录用户是普通用户则在图片左上角加上“免费用户试用”的字样。

新建ImageDownload.aspx页面: 

 <form id="form1" runat="server">
    <div>
    
        <asp:Label ID="Label1" runat="server" Text="用户名:"></asp:Label>
        <asp:TextBox ID="txtUserName" runat="server"></asp:TextBox>
        <br />
        <asp:Label ID="Label2" runat="server" Text="密码:"></asp:Label>
        <asp:TextBox ID="txtPassword" runat="server" TextMode="Password"></asp:TextBox>
        <br />
        <asp:Label ID="LabelErrorMsg" runat="server" BackColor="Red" Visible="False"></asp:Label>
        <br />
        <asp:Button ID="btnLogin" runat="server" Text="登录" onclick="btnLogin_Click" />
    
    </div>
    </form>

 

 新建数据库PicUserDB.mdf,添加新表T_Users,字段:ID,类型int,自动增长;UserName,Password,类型为nvarchar(50);Level,类型为int,0表示免费会员,1表是收费会员。录入两条数据,张三,123,1;李四,123,0。用强类型DataSet建立数据访问:在项目中新建文件夹DAL,在DAL文件夹上右击选择“添加”“新建项”选择“数据”选择“数据集”,输入名称:DataSetPic,在新生成的DataSetPic.xsd中把T_Users表拖到上面。右击T_UsersTableAdapter,选择“添加查询”“使用SQL语句”“下一步”“SELECT(返回行)(S)”“下一步”,输入“SELECT ID, UserName, Password, [Level] FROM dbo.T_Users where ID=@ID”将“填充DataTable(I)”前面的选择取消,在方法名称中输入“GetDataByID”,点“完成”。完成了一个根据ID新建的查询。接着新建一个根据UserName生成的查询,语句“SELECT ID, UserName, Password, [Level] FROM dbo.T_Users

where UserName=@UserName”。

ImageDownload.aspx页面的后台代码如下:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using ImageDownload.DAL.DataSetPicTableAdapters;

namespace ImageDownload
{
    public partial class ImageDownload : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        protected void btnLogin_Click(object sender, EventArgs e)
        {
            T_UsersTableAdapter adapter = new T_UsersTableAdapter();
            var data = adapter.GetDataByUserName(txtUserName.Text);
            if (data.Count <= 0)
            {
                LabelErrorMsg.Text = "用户名不存在!";
                LabelErrorMsg.Visible = true;
               Response.Redirect("DownloadList.aspx");//将用户重定向到Download.aspx
            }
            else
            {
                var user = data.Single();//Single方法:返回唯一一条数据,如果数据为0或者多条,,则抛出异常。把程序的错误扼杀在摇篮中。
                if (user.Password == txtPassword.Text)
                {
                    Session["是否登录"] = true;
                    Session["登录用户Id"] = user.ID;
                }
                else
                {
                    LabelErrorMsg.Text = "密码错误!";//todo:增加防暴力破解,重复错误5次就锁定帐户半个小时。
                    LabelErrorMsg.Visible = true;
                }
            }
        }
    }
}

 

 浏览ImageDownload.aspx页面,输入用户名,密码,页面运行正常。接着在项目中新建文件夹Images,考入几张图片,命名为1~7.jpg,新建DownloadList.aspx页面, 

 <form id="form1" runat="server">
    <div>
    <a href="DownloadPic.ashx?FileName=1.jpg">美女1</a><br/>
    <a href="DownloadPic.ashx?FileName=2.jpg">美女2</a><br/>
    <a href="DownloadPic.ashx?FileName=3.jpg">美女3</a><br/>
    <a href="DownloadPic.ashx?FileName=4.jpg">美女4</a><br/>
    <a href="DownloadPic.ashx?FileName=5.jpg">美女5</a><br/>
    <a href="DownloadPic.ashx?FileName=6.jpg">美女6</a><br/>
    <a href="DownloadPic.ashx?FileName=7.jpg">美女7</a><br/>
    </div>
</form>

 

 浏览ImageDownload.aspx页面,登陆成功后就能正确导向DownloadList.aspx页面。

新建一般处理程序DownloadPic.ashx:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using ImageDownload.DAL.DataSetPicTableAdapters;
using System.Drawing;
using System.Web.SessionState;
using System.Drawing.Imaging;

namespace ImageDownload
{
    /// <summary>
    /// DownloadPic 的摘要说明
    /// </summary>
    public class DownloadPic : IHttpHandler,IRequiresSessionState
    {

        public void ProcessRequest(HttpContext context)
        {
            if (context.Session["是否登录"] == null)
            {
                context.Response.Redirect("ImageDownload.aspx");//ToDo:转到跳转页(3秒以后自动转向登录页面,也有一个链接点击直接进入登录页面)
            }
            else
            {
                string filename = context.Request["FileName"];

                context.Response.ContentType = "image/JPEG";
                string encodeFileName = HttpUtility.UrlEncode(filename);
                context.Response.AddHeader("Content-Disposition", string.Format("attachment;filename=\"{0}\"", encodeFileName));

                int userId = (int)context.Session["登录用户Id"];
                T_UsersTableAdapter adapter = new T_UsersTableAdapter();
                var data = adapter.GetDataByID(userId);
                var user = data.Single();
                if (user.Level == 0)//普通用户
                {
                    using (Bitmap bitmap = new Bitmap(context.Server.MapPath("images/" + filename)))
                    {
                        using (Graphics g = Graphics.FromImage(bitmap))
                        {
                            g.DrawString("免费用户试用--" + user.UserName, new Font("宋体", 20), Brushes.Red, 0, 0);
                        }
                        bitmap.Save(context.Response.OutputStream, ImageFormat.Jpeg);//为了演示,效率不高。
                    }
                }
                else//收费用户
                {
                    context.Response.WriteFile("images/" + filename);//有攻击漏洞,如果输入“下载图片.ashx?FileName=../下载图片.ashx.cs”还能将ashx源代码下载下来。
                }
            }
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

 

IRequiresSessionState这是一个很重要的接口,如果不加这个接口,该页面的session就不能用。

浏览DownloadList.aspx,点击超连接,导向到ImageDownload.axps页面,输入用户名:张三,密码:123,正确登录后,能正常下载图片;输入用户名:李四,密码:123,登录后下载图片,图片上会有“免费用户试用”的字样。

这样看上去没有什么问题,如果用户正常在页面点击,当然没有什么问题,但如果用户在网站输入http://localhost:1207/DownloadPic.ashx?FileName=../DownloadPic.ashx.cs,就会把网站DownloadPic.ashx.cs源代码下载下来,网站就不安全了。

在数据库中T_User增加两个字段:ErrorTimes(类型为int)、LastErrorTime,类型为datetime。由于数据库中的表修改了,所以要重新配置DataSetPic.xsd,右击T_Uwers表,选择“配置”,点击“查询生成器”,购先刚才添加的两个字段。点击“完成”。完成配置。在DataSetPic.xsd中添加更新方法,右击T_Uwers,选择“添加查询”“下一步”“UDPDATE(U)”“下一步”,输入:UPDATE T_Users SET ErrorTimes = ErrorTimes+1, LastErrorTime = getdate() WHERE ID=@ID,点击“下一步”,输入名称:IncErrorTimesByID,点击“完成”。再添加一个“Update”方法,重置错误次数,语句:UPDATE [T_Users] SET ErrorTimes=0 where ID=@ID,名称:ResetErrorTimesByID。

修改ImageDownload.aspx.cs中的代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using ImageDownload.DAL.DataSetPicTableAdapters;

namespace ImageDownload
{
    public partial class ImageDownload : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        protected void btnLogin_Click(object sender, EventArgs e)
        {
            T_UsersTableAdapter adapter = new T_UsersTableAdapter();
            var data = adapter.GetDataByUserName(txtUserName.Text);
            if (data.Count <= 0)
            {
                LabelErrorMsg.Text = "用户名不存在!";
                LabelErrorMsg.Visible = true;
            }
            else
            {
                var user = data.Single();//Single方法:返回唯一一条数据,如果数据为0或者多条,,则抛出异常。把程序的错误扼杀在摇篮中。
                if (!user.IsLastErrorTimeNull() && !user.IsErrorTimesNull())
                {
                    double span = (DateTime.Now - user.LastErrorTime).TotalMinutes;//计算当前时间与上次错误时间之间的差的分钟数
                    if (user.ErrorTimes > 5 && span <= 30)
                    {
                        LabelErrorMsg.Text = "错误次数过多,30分钟后再重试!";//一旦被锁定就不告诉访问者的密码是对还是错
                        LabelErrorMsg.Visible = true;
                        return;
                    }
                }
                if (user.Password == txtPassword.Text)
                {
                    Session["是否登录"] = true;
                    Session["登录用户Id"] = user.ID;
                    adapter.ResetErrorTimesByID(user.ID);
                    Response.Redirect("DownloadList.aspx");//将用户重定向到Download.aspx
                }
                else
                {
                    adapter.IncErrorTimesByID(user.ID);//注意数据库为Null的问题,IsNull函数。
                    LabelErrorMsg.Text = "密码错误!";//todo:增加防暴力破解,重复错误5次就锁定帐户半个小时。
                    LabelErrorMsg.Visible = true;
                }
            }
        }
    }
}

浏览ImageDownload.aspx页面,输入用户名:张三,输入错的密码,多次确定,程序没有达到预期效果,这是因为数据库中的null+1=null的原因。数据库中有个ISNULL的方法,可以改变数据库中的值如select isnull(ErrorTimes,100) as expr1 form T_Users。修改DataSetPic.xsd中的IncErrorTimesByID方法,修改语句成:UPDATE T_Users SET ErrorTimes =IsNULL(ErrorTimes,0)+1, LastErrorTime = getdate() WHERE ID=@ID。

接下来新建RedirectLogin.htm页面:

<!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>
    <title></title>
    <script type="text/javascript">
        var leftSeconds = 5;
        setInterval(function () {
            if (leftSeconds <= 0) {
                window.location.href = ImageDownload.aspx;
            }
            document.getElementById("leftDiv").innerText = leftSeconds;
            leftSeconds--;
        }, 1000);
    </script>
</head>
<body>

请先登录,页面将在5秒后转向登录页面!如果你想立即进入登录页面,请<a href="ImageDownload.aspx">点击这里</a><br/>还剩<div id="leftDiv">0</div>秒

</body>

</html>

再将上面的DownloadPic.ashx文件中的context.Response.Redirect("ImageDownload.aspx");这名改成:context.Response.Redirect("RedirectLogin.htm");

浏览ImageDownload.aspx页面,达到要求。

补充:

    1、WebApplication每次修改以后点击【生成解决方案】也能立即看到修改效果,不需要重启浏览器。原理:生成以后才将变化的部分生成到DLL,而WebSite则每次访问页面的时候会检查cs是否变了,变了则自动重新编译,所以每次修改以后立即有效果。

    2、方便开发不用每次调试都设定起始页,在项目的选项中设定【Web】->启动操作->当前页面,这样当前激活的页就是起始页。

    3、VS2008一个Bug的发现:路径中有#的问题,发现问题的过程重现。

 

 

WebForm1:

    1、如果每次输出网页都直接用HttpHandler的话太痛苦了(开始我们建网页是这样做的,先建一个htm页面,再建一个ashx文件,把htm模板中的内容读取到ashx文件中,再发给用户。),所以一般生成html的时候都直接创建aspx(Web窗体,WebForm)。

    2、WebForm分为两个文件aspx和aspx.cs,aspx是页面模板,是页面描述文件,就是html的内容,和aspx结合的更好,不用像一开始哪样程序员自己去填充模板,控件都是定义在aspx中,内联的JavaScript、Css也是写在aspx中的,服务端的C#代码是定义在aspx.cs中。aspx控制页面长相,cs控制程序逻辑,这种“前aspx后cs”的方式就被称为CodeBehind。aspx就是模板引擎,不需要再去寻找第三方的模板引擎。

 

WebForm2:

    1、cs可以调用aspx中的控件,aspx中也可以访问cs中定义的字段、函数,还可以编写复杂的C#代码,for等所有C#代码都可以写在aspx中(不推荐)。

2、前面<%=UserName %> <%SayHello(); %> <%if(UserName=="aaa"){UserName="bbb";} %>  后面在当前位置输出表达式的值的时候使用<%=UserName %>,不要丢了=,相当于在当前位置调用Response.Write(UserName)。

Aspx页面:

<form id="form1" runat="server">
    <div>
    <%for(int i=0;i<10;i++){%>
         你好!
    <%} %>
    <br/>
    <%=MyName %>
    <br/>
    <%Response.Write("你好啊!"); %><br/>
    <%
        for (int i = 0; i < 10; i++)
        {
            Response.Write("哈哈");
        }
         %><br/>
         <%SayHello(); %><br/>
         <%=QuotodStr("hello") %>
    </div>
    </form>

后台代码:

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

public partial class _15WebForm入门1_Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
       
    }
    public string MyName
    {
        get
        {
            return "Tom";
        }
    }
    public void SayHello()
    {
        Response.Write("哈楼!");
    }
    public string QuotodStr(string s)
    {
        return "\""+s+"\"";
    }
}

 3、使用的函数、代码相当于在这个位置调用函数、执行代码。注意aspx中调用cs的成员级别必须是Protected或者Public,不能是Private的。

aspx、cs、dll之间的关系(*)

    1、在WebForm的页面中执行下面的代码:

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

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

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

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

    2、发现当前执行页面的类名是ASP.webform1_aspx这样的类名,父类才是ASPNETTest1.WebForm1。

    3、使用Reflector打开这个临时dll,反编译这两个类,发现ASPNETTest1.WebForm1是在VS中编写的aspx.cs类,而ASP.webform1_aspx则是一个继承自ASPNETTest1.WebForm1的子类,ASP.webform1_aspx代码是根据aspx内容动态生成的构建网页内容类。综上,aspx最终也会生成一个类,这个类是继承自aspx.cs中的类。查看反编译以后的代码,可以看到就是编译生成了普通的.Net代码。因为aspx生成的代码是cs类的子类,所以就明白了为什么“aspx中调用cs的成员级别必须是Protected或者Public,不能是Private的。”

 

Page类成员:

    1、Request、Response、Server属性:对Context.Request、Context.Response、Context.Server的简化调用。

    2、AppRelativeVirtualPath属性:获得页面相对于应用根路径的路径,比如~/Default2.aspx

    3、FindControl(CtrlId),根据控件的id找到控件。一般情况下直接在棋友中写控件Id引用控件就可以了,但是对于有些场合:使用ListView等控件的模板、编写自定义控件等则需要使用FindControl("TextBox1").txtBox.Text="aaa"。

    4、IsPostBack、Session

    5、ResolveClientUrl(url)将虚拟路径转换为客户端访问时的路径,比如ResolveClientUrl("~/a/b.aspx")结果是a/b.aspx,这通常在ListView等控件的模板中输出Html使用。基本就是对VirtualPathUtility.ToAbsolute简化调用。考虑当前页面的相对路径,生成的路径短。

    6、ResolveUrl(url)将虚拟路径转换为相对于网站根目录的路径,比如ResolveUrl("~/a/b.aspx")的结果是/WebSite4/a/b.aspx。不考虑当前页面。VirtualPathUtility.ToAbsolute直接转换为一个全路径。

Aspx页面:

form id="form1" runat="server">
    <div>
    <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
        <asp:Button ID="Button1"
        runat="server" Text="Button" onclick="Button1_Click" />
    </div>
    </form>

后台代码:

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

public partial class _17Page类1_17Page类1 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
       Response.Write(this.AppRelativeVirtualPath+"<br/>");
       Response.Write(ResolveClientUrl("~/17Page类1/17Page类1.aspx")+"<br/>");
       Response.Write(ResolveUrl("~/17Page类1/17Page类1.aspx"));

    }
    protected void Button1_Click(object sender, EventArgs e)
    {
        //TextBox1.Text = "1";
        TextBox txt = FindControl("TextBox1") as TextBox;
        txt.Text = "2";
    }
}

 

 

 

传智播客学习之ASP.net基础第七天

标签:

原文地址:http://www.cnblogs.com/wujianwei/p/4988518.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!