همان طور که ممکن است بدانید، اکشن متدها در کنترلرهای MVC می*توانند انواع مختلفی را برگشت دهند که شرح آن در مطالب این سایت به مفصل گذشته است. یکی از این انواع، نوع ActionResult می*باشد. این یک کلاس پایه برای انواع برگشتی توسط اکشن متدها مثل JsonResult، FileResult می*باشد. اما ممکن است مواقعی پیش بیاید که بخواهید نوعی را توسط یک اکشن متد برگشت دهید که به صورت توکار تعریف نشده باشد. مثلا زمانی را در نظر بگیرید که بخواهید یک تصویر امنیتی را برگشت دهید. یکی از راه حل*های ممکن به این صورت است که کلاسی ایجاد شود که از کلاس پایه ActionResult ارث بری کرده باشد. بدین صورت:

using System;
using System.Web.Mvc;

namespace MVCPersianCaptcha.Models
{
public class CaptchaImageResult : ActionResult
{
public override void ExecuteResult(ControllerContext context)
{
throw new NotImplementedException();
}
}
}


همان طور که مشاهده می*کنید، کلاسی به اسم CaptchaImageResult تعریف شده که از کلاس ActionResult ارث بری کرده است. در این صورت باید متد ExecuteResult را override کنید. متد ExecuteResult به صورت خودکار هنگامی که از CaptchaImageResult به عنوان یک نوع برگشتی اکشن متد استفاده شود اجرا می*شود. به همین خاطر باید تصویر امنیتی توسط این متد تولید شود و به صورت جریان (stream) برگشت داده شود

کدهای اولیه برای ایجاد یک تصویر امنیتی به صورت خیلی ساده از کلاس*های فراهم شده توسط +GDI ، که در دات نت فریمورک وجود دارند استفاده خواهند کرد. برای این کار ابتدا یک شیء از کلاس Bitmap با دستور زیر ایجاد خواهیم کرد:

Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);


پارامترهای اول و دوم به ترتبی عرض و ارتفاع تصویر امنیتی را مشخص خواهند کرد و پارامتر سوم نیز فرمت تصویر را بیان کرده است. Format32bppArgb یعنی یک تصویر که هر کدام از پیکسل*های آن 32 بیت فضا اشغال خواهند کرد ، 8 بیت اول میزان آلفا، 8 بیت دوم میزان رنگ قرمز، 8 بیت سوم میزان رنگ سبز، و 8 تای آخر نیز میزان رنگ آبی را مشخص خواهند کرد

سپس شیئی از نوع Graphics برای انجام عملیات ترسیم نوشته*های فارسی روی شیء bitmap ساخته می*شود:
Graphics gfxCaptchaImage = Graphics.FromImage(bitmap);


خصوصیات مورد نیاز ما از gfxCaptchaImage را به صورت زیر مقداردهی می*کنیم:

gfxCaptchaImage.PageUnit = GraphicsUnit.Pixel;
gfxCaptchaImage.SmoothingMode = SmoothingMode.HighQuality;
gfxCaptchaImage.Clear(Color.White);


واحد اندازه گیری به پیکسل، کیفیت تصویر تولید شده توسط دو دستور اول، و در دستور سوم ناحیه ترسیم با یک رنگ سفید پاک می*شود.

سپس یک عدد اتفاقی بین 1000 و 9999 با دستور زیر تولید می*شود:

int salt = CaptchaHelpers.CreateSalt();


متد CreateSalt در کلاس CaptchaHelpers قرار گرفته است، و نحوه پیاده سازی آن بدین صورت است:

public int CreateSalt()
{
Random random = new Random();
return random.Next(1000, 9999);
}


سپس مقدار موجود در salt را برای مقایسه با مقداری که کاربر وارد کرده است در session قرار می*دهیم:

HttpContext.Current.Session["captchastring"] = salt;


سپس عدد اتفاقی تولید شده باید تبدیل به حروف شود، مثلا اگر عدد 4524 توسط متد CreateSalt تولید شده باشد، رشته "چهار هزار و پانصد و بیست و چهار" معادل آن نیز باید تولید شود. برای تبدیل عدد به حروف، ما نیز از همین کلاس استفاده خواهیم کرد:
string randomString = (salt).NumberToText(Language.Persian);


در دستور بالا، متد الحاقی NumberToText با پارامتر Language.Persian وظیفه تبدیل عدد salt را به حروف فارسی معادل خواهد داشت.

به صورت پیش فرض نوشته*های تصویر امنیتی به صورت چپ چین نوشته خواهند شد، و با توجه به این که نوشته ای که باید در تصویر امنیتی قرار بگیرد فارسی است، پس بهتر است آنرا به صورت راست به چپ در تصویر بنویسیم، بدین صورت:

