第10季asp.net基础
- 什么是ASP.Net:
ASP.Net是一种动态网页技术,在服务器端运行.Net代码,动态生成HTML。可以使用javascript、Dom在浏览器端完成很多工作,但是有很多工作无法在浏览器端完成,比如存储数据、访问数据库、复杂的业务逻辑运算、安全性要求高的逻辑运算等。
- WebApplication(Web应用程序)和WebSite(网站)的区别,WebSite是为了兼容从ASP转过来的开发人员的习惯而存在的,用起来简单,比如不需要创建命名空间、cs代码修改以后不需要重启就能看到变化(无论是WebSite还是WebApplication,修改aspx都不需要重启),但是不利于工程的开发,比如代码出错不容易发现、代码不分命名空间。开发技术上没有任何区别,只是开发、调试习惯不同而已!
- 简单的请求页面:
用户点击提交后,会显示下面页面:
代码如下:
这里注意:在表单元素设置name属性。不能用id代替。因为服务器只认name,所以说,name是为服务器准备的,id是为web准备的!
- 要点分析:
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/html";
string fullPath = context.Server.MapPath("Hello2.html");//得到文件全路径
string content = System.IO.File.ReadAllText(fullPath);//读取文件的全部内容
context.Response.Write(content);//将数据显示给用户!
string username = context.Request["UserName"];//获取提交后的数据!
if (string.IsNullOrEmpty(username))//如果username为空!//通常在开发项目中使用这种方法!
{
context.Response.Write("直接进入!");
}
Else//否则!
{
context.Response.Write("提交进入!");
}
}
- 将数据替换为用户输入的数据:
Hello2.ashx文件:
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/html";
string username = context.Request["UserName"];
string msg = "";
string ispostback = context.Request["ispostback"];
if (ispostback=="true")
{
context.Response.Write("提交进入!");
msg = username + "你好!";
}
else
{
context.Response.Write("直接进入!");
username = "";
msg = "";
}
string fullPath = context.Server.MapPath("Hello2.html");//得到文件全路径
string content = System.IO.File.ReadAllText(fullPath);//读取文件的全部内容
content = content.Replace("@value", username);//将value的值替换为username的值!
content = content.Replace("@msg",msg);//将msg替换为msg的新值!
context.Response.Write(content);
}
Hello2.html文件:
<body>
<form action="Hello2.ashx">
<input type="hidden" name="ispostback" value="true"/>
姓名:<input type="text" name="UserName" value="@value"/><input type="submit" value="提交">
@msg
</form>
</body>
- 通过设定form 的method属性来指定表单的提交方式:
Method有两个值:分别为:get 和post
Get(默认值):是通过URL传递表单值;
post传递的表单值是隐藏到http报文中,url中看不到;
它们两个的区别:
Get传递的数据量是有限的!而post没有那个限制!
Post会有浏览器提示重新提交表单问题!get则没有!
- 简单的自增数据:
.ashx文件:
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/html";
string number = context.Request["number"];//通过表单传过来的数据都是string类型!
string ispostback = context.Request["ispostback"];
if (ispostback == "true")//说明点击【自增】进来的,需要把当前数值自增!
{
int i = Convert.ToInt32(number);
i++;
number=i.ToString();
}
else//第一次进来值为0!
{
number = "0";
}
string fullPath = context.Server.MapPath("incValue1.html");
string content = System.IO.File.ReadAllText(fullPath);
content = content.Replace("@value", number);
context.Response.Write(content);
}
.Html文件:
<body>
<form action="incValue1.ashx" method="POST">
<input type="hidden" name="ispostback" value="true"/>
<input type="text" name="number" value="@value" /><input type="submit" value="自增"/>
</form>
</body>
这里注意:
- 以后写程序的时候,.html不会被客户端访问!它是一个模板!被客户端访问的是.ashx文件!
- 如果将上面的sumbit改为button后,按下按钮时,数据是不会提交数据库中!要写为button,必须修改为下列代码才会和sumbit的作用一致!
<body>
<form id="form1" action="incValue1.ashx" method="POST">
<input type="hidden" name="ispostback" value="true"/>
<input type="text" name="number" value="@value" /><input type="button" value="自增" onclick="document.getElementById('form1').submit()">
</form>
</body>
- 加法计算器:
.ashx文件:
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/html";
string huilaile = context.Request["huilaile"];
string reasult = "";
string num1 = "";
string num2 = "";
if (huilaile == "yes")
{
num1 = context.Request["num1"];
num2 = context.Request["num2"];
reasult = (Convert.ToInt32(num1) + Convert.ToInt32(num2)).ToString();
}
string html模板文件全路径 = context.Server.MapPath("AddCalc1.html");
string content = System.IO.File.ReadAllText(html模板文件全路径);
content = content.Replace("@reasult",reasult);
content = content.Replace("@num1", num1);
content = content.Replace("@num2", num2);
context.Response.Write(content);
}
.html文件:
<body>
<form action="AddCalc1.ashx" method="POST">
<input type="hidden" name="huilaile" value="yes"/>
<input type="text" name="num1" value="@num1" />+<input type="text" name="num2" value="@num2"/><input type="submit" value="="/><input type="text" value="@reasult" readonly="readonly"/>
</form>
</body>
- 实现div内文本自增:
.ashx文件:
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/html";
string ispostback = context.Request["ispostback"];
//为什么设定div在服务器取不出值来呢?因为不是服务器来读取网页,而是浏览器收集客户在表单中输入的字段,然后形成请求参数发给服务器处理程序,由于没有把div当前的innertext发给服务器,所以服务器无法得知当前的值,也不要幻想有办法能将div的innertext提交给服务器,因为只有设定name的input、textarea、select的value属性值才会被提交给服务器。
string value = "0";
if (ispostback == "true")
{
value = context.Request["num1"];
int i = Convert.ToInt32(value);
i++;
value = i.ToString();
}
string fullpath = context.Server.MapPath("IncValue2.html");
string content = System.IO.File.ReadAllText(fullpath);
content = content.Replace("@value", value);
context.Response.Write(content);
}
.html文件:
<body>
<form action="IncValue2.ashx">
<input type="hidden" name="ispostback" value="true"/>
<input type="hidden" name="num1" value="@value"/>
<div>@value</div>
<input type="submit" value="自增"/>
</form>
</body>
- 非表单元素无法将客户端的元素值传递给服务器端,即使是表单元素也只能传递value值,对于其他属性值比如背景颜色、大小等也是无法传递的,因此对于这些值都要存在隐藏字段中,这就是ASP.net中ViewState的原理。
Label版本的值存到了ViewState中,TextBox版本的不用存,因为TextBox就是input,自己就会提交给服务器,不需要隐藏字段。
点击按钮使文本框自动增长:l
例如:
TextBox1.Width=new Unit(TextBox1.Width.Value + 10);//每点击一次就会增长10个像素!
- 禁用ViewState(严格说只是减少字段的尺寸!)
禁用单个控件的ViewState:
禁用整个页面的ViewState:
- 无状态Http:
Http协议是无状态的,不会记得上次和网页"发生了什么"。服务器不记得上次给了浏览器什么浏览器需要记住这些值,(input就是记到Value,对于其他的值就要放到隐藏字段中,比如ViewState),下次再提交服务器的时候(请在我的宽度基础上再增加10)就是把上次的值提交给服务器,让他想起来。如果要知道上一次的状态,一个方法是在对浏览器响应结束之前将状态信息保存到页面表单中,下次页面在向服务器发出请求的时候带上这些状态信息,这样服务器就能根据这些状态信息还原上次的状态,类似于看病的病历本!
状态信息保存到隐藏字段中的缺点:加大网站的流量,降低访问速度,机密数据放到表单中会有数据欺骗等安全性问题!
- Cookie
表单是和页面相关的,只有浏览器端提交了这些数据服务器端才能得到。而有时候希望在服务器端任意的地方存取一些和访问者相关的信息,这时候就不方便将这些信息保存到表单中了,因为如果那样的话,必须随时注意在所有页面表单中都保存这些信息。Cookie是和站点相关的,并且每次向服务器请求的时候除了发送表单参数外,还会向站点相关的所有Cookie都提交给服务器,是强制性的。Cookie也是保存在浏览器端的,而且浏览器会在每次请求的时候都会把和这个站点相关的Cookie
提交到服务器中并且将服务器返回的cookie更新回数据库中!因此必须将信息保存在cookie中,然后在服务器端读取、修改。服务器返回数据除了普通的http和html的数据以外,还会返回修改的cookie,浏览器把拿到的cookie值更新本地的浏览器的cookie就可以!
- 互联网优化的案例:
图片服务器和和主站的域名不一样,降低cookie流量的传输!
- 案例:
public partial class 变量1 : System.Web.UI.Page
{
private int i = 0;//每次请求来了,都会new一个新的实现了IHttpHandler接口的类"变量1"的实例进行处理,用完了就GC掉,所以不会保持上次的值!
private static int j = 0;//j属于全局变量!
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
j++;
Label1.Text = j.ToString();
}
}
当为i时,不会自增,原因如上!如果是j,会自增,因为j是全局变量!
- GetSession:
public class SessionMggr
{
//new一个容器,将session的Id对应的所有数据放到这个容器里面!IDictionary<string, object>中string对应session的Id,object对应的是数据!
private static IDictionary<string, IDictionary<string, object>> data = new Dictionary<string, IDictionary<string, object>>();
public IDictionary<string, object> GetSession(string sessionId)
{//如果找的session,就将数据返回回去!
if (data.ContainsKey(sessionId))
{
return data[sessionId];
}
Else//如果找不到,就先new 一个容器,然后得到对应的数据,将之返回回去!
{
IDictionary<string,object>session=new Dictionary<string, object>();
data[sessionId] = session;
return data[sessionId];
}
}
}
- 验证码:
//在一般处理程序中要使用session就必须实现IRequiresSessionState接口!
public class YZM : IHttpHandler ,IRequiresSessionState{
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "image/JPEG";
//创建一个图片,指明图片的宽和高!
using(System.Drawing.Bitmap bitmap=new System.Drawing.Bitmap(250,50))
{
//创建图片的画布!
using(System.Drawing.Graphics g=System.Drawing.Graphics.FromImage(bitmap))
{
//在画布上画字!
//画布.DrawString(画的字,字体,刷子的颜色,相对画布的位置)
/* g.DrawString("腾讯",new System.Drawing.Font("宋体",30),System.Drawing.Brushes.DarkOliveGreen,new System.Drawing.PointF(0,0) );
* 定义一个画笔
System.Drawing.Pen pen = (System.Drawing.Pen)System.Drawing.Pens.Yellow.Clone();
* 设置画笔的宽度:
pen.Width = 3;
* 在画布上画圈··下面黄色标志为圆圈的宽度和高度!
g.DrawEllipse(System.Drawing.Pens.Yellow,new System.Drawing.Rectangle(10,10,10,10));
g.DrawEllipse(pen, new Rectangle(20, 10, 10, 10));
* 将图片写入到输出流中。
bitmap.Save (context.Response.OutputStream,System.Drawing.Imaging.ImageFormat.Jpeg);*/
//产生随机数:
Random random=new Random();
int code = random.Next();
string strCode = code.ToString();
//将随机数存入到数据库中
HttpContext.Current.Session["code"] = strCode;
//将随机数画入到画布中
g.DrawString(strCode, new System.Drawing.Font("宋体", 30), System.Drawing.Brushes.DarkOliveGreen, new System.Drawing.PointF(0, 0));
//将图片写入到文件流中,并定义图片格式:
bitmap.Save(context.Response.OutputStream,System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
}
public bool IsReusable {
get {
return false;
}
}
}
将服务器中的验证码与客户输入的验证码进行对照,然后判断是否正确:
protected void Button1_Click(object sender, EventArgs e)
{
string 正确的验证码 = Convert.ToString(Session["code"]);
if (正确的验证码 == TextBox1.Text)
{
Response.Write("正确!");
}
else
{
Response.Write("错误!");
}
}
- Http协议:
Web开发是和http协议打交道的,所以必须了解http协议。http协议版本有:Http/0.9、Http/1.0、Http/1.1,现在主流的是Http/1.1版本的!
Http协议分析工具:
- DebugBar,Http(S)标签的内容。免费的。只能分析当前浏览器中的内容。
- Httpwatch,收费的,只能分析当前浏览器中的内容。推荐使用。
- HttpAnalyzer,收费的,能分析计算机所有的Http请求数据。
Http协议的几个概念:
- 连接(connection):浏览器和服务器之间传输数据的通道。一般就是请求完毕就关闭,不会保持连接。
- 请求(Request):浏览器向服务器发送的"我要。。。"的消息,包含请求的类型、请求的数据、浏览器的信息(语言、浏览器版本等)。
- 响应(Response):服务器对浏览器请求的返回的数据,包含是否成功、错误码等。
- http请求报文:
用httpwatch查看访问一个网站(用DiscuzNT测试环境)的响应的情况。敲入一个网址后,浏览器向服务器发出请求。页面中的图片、js、css在单独的请求中。
上面标注的200是状态码,代表处理成功!
GET/HTTP/1.1表示向服务器用GET方式请求首页,使用 HTTP/1.1协议。
Accept-Encoding gzip ,deflate表示浏览器支持gzip. Deflate两种压缩方式算法。
Accept-Language zh-cn表示浏览器支持的语言,很多进入后自动就是中文界面的国际网站就是通过读取这个头实现的。
Connection :Keep-Alive。 一般情况下,一旦web服务器向浏览器发送的请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了Connection :Keep-Alive,则TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节省了网络宽带。
Cookie是浏览器向服务器发送和当前网站关联的Cookie,这样在服务器端也能读取浏览器端的Cookie了!
User-Agent为浏览器的版本信息。通过这个信息就可以读取浏览器是IE还是FireFox、支持的插件、Net版本等。
- http响应码:
浏览器向服务器发送请求,服务器处理可能是"成功"、可能是失败,可能没有权限访问等原因,服务器会通过响应码来告诉浏览器处理结果。
"200" :OK;
"301" :Moved Permanently 永久转移;
"302" :Found 暂时转移;
"307" :Temporary Redirect;
"400" :Bad Request 错误请求;
"401" :Unauthorized 未认证;
"403" :Forbidden 禁止;
"404" :Not Found 无找到;
"500" :Intelnal Server Error 服务器内部错误;
"503" :Service Unavailable . 一般是访问人数过多。
- web开发的一些基本原则:
最小权限原则。只允许用户做***,而不是"不允许用户做***";
浏览器查看的是服务端代码的执行输出的文本,除非服务器有漏洞,否则浏览者无法查看服务端的aspx、cs代码,目标另存为也是保存的aspx的执行结果,而看不到aspx的源代码。Js、html是被输出到浏览器上执行的,因此无法禁止浏览者查看js、html。
C#代码是运行在服务端的,js代码是运行在浏览器客户端的;
能到浏览端完成的事情,就不要到服务端去做。
- 原则:
按钮确认提交的实现在Button的OnClient中写(return confirm('真的要删除吗?'))。
<input type="submit" name="delete" value="删除" onclick="return confirm('真的要删除吗?')">
代码是运行在浏览器端的,和服务器端没有关系。
在服务端"弹出消息窗口"
Response.Write("<script type='text/javascript'>alert('你点了我!');</script>");
并不是真的是在服务器端运行的,只是生成了javascript代码到浏览器端,浏览器会在解析文档的时候运行alert,不推荐用这种写法,读懂即可,推荐用后面讲的,
对于服务器端的代码来说,生成一堆HTML代码就是一堆字符串,没有任何意义,只有到了浏览器端执行才有意义。
- 127.0.0.1是回环地址(loopback),就是访问本机。Localhost是127.0.0.1的别名。是无法在外部访问的。
0.0.0.0 就是任意IP (Any IP);
两种弹出对话框的方式:
protected void Button2_Click(object sender, EventArgs e)
{
//虽然代码是在服务器端,但是运行时,通过下面代码会将括号里面的字符串写入到浏览器端!
Response.Write("<script type='text/javascript'>alert('你点了我!');</script>");
}
protected void Button3_Click(object sender, EventArgs e)
{
//运行在服务器端的。
MessageBox.Show("多么简单呀,服务端弹出对话框!");
}
中木马:
因为这是在服务器端运行的代码,所以交给客户端,客户端不小心按了按钮4,木马就会在服务器端的D盘生成,对于用户来说,不起任何作用!
protected void Button4_Click(object sender, EventArgs e)
{
File.WriteAllText("D:/muma.exe","木马(){葵花点穴手();降龙十八掌();熊猫点香();}");
}
隐 藏 :【通过将Visible属性设置为falsek,就可以隐藏该控件】
//通过C#代码,在服务器端隐藏控件:
protected void Button6_Click(object sender, EventArgs e)
{
TextBox1.Visible = false;
}
//通过html,在客户端隐藏控件:
<input type="button" value="客户端隐藏" onclick="document.getElementById('TextBox1').style.display='none'">
- 客户端验证不能代替服务端验证
客户端校验:
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<asp:Button ID="Button1" runat="server" OnClientClick="var value=document.getElementById('TextBox1').value;if(parseInt(value,10)>100){alert('金额不能大于100');return false;
}"
//这句话的意思是:"通过该控件(TextBox),获得其值,然后将其值转化为十进制数,如果该数大于100,那么就提示用户【金额不能大于100】,然后返回false,通过返回值,就不用向服务器重新请求。直接通过javascript代码就能够达到提示的效果。"
OnClick="Button1_Click" Text="Button" />
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
对于<asp:Button来讲,onclick是服务端事件,OnClientClick是最终生成到客户端的浏览器中的onclick代码。
服务端校验:
protected void Button1_Click(object sender, EventArgs e)
{
if (Convert.ToInt32(TextBox1.Text) > 100)
{
Label1.Text = "由于金额大于100,所以禁止取款!";
}
else
{
Label1.Text = "取款成功,金额为:" + TextBox1.Text;
}
}
服务端校验是为了更好的客户端体验,服务端校验是为了最后一次把关,防止恶意请求。
为什么这么说呢?因为客户端有禁止javascript语言功能,所以万一用户禁止javascript脚本,那么就会取出大于100的金额!
- 服务器控件HyperLink1的Visible属性:
当Visible为false时,就是说将控件不在显示在浏览器端,并不是隐藏,换句话说,也就是在浏览器端的标签代码中是看不见的。
当Visible为true时,服务端就会重新响应,将控件画回到客户端。
protected void Button1_Click(object sender, EventArgs e)
{
if (TextBox1.Text =="111")
{
HyperLink1.Visible = true;
}
}
在客户端隐藏:
<input type="text" id="password"/><input type="button" value="确 定" onclick="var password = document.getElementById('password').value;if (password=='111') {
document.getElementById('div1').style.display ='';
这样做不安全。 控件虽然没有显示在用户面前,但它在标签中显示,有一点基础的人就会看得出来。
- XSS漏洞:
利用漏洞1:收集账户、密码;
利用漏洞2:送奖品的消息框;
上面的两个例子就是XSS(跨站脚本,Cross-site Scripting)
例1:
有这么一个页面:ShowMsg.aspx有一Lable标签,显示信息;
利用Response.Redirect("ShowMsg.aspx?Msg=不能为空");函数显示ShowMsg.aspx?Msg=不能为空内容;
函数Redirect可以自动将中文字符转化为浏览器能识别的编码。然后展现给客户端。页面ShowMsg.aspx就是显示Msg的值。
页面ShowMsg.aspx的后台代码如下:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
Label1.Text = Request["Msg"];
}
}
ASP.Net有自动将蛀虫漏洞进行捕捉,所以在进行测试的时候,应该将其关闭:
只要将<%@ Page Language="C#" ValidateRequest="false" AutoEventWireup="true" CodeFile="ShowMsg.aspx.cs" Inherits="XSS_ShowMsg" %>
中标志的属性设置为false就可以了!
例2:
通过Button1,将用户写入在TextBox1.Text中的信息添加写入到D:/1.txt文件里面,
protected void Button1_Click(object sender, EventArgs e)
{
File.AppendAllText("D:/1.txt",TextBox1.Text+"<br/>");
}
然后通过展示页面,将文件内容写入到客户端浏览器中;这样考虑到用户可能将一些javascript代码写入到文件中,那么用户写入的代码就会被浏览器端执行,从而造成不安全因素。由于aspx自动会禁止客户端写入不合法的信息,所以只有我们将ValidateRequest的属性设置为false,就可以实现这种作用。但一般不会有这种需求。往往会有这样一种需求,想将用户输入的代码显示到显示屏中,这样我们就不能用这种方法去做。
protected void Page_Load(object sender, EventArgs e)
{
Response.Write(File.ReadAllText("D:/1.txt"));
}
如下可以实现上面的需求:
protected void Page_Load(object sender, EventArgs e)
{
string s = File.ReadAllText("D:/1.txt");
Response.Write(HttpUtility.HtmlEncode(s));
}
通过上面标志的代码,就可以将用户提交的代码,原原本本的显示在浏览器中。并且代码对浏览器不起任何作用。
HttpUtility.HtmlEncode(s)是将字符串【s】中的<、>等特殊字符转换为【<>】转义符。