Web开发之ASP.Net:(4)一般处理程序(.ashx)
1一般处理程序(HttpHandler)
1.1 关于IHttpHandler 接口
实现该接口需要为接口的ProcessRequest(HttpContext context)方法和IsReusable 属性提供实现。
namespace System.Web
{
// 摘要: 定义 ASP.NET 为使用自定义 HTTP 处理程序同步处理 HTTP Web 请求而实现的协定。
public interface IHttpHandler
{
// 摘要:获取一个值,该值指示其他请求是否可以使用 System.Web.IHttpHandler 实例。
// 返回结果:如果 System.Web.IHttpHandler 实例可再次使用,则为 true;否则为 false。
bool IsReusable { get; }
// 摘要:通过实现 System.Web.IHttpHandler 接口的自定义 HttpHandler 启用 HTTP Web 请求的处理。
// 参数:context:System.Web.HttpContext 对象
// 提供对内部服务器对象(如 Request、Response、Session 和 Server)的引用,为 HTTP 请求提供服务。
void ProcessRequest(HttpContext context);
}
}
相册增删改查代码略。
1.2 一个简单的一般处理程序(ashx)
(1)注意context.Response.ContentType要正确,比如用"text/plain",在有些浏览器中不会用html来解析
(2)注意IHttpHandler在System.Web下,与2.2中namespace Server下自定义的interface IHttpHandler不同
using System;
using System.Web;
using System.Text;
public class FirstHandler : IHttpHandler {
StringBuilder sb = new StringBuilder();
public void ProcessRequest (HttpContext context) {
//context.Response.ContentType = "text/plain";//如果用"text/plain",在有些浏览器中不会用html解析,因此改成"text/html"。
context.Response.ContentType = "text/html";
sb.AppendLine("<html>");
sb.AppendLine("<head>");
sb.AppendLine("</head>");
sb.AppendLine("<body>");
sb.AppendLine("<h3>");
sb.AppendLine("Girl");
sb.AppendLine("</h3>");
sb.AppendLine("<img src ='./pic/03.jpg'>");
sb.AppendLine("</body>");
sb.AppendLine("</html>");
string StringHtml = sb.ToString();
context.Response.Write(StringHtml);
}
public bool IsReusable {
get {
return false;
}
}
}
1.3 HttpHandler处理Get请求
1.3.1 Get请求的提交(html)
url传参 通过window.location.href=""指定要请求的页面 通过单击事件触发提交请求
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<a href="3Get.ashx?name=tom&gender=femail">提交</a>
</body>
</html>
1.3.2 HttpHandler处理get请求(*.ashx)
context.Request.QueryString[]获取url参数
context.Response.Write进行响应
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/html";
string name= context.Request.QueryString["name"];
string gender= context.Request.QueryString["gender"];
context.Response.Write(name ":" gender);
}
1.4 HttpHandler处理Post请求
1.4.1 Post请求的提交 (html )
//通过表单元素的value传值 通过action=""指定要请求的页面 通过submit提交
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<form action="4Post.ashx" method="post">
<input type="text" name="uid" value="" />
<input type="text" name="pwd" value="" />
<input type = "submit" value = "提交" />
</form>
</body>
</html>
1.4.2 HttpHandler处理Post请求(*.ashx)
context.Request.Form[]获取表单元素的值,context.Response.Write进行响应
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/html";
string uid = context.Request.Form["uid"];
string pwd = context.Request.Form["pwd"];
if (uid == "admin" && pwd == "admin")
{
context.Response.Write("登录成功!");
}
else
{
context.Response.Write("用户名或密码错误,登录失败!");
}
}
1.5 HttpHandler应用示例:ashx html模板
1.5.1 实现登录页面
登录失败后自动填充用户名和密码,以免用户全部重新输入
将replace替换过的html页面(字符串)写回浏览器
//html模板 注意隐藏域的使用,用来方便ashx区别当次请求是get请求还是post请求
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<form action="5Login.ashx" method="post">
<input type="hidden" name="viewstate" value ="a" />
<input type="text" name="uid" value="@name" />
<input type="text" name="pwd" value="@pwd" />
<input type = "submit" value = "提交" />
</form>
</body>
</html>
//ashx 处理要发回浏览器的html字符串 用replace替换
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/html";
string path = context.Request.MapPath("5Login.htm");
string html = System.IO.File.ReadAllText(path);
if (!string.IsNullOrEmpty(context.Request.Form["viewstate"]))
{
string uid = context.Request.Form["uid"];
string pwd = context.Request.Form["pwd"];
if (uid == "admin" && pwd == "admin")
{
context.Response.Write("登录成功!");
context.Response.Redirect("home.html");//登录成功,跳转到主页
}
else
{
context.Response.Write("用户名或密码错误,登录失败!");
}
html = html.Replace("@name", uid).Replace("@pwd", pwd);//做字符串替换
}//post请求
else
{
html = html.Replace("@name", "").Replace("@pwd", "");//做字符串替换
}//get请求
context.Response.Write(html);
}
1.5.2 数字自增功能
换了浏览器后,只把该浏览器页面文本框的值提交给服务器
用隐藏域获取它 不为空就是post过来的 则执行
//--div和隐藏域使用同一个代替换的字符串:@value
<body>
<form action="6Add.ashx" method="post">
<input type="hidden" name="viewstate" value ="aa" />
<input type="hidden" name="num" value ="@value" />
<input type = "submit" value = "提交" />
</form>
<div>@value</div>
</body>
1.5.3实现加法计算器
ashx 关键代码:
if (!string.IsNullOrEmpty(context.Request.Form["viewstate"]))
{
string num1 = context.Request.Form["num1"];
string num2 = context.Request.Form["num2"];
int res = int.Parse(num1) int.Parse(num2);
html = html.Replace("@num1", num1).Replace("@num2", num2).Replace("@res", res.ToString());
}
else
{
html = html.Replace("@num1", "").Replace("@num2", "").Replace("@res", "");
}
context.Response.Write(html);
2 一般处理程序应用
2.1 文件上传
Upload.htm
//注意上传文件用post请求
//注意设置enctype ="multipart/form-data" 设置随机的分隔符
<body>
<form action="1Upload.ashx" method="post" enctype = "multipart/form-data">
<input type="file" name="pic" value="选择" />
<input type="submit" value="Upload" />
</form>
</body>
Upload.ashx
//用context.Request.Files[0]接收文件数据,得到HttpPostedFile类别的数据
//HttpPostedFile类别的file 有file.FileName属性和file.SaveAs(path)方法
public class Upload : IHttpHandler {
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/plain";
HttpPostedFile file = context.Request.Files[0];
if (!string.IsNullOrEmpty(file.FileName))
{
string fileName = file.FileName;
string ext = System.IO.Path.GetExtension(fileName);
Random rd = new Random();
string name = DateTime.Now.ToString("yyyyMMddhhmmss") rd.Next(10000, 100000) ext;
string path = context.Request.MapPath("file/" name);
file.SaveAs(path);
context.Response.Write("上传成功");
}
}
}
2.2 文件下载(下载html/txt/jpeg文件)
如果HttpHandler输出的是html/txt/jpeg等类型的信息,那么浏览器会直接显示。只有*.exe *.avi等这些浏览器处理不了的文件,浏览器才会去下载。
对于html/txt/jpeg等类型的文件,如果希望弹出保存对话框,则需要添加Header。
然后再用 context.Response.WriteFile(path);即可实现下载。
attachment表示这是一个附件。
其中filename后为编码后的文件名,filename段为建议的保存文件名。
string name = HttpUtility.UrlEncode("临时.jpg");
context.Response.AddHeader("Content-Disposition", "attachment;filename =" name);
context.Response.WriteFile("img/pig.jpg");
2.3 绘制输出缩略图(由浏览器显示)
//注意bitmp.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg)
public class GetSmallImg : IHttpHandler {
public void ProcessRequest (HttpContext context) {
//context.Response.ContentType = "text/plain";
context.Response.ContentType = "image/jpeg";
string path =context.Request["path"];
if (!string.IsNullOrEmpty(path))
{
string path = context.Request.MapPath(path );
//准备原图
using (System.Drawing.Image img = Image.FromFile(path))
{
//准备缩略图参数
int smallWidth = 150; //小图宽度最大值
int smallHeight = 100; //小图高度最大值
if (img.Width>img.Height)
{
smallHeight =img.Height*smallWidth/img.Width;
}//如果原图是宽>高 保留小图的宽为150
else
{
smallWidth =img.Width*smallHeight/img.Height;
}//如果原图是宽<高 保留小图的高为100
//准备缩略图画板
using (Bitmap bitmp = new Bitmap(smallWidth,smallHeight))
{
//准备画笔
using(Graphics g = Graphics.FromImage(bitmp))
{
//将img按bitmp.Width,bitmp.Height的大小画到bitmp上(0,0)坐标处
//准备绘制
g.DrawImage(img,0,0,bitmp.Width,bitmp.Height);
}
bitmp.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
}
}
}
2.4 验证码图片
<%@ WebHandler Language="C#" Class="ValidateCode" %>
using System;
using System.Web;
using System.Drawing;
using System.Web.SessionState;
public class ValidateCode : IHttpHandler, IRequiresSessionState
{
/// <summary>
/// 绘制印证码图片输出
/// </summary>
/// <param name="context"></param>
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/plain";
VerifyCodeImage verifyCodeImage = new VerifyCodeImage();
// 取随机码
string code = verifyCodeImage.CreateVerifyCode().ToUpper();
//context.Session["code"] = code;
context.Session.Add("code", code);
// 输出图片
Image img = verifyCodeImage.CreateImage(code, 3);
img.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
}
public class VerifyCodeImage
{
public VerifyCodeImage()
{
}
#region 验证码长度(默认4个验证码的长度)
int length = 4;
public int Length
{
get { return length; }
set { length = value; }
}
#endregion
#region 验证码字体大小(为了显示扭曲效果,默认40像素,可以自行修改)
int fontSize = 20;
public int FontSize
{
get { return fontSize; }
set { fontSize = value; }
}
#endregion
#region 边框补(默认1像素)
int padding = 2;
public int Padding
{
get { return padding; }
set { padding = value; }
}
#endregion
#region 是否输出燥点(默认不输出)
bool chaos = true;
public bool Chaos
{
get { return chaos; }
set { chaos = value; }
}
#endregion
#region 输出燥点的颜色(默认灰色)
Color chaosColor = Color.LightGray;
public Color ChaosColor
{
get { return chaosColor; }
set { chaosColor = value; }
}
#endregion
#region 自定义背景色(默认白色)
Color backgroundColor = Color.White;
public Color BackgroundColor
{
get { return backgroundColor; }
set { backgroundColor = value; }
}
#endregion
#region 自定义随机颜色数组
Color[] colors = { Color.Black, Color.Red, Color.DarkBlue, Color.Green, Color.Orange, Color.Brown, Color.DarkCyan, Color.Purple };
public Color[] Colors
{
get { return colors; }
set { colors = value; }
}
#endregion
#region 自定义字体数组
string[] fonts = { "Arial", "Georgia" };
public string[] Fonts
{
get { return fonts; }
set { fonts = value; }
}
#endregion
#region 自定义随机码字符串序列(使用逗号分隔)
// 去除 0,1,i,l,o,I,L,O
string codeSerial = "2,3,4,5,6,7,8,9,a,b,c,d,e,f,g,h,j,k,m,n,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,J,K,M,N,P,Q,R,S,T,U,V,W,X,Y,Z";
public string CodeSerial
{
get { return codeSerial; }
set { codeSerial = value; }
}
#endregion
#region 产生波形滤镜效果
//private const double PI = 3.1415926535897932384626433832795;
private const double PI2 = 6.283185307179586476925286766559;
/// <summary>
/// 正弦曲线Wave扭曲图片(Edit By 51aspx.com)
/// </summary>
/// <param name="srcBmp">图片路径</param>
/// <param name="bXDir">如果扭曲则选择为True</param>
/// <param name="nMultValue">波形的幅度倍数,越大扭曲的程度越高,一般为3</param>
/// <param name="dPhase">波形的起始相位,取值区间[0-2*PI)</param>
/// <returns></returns>
public System.Drawing.Bitmap TwistImage(Bitmap srcBmp, bool bXDir, double dMultValue, double dPhase)
{
System.Drawing.Bitmap bitmap = new Bitmap(srcBmp.Width, srcBmp.Height);
// 将位图背景填充为白色
System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(bitmap);
graphics.FillRectangle(new SolidBrush(System.Drawing.Color.White), 0, 0, bitmap.Width, bitmap.Height);
graphics.Dispose();
double dBaseAxisLen = bXDir ? (double)bitmap.Height : (double)bitmap.Width;
for (int i = 0; i < bitmap.Width; i )
{
for (int j = 0; j < bitmap.Height; j )
{
double dx = 0;
dx = bXDir ? (PI2 * (double)j) / dBaseAxisLen : (PI2 * (double)i) / dBaseAxisLen;
dx = dPhase;
double dy = Math.Sin(dx);
// 取得当前点的颜色
int nOldX = 0, nOldY = 0;
nOldX = bXDir ? i (int)(dy * dMultValue) : i;
nOldY = bXDir ? j : j (int)(dy * dMultValue);
System.Drawing.Color color = srcBmp.GetPixel(i, j);
if (nOldX >= 0 && nOldX < bitmap.Width
&& nOldY >= 0 && nOldY < bitmap.Height)
{
bitmap.SetPixel(nOldX, nOldY, color);
}
}
}
return bitmap;
}
#endregion
#region 生成校验码图片
public Bitmap CreateImage(string code, double multValue)
{
int fSize = FontSize;
int fWidth = fSize Padding;
int imageWidth = (int)(code.Length * fWidth) 4 Padding * 2;
int imageHeight = fSize * 2 Padding;
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(imageWidth, imageHeight);
Graphics graphics = Graphics.FromImage(bitmap);
graphics.Clear(BackgroundColor);
Random rand = new Random();
// 给背景添加随机生成的燥点
if (this.Chaos)
{
Pen pen = new Pen(ChaosColor, 0);
int c = Length * 10;
for (int i = 0; i < c; i )
{
int x = rand.Next(bitmap.Width);
int y = rand.Next(bitmap.Height);
graphics.DrawRectangle(pen, x, y, 1, 1);
}
}
int left = 0, top = 0, top1 = 1, top2 = 1;
int n1 = (imageHeight - FontSize - Padding * 2);
int n2 = n1 / 4;
top1 = n2;
top2 = n2 * 2;
Font font;
Brush brush;
int cindex, findex;
// 随机字体和颜色的验证码字符
for (int i = 0; i < code.Length; i )
{
cindex = rand.Next(Colors.Length - 1);
findex = rand.Next(Fonts.Length - 1);
font = new System.Drawing.Font(Fonts[findex], fSize, System.Drawing.FontStyle.Bold);
brush = new System.Drawing.SolidBrush(Colors[cindex]);
if (i % 2 == 1)
{
top = top2;
}
else
{
top = top1;
}
left = i * fWidth;
graphics.DrawString(code.Substring(i, 1), font, brush, left, top);
}
// 画一个边框 边框颜色为Color.Gainsboro
graphics.DrawRectangle(new Pen(Color.Gainsboro, 0), 0, 0, bitmap.Width - 1, bitmap.Height - 1);
graphics.Dispose();
// 产生波形
bitmap = TwistImage(bitmap, true, multValue, 4);
return bitmap;
}
#endregion
#region 生成随机字符码
public string CreateVerifyCode(int codeLength)
{
if (codeLength == 0)
{
codeLength = Length;
}
string[] arr = CodeSerial.Split(',');
string code = "";
int randValue = -1;
Random random = new Random(unchecked((int)DateTime.Now.Ticks));
for (int i = 0; i < codeLength; i )
{
randValue = random.Next(0, arr.Length - 1);
code = arr[randValue];
}
return code;
}
public string CreateVerifyCode()
{
return CreateVerifyCode(0);
}
#endregion
#region 将创建好的图片输出到页面
/// <summary>
/// 将创建好的图片输出到页面
/// </summary>
/// <param name="code">验证码</param>
/// <param name="multValue">扭曲度(越大越扭曲)</param>
/// <param name="httpContext">上下文</param>
public void CreateImageOnPage(string code, double multValue, HttpContext httpContext)
{
System.IO.MemoryStream memoryStream = new System.IO.MemoryStream();
Bitmap bitmap = this.CreateImage(code, multValue);
bitmap.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Jpeg);
httpContext.Response.ClearContent();
httpContext.Response.ContentType = "image/Jpeg";
httpContext.Response.BinaryWrite(memoryStream.GetBuffer());
memoryStream.Close();
memoryStream = null;
bitmap.Dispose();
bitmap = null;
}
#endregion
}
public bool IsReusable {
get {
return false;
}
}
}
3 请求处理流程(一般处理程序)
3.1一般处理程序请求处理过程
(.ashx)请求--> iis (映射表中找到)-->aspnet_isapi.dll-->开启了工作者进程w3wp.exe
--> 调用HttpRuntime的静态方法ProcessRequest (传入了参数HttpWorkerRequest wr)
--> HttpRuntime .ProcessRequest根据wr创建了Httpcontext 的对象context (context 内部根据wr创建HttpRequest对象和HttpResponse对象,做为context 自己的属性)
-->HttpRuntime .ProcessRequest创建了HttpApplication对象 application,并调用了application. ProcessRequest(传入了参数Httpcontext context)注:HttpApplication类实现了IHttpHandler
--> application. ProcessRequest方法内部通过反射创建一般处理程序类的对象
-->执行页面类对象的ProcessRequest方法-->在该方法中通过参数context的Request获取请求信息;编写业务代码,处理请求,生成响应的数据;通过参数context的Response属性向浏览器发送响应报文。
3.2 ProcessRequest方法内部实现
创建了httpcontext对象(请求上下文)
创建了httpapplication
在httpapplication的第8个事件创建了一般处理程序的对象
在第11和12个事件之间执行了对象(一般处理程序)的ProcessRequest方法
//HttpWorkerRequest—就是更底层的请求上下文
HttpWorkerRequest workerRequest
//传入参数workerRequest,调用Httpruntime的静态方法ProcessRequest
HttpRuntime.ProcessRequest(HttpWorkerRequest workerRequest);
//HttpRuntime.ProcessRequest内部创建Httpcontext context –封装成高层的请求上下文,用起来更方便 (内部根据wr创建HttpRequest对象和HttpResponse对象)
context = new HttpContext(workerRequest, false);
//HttpRuntime.ProcessRequest内部创建HttpApplication applicationInstance实例,并调用了applicationInstance对象的ProcessRequest方法
IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context);
//applicationInstance.ProcessRequest方法内部通过反射创建了一般处理程序类的对象(实现IHttpHandler的类的实例)
applicationInstance.ProcessRequest(context);
//一般处理程序类的对象对请求进行处理和响应。
public class Upload : IHttpHandler{
public void ProcessRequest (HttpContext context) {
//Code To Process and Response the Request
}
}
4 ashx html版相册增删改查
**常见错误汇总
(1)错误:导致图片上传不成功,监视
hidden=hd&PTitle=wewqe&PUrl=7922c9c4c121e68a8226aca0.bmp&PDes=wqeqw
解决:设置enctype ="multipart/form-data"
<form action ="01Add.ashx" method="post" enctype ="multipart/form-data">
(2)参数值为int时,context.Request.QueryString[]获取不到数据
href ='02Delete.ashx?pid =" p[i].Pid "'
string pidstr= context.Request.QueryString["pid"];
解决:提交前将参数的值转化为字符串
href ='02Delete.ashx?pid =" p[i].Pid.ToString() "'
(3)错误:参数传递 注意不要轻易加空格
string pidstr= context.Request.QueryString["pid"];
(A)等号前后有空格:pid = " p[i].Pid.ToString() " 得到pidstr =null
(B) 等号后有空格:不正确:pid= " p[i].Pid.ToString() " 得到pidstr =” 10”
标准:等号前后无空格pid=" p[i].Pid.ToString() " pidstr =” 13”
监视:GET /MyPhotos/02Delete.ashx?pid=13 HTTP/1.1
(4)原图不存在时,处理显示缩略图问题
catch (Exception)
{
//设置画板初始大小
int imgWidth = 150;
int imgHeight = 100;
//准备画板
using (Bitmap bitmap = new Bitmap(imgWidth, imgHeight))
{
//准备画笔绘制
using (Graphics g = Graphics.FromImage(bitmap))//Bitmap继承自Image,所以可以隐式转换赋值
{
g.DrawString("图片不存在!",new Font("楷体",12),Brushes.Red,20,10);//将原图以新的size绘制到画板上
}
//保存输出小图
bitmap.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
(5)删除图片报错,是因为缩略图处理的页面没用using,一直占用文件,没有释放!!!
using (Image img = Image.FromFile(imgPath))//此处不用using会在程序运行过程中一直占用图片文件
(6)图片请求处理:正确路径(相对于图片所在目录,web.App中已经配置好)
因为图片是单独请求的,请求的文件就是图片本身
path = context.Request.MapPath(path);
(错误:path = context.Request.MapPath(“images/” path))
string path = context.Request.RawUrl;
注:相册代码略。