var format = new StringFormat();
int faLCID = new System.Globalization.CultureInfo("fa-IR").LCID;
format.SetDigitSubstitution(faLCID, StringDigitSubstitute.National);
format.Alignment = StringAlignment.Near;
format.LineAlignment = StringAlignment.Near;
format.FormatFlags = StringFormatFlags.DirectionRightToLeft;


و همچنین نوع و اندازه فونت که در این مثال tahoma می*باشد:

Font font = new Font("Tahoma", 10);


خوب نوشته فارسی اتفاقی تولید شده آماده ترسیم شدن است، اما اگر چنین تصویری تولید شود احتمال خوانده شدن آن توسط روبات*های پردازش گر تصویر شاید زیاد سخت نباشد. به همین دلیل باید کاری کنیم تا خواندن این تصویر برای این روبات*ها سخت*تر شود، روش*های مختلفی برای این کار وجود دارند: مثل ایجاد نویز در تصویر امنیتی یا استفاده از توابع ریاضی سینوسی و کسینوسی برای نوشتن نوشته*ها به صورت موج. برای این کار اول یک مسیر گرافیکی در تصویر یا موج اتفاقی ساخته شود و به شیء gfxCaptchaImage نسبت داده شود. برای این کار اول نمونه ای از روی کلاس GraphicsPath ساخته می*شود،

GraphicsPath path = new GraphicsPath();


و با استفاده از متد AddString ، رشته اتفاقی تولید شده را با فونت مشخص شده، و تنظیمات اندازه دربرگیرنده رشته مورد نظرر، و تنظیمات فرمت بندی رشته را لحاظ خواهیم کرد.




path.AddString(randomString,
font.FontFamily,
(int)font.Style,
(gfxCaptchaImage.DpiY * font.SizeInPoints / 72),
new Rectangle(0, 0, width, height), format);

با خط کد زیر شیء path را با رنگ بنقش با استفاده از شیء gfxCaptchaImage روی تصویر bitmap ترسیم خواهیم کرد:


gfxCaptchaImage.DrawPath(Pens.Navy, path);

برای ایجاد یک منحنی و موج از کدهای زیر استفاده خواهیم کرد:


//-- using a sin ware distort the image
int distortion = random.Next(-10, 10);
using (Bitmap copy = (Bitmap)bitmap.Clone())
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int newX = (int)(x + (distortion * Math.Sin(Math.PI * y / 64.0)));
int newY = (int)(y + (distortion * Math.Cos(Math.PI * x / 64.0)));
if (newX < 0 || newX >= width) newX = 0;
if (newY < 0 || newY >= height) newY = 0;
bitmap.SetPixel(x, y, copy.GetPixel(newX, newY));
}
}
}

موقع ترسیم تصویر امنیتی است:


//-- Draw the graphic to the bitmap
gfxCaptchaImage.DrawImage(bitmap, new Point(0, 0));

gfxCaptchaImage.Flush();

تصویر امنیتی به صورت یک تصویر با فرمت jpg به صورت جریان (stream) به مرورگر باید فرستاده شوند:

HttpResponseBase response = context.HttpContext.Response;
response.ContentType = "image/jpeg";
bitmap.Save(response.OutputStream, ImageFormat.Jpeg);

و در نهایت حافظه*های اشغال شده توسط اشیاء فونت و گرافیک و تصویر امنیتی آزاد خواهند شد:

// Clean up.
font.Dispose();
gfxCaptchaImage.Dispose();
bitmap.Dispose();
برای استفاده از این کدها، اکشن متدی نوشته می*شود که نوع CaptchaImageResult را برگشت می*دهد:


public CaptchaImageResult CaptchaImage()
{
return new CaptchaImageResult();
}

اگر در یک View خصیصه src یک تصویر به آدرس این اکشن متد مقداردهی شود، آنگاه تصویر امنیتی تولید شده نمایش پیدا می*کند:


<img src="@Url.Action("CaptchaImage")"/>

بعد از پست کردن فرم مقدار text box تصویر امنیتی خوانده شده و با مقدار موجود در session مقایسه می*شود، در صورتی که یکسان باشند، کاربر می*تواند وارد سایت شود (در صورتی که نام کاربری یا کلمه عبور خود را درست وارد کرده باشد) یا اگر از این captcha در صفحات دیگری استفاده شود عمل مورد نظر می*تواند انجام شود. در مثال زیر به طور ساده اگر کاربر در کادر متن مربوط به تصویر امنیتی مقدار درستی را وارد کرده باشد یا نه، پیغامی به او نشان داده می*شود.


[HttpPost]
public ActionResult Index(LogOnModel model)
{
if (!ModelState.IsValid) return View(model);

if (model.CaptchaInputText == Session["captchastring"].ToString())
TempData["message"] = "تصویر امنتی را صحیح وارد کرده اید";
else
TempData["message"] = "تصویر امنیتی را اشتباه وارد کرده اید";

return View();
}