Web开发之ASP.Net:(4)一般处理程序(.ashx)

作者:陆金龙    发表时间:2016-08-21 14:38   


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;

 

注:相册代码略。