مطالب
استفاده از Re-Captcha
در اینجا استفاده از re-CAPTCHA برای ASP.Net و در اینجا برای ASP.Net MVC با استفاده از سرویس گوگل نسخه 1 آن آشنا شدید. در این مقاله می‌خواهیم توضیحاتی را در مورد دلیل استفاده و نحوه‌ی ثبت re-CAPTCHA نسخه 2 برای تکنولوژی‌های ASP.Net و ASP.Net MVC ارائه کنیم.

  reCAPTCHA چیست؟

استفاده آسان و امنیت بالا، جمله‌ای می‌باشد که گوگل در سرتیتر تعریف آن جای داده که البته عنوان «من روبات نیستم» در سرویس استفاده شده‌است. reCAPTCHA یک سرویس رایگان برای وب سایت‌های شما در جهت حفظ آن در برابر روبات‌های مخرب است و از موتور تجزیه و تحلیل پیشرفته‌ی تشخیص انسان در برابر روبات‌ها استفاده می‌نماید. reCAPTCHA را میتوان به صورت ماژول در بلاگ و یا فرم‌های ثبت نام و ... جای داد که فقط با یک کلیک هویت سنجی انجام خواهد شد. گاها ممکن است بجای کلیک از شما سوالی پرسیده شود که در این صورت می‌بایستی تصاویر مرتبط با آن سوال را تیک زده باشید.



دلیل استفاده از reCAPTCHA:

  1. گزارش روزانه از وضعیت موفقیت آمیز بودن هویت سنجی
  2. سهولت استفاده برای کاربران
  3. سهولت استفاده جهت برنامه نویسان
  4. دسترسی پذیری مناسب بدلیل وجود سؤالات تصویری و تلفظ و پخش عبارت بصورت صوتی
  5. امنیت بالا 

آیا می‌توان قالب reCAPTCHA را تغییر داد؟

جواب این سوال بله می‌باشد. این سرویس در دو قالب سفید و مشکی ارائه شده‌است که به صورت پیش فرض قالب سفید آن انتخاب می‌شود. در تصویر زیر قالب‌های این سرویس را مشاهده خواهید کرد.



زبان‌های پشتیبانی شده در این سرویس:


اضافه نمودن reCAPTCHA به سایت:

اگر قبلا در گوگل ثبت نام نموده‌اید کافیست وارد این سایت شوید و بر روی Get reCAPTCHA کلیک نمائید؛ در غیر اینصورت می‌بایستی یک حساب کاربری ایجاد نماید. بعد از ورود، به کنترل پنل هدایت خواهید شد. در نمای اول به تصویر زیر برخورد خواهید کرد که از شما ثبت سایت جدید را خواستار است:



نام، دامنه سایت و مالک را وارد و ثبت نام نماید.

پس از آنکه بر روی دکمه‌ی ثبت نام کلیک نمودید، برای شما دو کلید جدید را ثبت می‌نماید که منحصر به سایت شماست. Site Key رشته ای را داراست که در کد‌های HTML قرار خواهد گرفت و کلید بعدی Secret Key می‌باشد. ارتباط سایت شما با گوگل می‌بایستی به صورت محرمانه محفوظ بماند.


گام‌های لازم جهت نمایش سرویس در سایت:

  1. دستورات سمت کاربر
  2. دستورات سمت سرور 

دستورات سمت کاربر:

کد زیر را در قبل از بسته شدن تک <head/> قرار دهید:

<script src='https://www.google.com/recaptcha/api.js'></script>
کد زیر را در داخل تگ فرمی که می‌خواهید کپچا نمایش داده شود قرار دهید:
<div data-sitekey="6LdHGgwTAAAAAClKFhGthRrjBXh5AUGd4eWNCQq7"></div>

نکته: مقدار data-sitekey برابر است با رشته Site Key که گوگل برای شما ثبت نمود.



دستورات سمت سرور:

وقتی کاربر فرم حاوی کپچا را که به صورت صحیح هویت سنجی آن انجام شده باشد به سمت سرور ارسال کند، به عنوان بخشی از داده‌ی ارسال شده، یک رشته با نام g-recaptcha-response  با دستور Request دریافت خواهید کرد که به منظور بررسی اینکه آیا گوگل تایید کرده است که کاربر، یک درخواست POST ارسال نمود‌است. با این پارامترها یک مقدار json برگشت داده خواهد شد که می‌بایستی کلاسی متناظر با آن جهت خواندن ساخته شود.

با استفاده از کد زیر مقدار برگشتی Json را در کلاس مپ می‌نمائیم:
using System.Collections.Generic;
using Newtonsoft.Json;

namespace BaseConfig.Security.Captcha
{
    public class RepaptchaResponse
    {
        [JsonProperty("success")]
        public bool Success { get; set; }

        [JsonProperty("error-codes")]
        public List<string> ErrorCodes { get; set; }
    }
}

با استفاده از کلاس زیر درخواستی به گوگل ارسال شده و در صورتیکه با خطا مواجه شود با استفاده از دستور switch به آن دسترسی خواهیم یافت.
using System.Configuration;
using System.Net;
using Newtonsoft.Json;

namespace BaseConfig.Security.Captcha
{
    public class ReCaptcha
    {
        public static string _secret;

        static ReCaptcha()
        {
            _secret = ConfigurationManager.AppSettings["ReCaptchaGoogleSecretKey"];
        }

        public static bool IsValid(string response)
        {
            //secret that was generated in key value pair
            var client = new WebClient();
            var reply = client.DownloadString($"https://www.google.com/recaptcha/api/siteverify?secret={_secret}&response={response}");

            var captchaResponse = JsonConvert.DeserializeObject<RepaptchaResponse>(reply);

            // when response is false check for the error message
            if (!captchaResponse.Success)
            {
                //if (captchaResponse.ErrorCodes.Count <= 0) return View();

                //var error = captchaResponse.ErrorCodes[0].ToLower();
                //switch (error)
                //{
                //    case ("missing-input-secret"):
                //        ViewBag.Message = "The secret parameter is missing.";
                //        break;
                //    case ("invalid-input-secret"):
                //        ViewBag.Message = "The secret parameter is invalid or malformed.";
                //        break;

                //    case ("missing-input-response"):
                //        ViewBag.Message = "The response parameter is missing.";
                //        break;
                //    case ("invalid-input-response"):
                //        ViewBag.Message = "The response parameter is invalid or malformed.";
                //        break;

                //    default:
                //        ViewBag.Message = "Error occured. Please try again";
                //        break;
                //}
                return false;
            }
            // Captcha is valid
            return true;
        }
    }
}

تابع IsValid از نوع برگشتی Boolean بوده و خطایی برگشت داده نخواهد شد و از این جهت به صورت کامنت برای شما گذاشته شده که می‌توان متناظر با کد نویسی آن را تغییر دهید.
در اکشن زیر مقدار response برسی می‌شود تا خالی نباشد و همچنین مقدار آن را می‌توان با استفاده از تابع IsValid در کلاس ReCaptcha به سمت گوگل فرستاد.
        //
        // POST: /Account/Login
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public virtual async Task<ActionResult> Login(LoginPageModel model, string returnUrl)
        {
            var response = Request["g-recaptcha-response"];
            if (response != null && ReCaptcha.IsValid(response))
            {
                // 
            }
         }

گاها اتفاق می‌افتد که از دستورات Ajax برای ارسال اطلاعات به سمت سرور استفاده می‌شود که در این صورت لازم است بعد از پایان عملیات، کپچا ریفرش گردد. برای این کار می‌توان از دستور جاوا اسکریپتی زیر استفاده نمود. در صورتیکه صفحه Postback شود، کپچا مجددا ریفرش خواهد شد.

/**
 * 
 * @param {} data 
 * @returns {} 
 */
function Success(data) {
    grecaptcha.reset();
}

تا اینجا موفق شدیم تا فرم ارسالی همراه کپچا را به سمت سرور ارسال کنیم. اما ممکن است در یک صفحه از چند کپچا استفاده شود که در این صورت می‌بایستی دستورات سمت کاربر تغییر نمایند.

برای این کار دستور
<div data-sitekey="6LdHGgwTAAAAAClKFhGthRrjBXh5AUGd4eWNCQq7"></div>  
که در بالا تعریف شد، به شکل زیر تغییر خواهد کرد:

    <script>
        var recaptcha1;
        var recaptcha2;
        var myCallBack = function () {
            //Render the recaptcha1 on the element with ID "recaptcha1"
            recaptcha1 = grecaptcha.render('recaptcha1', {
                'sitekey': '6Lf9FQwTAAAAAE6XlDqrey24K4xJOPM5nNVBmNO9',
                'theme': 'light'
            });

            //Render the recaptcha2 on the element with ID "recaptcha2"
            recaptcha2 = grecaptcha.render('recaptcha2', {
                'sitekey': '6Lf9FQwTAAAAAE6XlDqrey24K4xJOPM5nNVBmNO9',
                'theme': 'light'
            });

            //Render the recaptcha3 on the element with ID "recaptcha3"
            recaptcha2 = grecaptcha.render('recaptcha3', {
                'sitekey': '6Lf9FQwTAAAAAE6XlDqrey24K4xJOPM5nNVBmNO9',
                'theme': 'light'
            });
        };
    </script>

برای نمایش کپچا، تگ‌های div با id متناظر با recaptcha1, recaptcha2, recaptcha3 ( در این مثال از سه کپچا در صفحه استفاده شده است ) در صفحه قرار خواهند گرفت.

<div id="recaptcha1"></div>
<div id="recaptcha2"></div>
<div id="recaptcha3"></div>

کار ما تمام شد. حال اگر پروژه را اجرا نمائید، در صفحه سه کپچا مشاهده خواهید کرد.


چند زبانه کردن کپچا:

برای چند زبانه کردن کافیست با مراجعه به این لینک و یا استفاده از تصویر بالا ( زبان‌های پشتیبانی ) مقدار آن زبان را برابر با پراپرتی hl که به صورت کوئری استرینگ برای گوگل ارسال می‌گردد، استفاده نمود. کد زیر نمونه‌ی استفاده شده برای زبان‌های انگلیسی و فارسی می‌باشد که با ریسورس مقدار دهی می‌شود.
<script src='https://www.google.com/recaptcha/api.js?hl=@(App_GlobalResources.CP.CurrentAbbrivation)'></script>

در صورتی که از فایل ریسوس استفاده نمی‌کنید می‌توان به صورت مستقیم مقدار دهی نمائید:
<script src='https://www.google.com/recaptcha/api.js?hl=fa'></script>



برای دوستانی که با تکنولوژی ASP.Net کار می‌کنند، این روال هم برای آنها هم صادق می‌باشد.

برای دریافت پروژه اینجا کلیک نمائید.
مطالب
لینک‌های هفته دوم دی

وبلاگ‌ها ، سایت‌ها و مقالات ایرانی (داخل و خارج از ایران)


ASP. Net


طراحی و توسعه وب


PHP


اس‌کیوال سرور


سی شارپ


SharePoint

عمومی دات نت


ویندوز


مسایل اجتماعی و انسانی برنامه نویسی


متفرقه


مطالب
دریافت مناسبت‌های سال 1388 از یک فایل XML

همیشه با نزدیک شدن آخر سال، به روز کردن مناسبت‌های تقویم سال بعد ضروری می‌شود. دوستان لینوکسی ما هم در این مورد زحمت کشیده و برنامه‌ای را تهیه کرده‌اند که از آدرس زیر قابل دریافت است:
http://download.gna.org/jalali-calendar/

پس از دریافت برنامه، مناسبت‌های سال 1388 در فایل 1388.xml قابل مشاهده است (با تقدیر و تشکر از زحمات این عزیزان). فرض کنید می‌خواهیم این اطلاعات را به اس کیوال سرور منتقل کنیم.
فرمت این فایل به شکل زیر است:

<?xml version="1.0" encoding="UTF-8"?>
<cal1388>
<day>
<num>1/1</num>
<desc>عید نوروز، لحظه تحویل سال:ساعت 15 و 13 دقیقه و 39 ثانیه </desc>
</day>
</cal1388>

حداقل سه راه حل برای انجام اینکار (خواندن فایل xml توسط اس کیوال سرور) موجود است:

راه اول:

SELECT '1388' saal,
X.day.query('num').value('.', 'nVARCHAR(50)') rooz_maah,
X.day.query('desc').value('.', 'nVARCHAR(max)') tozih
FROM (
SELECT CAST(x AS XML)
FROM OPENROWSET(
BULK
'c:\1388.xml',
SINGLE_BLOB
) AS T(x)
) AS T(x)
CROSS APPLY x.nodes('cal1388/day') AS X(day);

راه دوم:

DECLARE @MyXML XML

SELECT @MyXML = CAST(x AS XML)
FROM OPENROWSET(
BULK
'c:\1388.xml',
SINGLE_BLOB
) AS T(x)

SELECT 1388 saal,
nref.value('num[1]', 'nvarchar(50)') rooz_maah,
nref.value('desc[1]', 'nvarchar(max)') tozih

FROM @MyXML.nodes('//day') AS R(nref)

راه سوم:
DECLARE @MyXML XML
DECLARE @handle INT

SELECT @MyXML = CAST(x AS XML)
FROM OPENROWSET(
BULK
'c:\1388.xml',
SINGLE_BLOB
) AS T(x)

EXEC sp_xml_preparedocument @handle OUTPUT,
@MyXML

SELECT 1388 saal,
*
FROM OPENXML(@handle, '/cal1388/day', 2) WITH
(num VARCHAR(20), [desc] NVARCHAR(MAX))

EXEC sp_xml_removedocument @handle

اکنون که می‌توانیم اطلاعات این فایل را select کنیم، Insert آن ساده است . برای مثال:
INSERT INTO tblMonasebat
(
saal,
rooz_mah,
tozih
)
SELECT '1388' saal,
nref.value('num[1]', 'nvarchar(50)') rooz_maah,
nref.value('desc[1]', 'nvarchar(max)') tozih
FROM @MyXML.nodes('//day') AS R(nref)

بدیهی است امکان مقدار دهی @MyXML به صورت یک رشته که حاوی محتویات فایل است نیز مهیا می‌باشد (بجای خواندن از فایل).

برای مطالعه‌ی بیشتر
XML Support in Microsoft SQL Server 2005
Beginning SQL Server 2005 XML Programming

نظرات مطالب
ایجاد کپچایی (captcha) سریع و ساده در ASP.NET MVC 5
با سلام و با تشکر؛ با اجازه بنده کد فوق رو کامل‌تر کردم و یک سری کد جدید بهش اضافه کردم و برخی بخش‌ها رو هم تغییر داده ام:
1- به جای سوال ، بنده یک عبارت رو نمایش میدم
2- ارسال دیتا از طریق کوئری استرینگ که باعث میشه سشن دیگه نیاز نباشه و از مصرف حافظه رو تا حد زیادی کاسته بشه.
البته این مورد برای سایت‌های پربازدید خیلی قابل لمس است و ممکنه روی سایت‌های معمولی تفاوت زیادی احساس نشه.
3- ارسال داده بصورت هش شده ، که این رو بنده خودم با یک کلاس دست ساز معمولی به روش TripleDes انجام داده ام که دوستان به هر روشی می‌تونن داده هاشون رو هش کنن.
4- یکم حروف رو چرخوندم و فاصله بین حروف رو هم طوری تنظیم کردم که در عرض تصویر پخش بشن (از کل عرض تصویر استفاده بشه)
* شایان ذکر است که به نظر من روش فوق در ایجاد نویز‌های دایره ای بسیار زیبا بود، چون همیشه همه جا با یک سری خط ساده نویز ایجاد می‌کنن ولی روش فوق واقعا خلاقانه و قشنگ بود :)
ساختار کنترلر ریکپچای من :
public class CaptchaController : Controller
    {
        private static readonly Brush ForeColor = Brushes.Black;
        private const string FontName = "tahoma";
        private const int FontSize = 14;
        private const int Width = 130;
        private const int Height = 35;

        [HttpGet]
        public ActionResult Image(string cc)
        {
            if (string.IsNullOrEmpty(cc) || string.IsNullOrWhiteSpace(cc))
                return null;

            var captchaData = CustomHashing.DecryptTpl(cc);

            var rand = new Random((int)DateTime.Now.Ticks);

            // image stream
            FileContentResult img = null;

            using (var mem = new MemoryStream())
            using (var bmp = new Bitmap(Width, Height))
            using (var mtrx = new Matrix())
            using (var gfx = Graphics.FromImage((Image)bmp))
            {
                gfx.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
                gfx.SmoothingMode = SmoothingMode.AntiAlias;
                gfx.FillRectangle(Brushes.White, new Rectangle(0, 0, bmp.Width, bmp.Height));

                //add noise
                int rn, xn, yn;
                var pen = new Pen(Color.Yellow);

                for (int i = 1; i < 10; i++)
                {
                    pen.Color = Color.FromArgb((rand.Next(0, 255)), (rand.Next(0, 255)), (rand.Next(0, 255)));

                    rn = rand.Next(0, (130 / 3));
                    xn = rand.Next(0, 130);
                    yn = rand.Next(0, 30);

                    gfx.DrawEllipse(pen, xn - rn, yn - rn, rn, rn);
                }

                //add chars
                #region draw pic

                float x = 1, y = 1;
                int degree = 10;

                for (int i = 0; i < captchaData.Length; i++)
                {
                    mtrx.Reset();

                    x = (float)(Width * (0.19 * i));

                    y = (float)(Height * 0.19);

                    degree = rand.Next(-25, 25);

                    if (i == 0 && degree > 20)
                    {
                        x += (FontSize + 5);
                        y -= 15;
                    }

                    mtrx.RotateAt(degree, new PointF(x, y));

                    gfx.Transform = mtrx;

                    gfx.DrawString(captchaData[i].ToString(), new Font(FontName, FontSize), ForeColor, x, y);

                    gfx.ResetTransform();
                }
                #endregion

                //render as Jpeg
                bmp.Save(mem, System.Drawing.Imaging.ImageFormat.Jpeg);
                img = this.File(mem.GetBuffer(), "image/Jpeg");
            }

            return img;
        }
برای استفاده هم داریم :
@{
    var r = new Web.Tools.CustomRandom();
    string hash = Web.Tools.CustomHashing.EncryptTpl(r.CraeteCapchaNumericData(4));
} 

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>test Index</title>
</head>
<body>
<div>

    <img src="@Url.Action("Image", "Captcha", new { cc = hash })" />

</div>
</body>
</html>
محتوای کلاس CustomRandom :
این کلاس به تعداد مورد نیاز کاراکتر عددی/عددی-حروفی می‌سازه و به شما تحویل میده
public class CustomRandom
 {
        /// <summary>
        /// ساخت یک عبارت عددی رندوم
        /// </summary>
        public string CraeteCapchaNumericData(int length)
        {
            var rnd = new Random((int) DateTime.Now.Ticks);
            var temp = new StringBuilder();

            for (var i = 0; i < length; i++)
                temp.Append(Convert.ToChar(rnd.Next(49, 58)));

            return temp.ToString();
        }

        /// <summary>
        /// ساخت یک عبارت رندوم
        /// </summary>
        public string CreateRandomName(int length)
        {
            var rnd = new Random((int) DateTime.Now.Ticks);
            var temp = new StringBuilder();
            var flag = 1;

            for (var i = 0; i < length; i++)
            {
                flag = rnd.Next(0, 15);

                if (flag < 5)
                    temp.Append(Convert.ToChar(rnd.Next(97, 123))); // lower
                else if (flag >= 5 && flag < 10)
                    temp.Append(Convert.ToChar(rnd.Next(49, 58))); // numeric
                else
                    temp.Append(Convert.ToChar(rnd.Next(65, 91))); // biger
            }

            return temp.ToString();
        } 
}
همانطور که گفتم پیاده سازی متد های DecryptTpl   و EncryptTpl  کلاس CustomHashing   رو به خود دوستان واگذار می‌کنم تا با هر الگوریتمی که دوست دارن این کار رو انجام بدن. (^)
امیدوارم کد بنده به دوستان کمک کنه.
موفق باشید
نظرات مطالب
Postable
نه با مطالب دیگری هم که ارسال کردید به همین صورت است برای مثال
http://yourite/2011/03/blog-post_12.html
نظرات مطالب
تهیه قالب برای ارسال ایمیل‌ها در ASP.NET Core توسط Razor Viewها
راه حل جامعی ندارد؛ چون تمام mail clients از آن پشتیبانی نمی‌کنند. اما آنهایی که از آن پشتیبانی می‌کنند به صورت ذیل است:
- ابتدا تعریف فونت وب به صورت لینک شده از محلی مشخص؛ به همراه فونت fallback برای آوت لوک:
<style type="text/css">
  @font-face{
    font-family:'Open Sans';
    font-style:normal;
    font-weight:400;
    src:local('Open Sans'), local('OpenSans'), url('http://fonts.gstatic.com/s/opensans/v10/cJZKeOuBrn4kERxqtaUH3bO3LdcAZYWl9Si6vvxL-qU.woff') format('woff');
  }
</style>

<!--[if mso]>
<style type="text/css">
.fallback-text {
    font-family: Arial, sans-serif;
}
</style>
<![endif]-->
- سپس استفاده‌ی از آن:
<td class="fallback-text" style="font-family: 'Open Sans', Arial, sans-serif;">Open sans font for all!</td>
نظرات مطالب
استفاده از Froala WYSIWYG Editor در ASP.NET
برای حذف یک تصویر از قسمت منیجر از خاصیت imageDeleteURL  و برای ارسال پارامترها از imageDeleteParams استفاده می‌کنیم. خاصیت imageDeleteParams را باید به چه صورت مقداردهی کرد
موقعی که از کد زیر استفاده میکنم ادیتور لود نمیشه
imageDeleteParams: { src: $img.attr('src') }
مطالب
معرفی و استفاده از DDL Triggers در SQL Server

استفاده از DDL Trigger

امکان ایجاد Trigger برای عملیات (DDL(Data Definition Language از SQL Server 2005  فراهم گردید. عملیاتی مانند ایجاد یک جدول جدید در بانک اطلاعاتی، اضافه شدن یک Login جدید و یا ایجاد یک بانک اطلاعاتی جدید را به وسیله این نوع Trigger‌ها می‌توان کنترل نمود. در حقیقت DDL Trigger به شما اجازه می‌دهد که از  تاثیر تعدادی از دستورات DDL جلوگیری کنید. بدین ترتیب که تقریباً هر دستور DDL به طور خودکار، تراکنشی (Transactional) اجرا می‌شود . می‌توان با دستور ROLLBACK TRANSACTION اجرای دستور DDL را لغو نمود. توجه شود همه دستورات DDL به صورت تراکنشی اجرا نمی‌شوند، به عنوان مثال دستور  ALTER DATABASE ممکن است Database را تغییر دهد. در این صورت ساختار فایلی Database را تغییر می‌دهد، از آنجائی که سیستم عامل ویندوز به صورت تراکنشی عمل نمی‌کند بنابراین شما نمی‌توانید این عمل فایل سیستمی را لغو نمائید. به هر حال شما می‌توانید Trigger را با ALTER DATABASE فعال (fire) کنید برای عملیات Auditing، ولی نمی‌توان از انجام عمل  ALTER  DATABASE جلوگیری کرد.
برای نمونه می‌خواهیم از حذف و یا تغییر جداول یک بانک اطلاعاتی که به صورت عملیاتی در حال سرویس دهی است جلوگیری کنیم، برای اینکار از دستورهای زیر استفاده می‌کنیم:
create trigger Prevent_AlterDrop
on database
for drop_table, alter_table
as
  print 'table can not be dropped or altered'
  rollback transaction
از عبارت  ON  برای مشخص کردن محدوده Trigger در سطح SQL Instance (در این صورت ON All SERVER نوشته می‌شود) و یا در سطح Database (در این حالت ON DATABASE نوشته می‌شود)  استفاده می‌شود و از عبارت FOR  برای مشخص کردن رویداد یا گروه رویدادی که سبب فراخوانی  Trigger می‌شود، استفاده  خواهد شد. 

1- معرفی تابع ()EVENTDATA

  این تابع، یک تابع سیستمی مهم است که در DDL Trigger استفاده می‌شود. در حالیکه DDL Trigger در هر سطحی فعال (fire) شود تابع سیستمی ()EVENTDATA فراخوانی (raise) می‌شود. خروجی تابع در قالب XML است. می‌توان اطلاعات را از تابع EVENTDATA دریافت کرد و آنها را در یک جدول با فیلدی از جنس XML و یا با استفاده از XPath Query  ثبت کرد (Logging). عناصر کلیدی (Key Elements) تابع EVENTDATA  به شرح زیر است:
•  EventType: نوع رویدادی که باعث فراخوانی Trigger شده است.
•  PostTime: زمانی که رویداد رخ می‌دهد.
•  SPID :SPID کاربری که باعث ایجاد رویداد شده است.
•  ServerName:  نام SQL Instance  که  رویداد در آن رخ داده است.
•  LoginName: نام Login که عمل مربوط به وقوع رویداد را اجرا می‌کند.
•  UserName: نام User که عمل مربوط به وقوع رویداد را اجرا می‌کند.
•  DatabaseName: نام Database که رویداد در آن رخ می‌دهد.
•  ObjectType: نوع Object که اصلاح، حذف و یا ایجاد شده است.
•  ObjectName: نام Object که اصلاح، حذف و یا ایجاد شده است.
•  TSQLCommand: دستور T-SQL که اجرا شده و باعث اجرا شدن Trigger شده است. 

2- بررسی یک سناریو نمونه

  برای نمونه در دستورات زیر جدولی با نام ddl_log
  CREATE TABLE ddl_log 
  (
     EventType nvarchar(100), 
 PostTime datetime,
 SPID nvarchar(100),
 ServerName nvarchar(100),
 LoginName nvarchar(100),
 UserName  nvarchar(100),
 DatabaseName  nvarchar(100),
 ObjectName  nvarchar(100),
 ObjectType  nvarchar(100),
 DefaultSchema nvarchar(100),
 [SID]  nvarchar(100),
 TSQLCommand nvarchar(2000));
و یک Trigger با نام log برای رویدادهایی که در سطح Database رخ می‌دهد، ایجاد می‌کنیم.
CREATE TRIGGER [Log] ON DATABASE
 FOR DDL_DATABASE_LEVEL_EVENTS
 AS
 DECLARE @data XML
 SET @data = EVENTDATA()
 INSERT INTO ddl_log
 VALUES (
   @data.value('(/EVENT_INSTANCE/EventType)[1]',   'nvarchar(100)'),
   @data.value('(/EVENT_INSTANCE/PostTime)[1]',   'datetime'),
   @data.value('(/EVENT_INSTANCE/SPID)[1]',   'nvarchar(100)'),
   @data.value('(/EVENT_INSTANCE/ServerName)[1]',   'nvarchar(100)'),
   @data.value('(/EVENT_INSTANCE/LoginName)[1]',   'nvarchar(100)'),
   @data.value('(/EVENT_INSTANCE/UserName)[1]',   'nvarchar(100)'),
   @data.value('(/EVENT_INSTANCE/DatabaseName)[1]',   'nvarchar(100)'),
   @data.value('(/EVENT_INSTANCE/ObjectName)[1]',   'nvarchar(100)'),
   @data.value('(/EVENT_INSTANCE/ObjectType)[1]',   'nvarchar(100)'),
   @data.value('(/EVENT_INSTANCE/DefaultSchema)[1]',   'nvarchar(100)'),
   @data.value('(/EVENT_INSTANCE/SID)[1]',   'nvarchar(100)'),
   @data.value('(/EVENT_INSTANCE/TSQLCommand)[1]',   'nvarchar(2000)'));
نمونه ای از مقادیر ذخیره شده در جدول  ddl_log به شکل زیر خواهد بود:

3- ملاحظات

  در صورت فعال شدن  Trigger می‌توان برخی موارد مانند محدودیت زمانی، کاربر اجرا کننده و ... را  اضافه نمود. برای مثال در دستور زیر اجازه تغییرات در این زمان ( بین  7:00 A.M. تا .8:00 P.M ) امکان پذیر نیست و در صورت اقدام پیغام خطا دریافت می‌کنید و دستورات Create لغو خواهند شد و اگر خارج از زمان فوق دستورات DDL را اجرا کنید دستورات به طور موفقیت آمیز اجرا می‌شود و البته تغییرات نیز Log می‌شوند. 
 

  این Trigger تاثیرات کمی بر روی کارایی دارد به این دلیل که معمولاً رویداد‌های DDL به ندرت رخ می‌دهد. می‌توانید هنگامی که قصد دارید دستورات DDL را اجرا کنید موقتاً Trigger را با دستورات زیر غیر فعال نمائید:

پس از Overrdie کردن می‌توانید مجدداً Trigger را فعال کنید: 


4- معرفی DDL Event Groups: 

برای مشاهده جزئیات بیشتر می‌توانید به این لینک مراجعه کنید.

Server Level

DDL_SERVER_LEVEL_EVENTS

 DDL_LINKED_SERVER_EVENTS
 DDL_LINKED_SERVER_LOGIN_EVENTS
 DDL_REMOTE_SERVER_EVENTS
 DDL_EXTENDED_PROCEDURE_EVENTS
 DDL_MESSAGE_EVENTS
 DDL_ENDPOINT_EVENTS
 DDL_SERVER_SECURITY_EVENTS
 DDL_LOGIN_EVENTS
 DDL_GDR_SERVER_EVENTS
 DDL_AUTHORIZATION_SERVER_EVENT Database Level

Database Level

DDL_DATABASE_LEVEL_EVENTS  
DDL_TABLE_VIEW_EVENTS
DDL_TABLE-EVENTS
DDL_VIEW_EVENTS
DDL_INDEX_EVENTS
DDL_STATISTICS_EVENTS
DDL_DATABASE_SECURITY_EVENTS
DDL_CERTIFICATE_EVENTS
DDL_USER_EVENTS
DDL_ROLE_EVENTS
DDL_APPLICATION_ROLE_EVENTS
DDL_SCHEMA_EVENTS
DDL_GDR_DATABASE_EVENTS
DDL_AUTHORIZATION_DATABASE_EVENTS
DDL_FUNCTION_EVENTS
DDL_PROCEDUER_EVENTS
DDL_TRIGGER_EVENTS
DDL_PARTITION_EVENTS
DDL_PARTITION_FUNCTION_EVENTS
DDL_PARTITION_SCHEME_EVENTS
DDL_SSB_EVENTS
DDL_MESSAGE_TYPE_EVENTS
DDL_CONTRACT_EVENTS
DDL_QUEUE_EVENTS
DDL_SERVER_EVENTS
DDL_ROUTE_EVENTS
DDL_REMOTE_SERVICE_BINDING_EVENTS
DDL_XML_SCHEMA_COLEECTION_EVENTS
DDL_FULLTEXT_CATALOG_EVENTS
DDL_DEFAULT_EVENTS
DDL_EXTENDED_PROPERTY_EVENTS
DDL_PLAN_GUIDE_EVENTS
DDL_RULE_EVENTS
DDL_SYNONYM_EVENTS
DDL_EVENT_NOTIFICATION_EVENTS
DDL_ASSEMBLY_EVENTS
DDL_TYPE_EVENTS


مطالب
نگاشت دیتای XML به کمک AutoMapper
صورت مساله که مشخصه قراره دیتای رو از منبع داده‌ی Xml به model مورد نظرمون نگاشت کنیم چیزی شبیه کاری که متد GetEntries انجام میده و تو این پست معرفی شده...

AutoMapper به صورت داخلی و با استفاده از قرارداد‌ها نمیتونه xml رو به object تبدیل کنه ولی این کار به کمک LINQ to XML قابل انجامه.

مثالی که برای این پست انتخاب شده سوژه‌ی داغ روزهای اخیره ؟!
مدل زیر رو در نظر داشته باشید
 public class PreciousMetal
    {
        public string Name { get; set; }
        public float Price { get; set; }
        public DateTime UpdateTime { get; set; }
    }

قراره از یک وب سرویس اطلاعات مربوط به فلزات گرانبها رو دریافت و به مدل PreciousMetal نگاشت کنیم.ساختار اطلاعات دریافتی ما به شکل زیره
<pricelist currency="usd">
  <price timestamp="1349347920" per="ozt" commodity="gold">1788.70</price>
  <price timestamp="1349347860" per="ozt" commodity="palladium">665.50</price>
  <price timestamp="1349347920" per="ozt" commodity="platinum">1701.25</price>
  <price timestamp="1349347920" per="ozt" commodity="silver">34.91</price>
</pricelist>

برای نگاشت‌های معمولی کار سختی نداریم و از MapFrom استفاده میکنیم مثلا برای قیمت
Mapper.CreateMap<XElement, PreciousMetal>().ForMember(des => des.Price,
                                                                  op =>
                                                                  op.MapFrom(src => src.Value));

ولی برای زمان دریافت قیمت با توجه به متفاوت بودن زمان دریافتی مثلا در اینجا Unix time از Custom value resolvers استفاده میکنیم
public class UnixTimestampResolver : ValueResolver<XElement, DateTime>
    {
        protected override DateTime ResolveCore(XElement source)
        {
            var origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return origin.AddSeconds(Convert.ToDouble(source.Attribute("timestamp").Value));
        }
    }

همچنیا میخواهیم از معادل فارسی نام فلزات گرانبها استفاده کنیم
public class EnglishPMetalToFarsiResolver : ValueResolver<XElement, string>
    {
        readonly Dictionary<string, string> _pMetaldic = new Dictionary<string, string>
                                                       {
                                                           {"gold", "طلا"},
                                                           {"palladium", "پالادیوم"},
                                                           {"platinum", "پلاتین "},
                                                           {"silver", "نقره"}
                                                       };
        protected override string ResolveCore(XElement source)
        {
            string pMetalFarsi;
            return _pMetaldic.TryGetValue(source.Attribute("commodity").Value, out pMetalFarsi) ? pMetalFarsi : string.Empty;
        }
    }

نکته:از سری قبلی آشنایی با AutoMapper همیشه بین انتخاب Custom Value Formatters و Custom value resolvers مشکل داشتم مثلا همین قسمت بنظر خودم Custom Value Formatters مناسبتر میاد بعد کمی وقت گذاشتن مشخص شد گویا یه جورایی Custom Value Formatters اضافه س و اشتباه تو طراحی بوده.

و اما نحوه استفاده
static void Main(string[] args)
        {
            //تعریف نگاشت‌ها Mapper.CreateMap<XElement, PreciousMetal>().ForMember(des => des.Name,
                                                                  op => op.ResolveUsing<EnglishPMetalToFarsiResolver>())
                .ForMember(des => des.Price,
                           op =>
                           op.MapFrom(src => src.Value))

                .ForMember(des => des.UpdateTime, op => op.ResolveUsing<UnixTimestampResolver>());

            Mapper.AssertConfigurationIsValid();

            //دریافت قیمت‌ها از منبع داده
            var doc = XDocument.Load("http://www.xmlcharts.com/cache/precious-metals.xml");
            var priceData = doc.Descendants("pricelist").Take(1).Elements("price");

            //فراخوانی نگاشت
            var preciousMetals = Mapper.Map<IEnumerable<XElement>, IList<PreciousMetal>>(priceData);


            foreach (var preciousMetal in preciousMetals)
            {
                Console.WriteLine(preciousMetal.Name + " " + preciousMetal.Price + " " + preciousMetal.UpdateTime.ToShortDateString());
            }

            Console.ReadLine();

        }

مطالب
حذف هدرهای مربوط به وب سرور از یک برنامه‌ی ASP.Net

به همراه هر درخواستی از سرور چه از طرف کلاینت و چه از طرف سرور، یک سری header نیز ارسال می‌شود. برای مثال مرورگر، نوع خود را به همراه یک سری از قابلیت‌های مربوطه مانند الگوریتم‌های فشرده سازی پشتیبانی شده به سرور ارسال می‌کند و در مقابل وب سرور هم یک سری هدر را مانند مدت زمان کش کردن اطلاعات دریافتی، نوع و نگارش سرور و امثال آن، به کلاینت ارسال خواهد کرد.
از دیدگاه امنیتی این اطلاعات اضافی هستند. برای مثال از تصویر زیر (که با استفاده از افزونه‌ی فایرباگ تهیه شده) دقیقا می‌توان وب سرور، نگارش آن و بسیاری از موارد دیگر را به سادگی تشخیص داد. در ادامه می‌خواهیم این هدرهای اضافی را حذف کنیم.



امکان تغییر و حذف هدرهای مربوط به response تنها با استفاده از امکانات IIS7 میسر است (در حالت integrated pipeline) و IIS6 چنین اجازه‌ای را به ASP.Net نمی‌دهد.
برای این منظور یک پروژه‌ی جدید، به نام SecurityMdl از نوع class library را ایجاد کرده و دو فایل SecurityMdl.cs و CRemoveHeader.cs را به آن اضافه خواهیم کرد:

//SecurityMdl.cs
using System;
using System.Web;

namespace SecurityMdl
{
public class SecurityMdl : IHttpModule
{
public void Init(HttpApplication app)
{
app.PreSendRequestHeaders += app_PreSendRequestHeaders;
}

static void app_PreSendRequestHeaders(object sender, EventArgs e)
{
CRemoveHeader.CheckPreSendRequestHeaders(sender);
}

public void Dispose() { }
}
}


در این Http module ، با تغییر اطلاعات دریافتی در روال رخ‌داد گردان PreSendRequestHeaders می‌توان به مقصود رسید:

//CRemoveHeader.cs
using System;
using System.Collections.Generic;
using System.Web;

namespace SecurityMdl
{
class CRemoveHeader
{
private static readonly List<string> _headersToRemoveCache
= new List<string>
{
"X-AspNet-Version",
"X-AspNetMvc-Version",
"Server"
};

public static void CheckPreSendRequestHeaders(Object sender)
{
//capture the current request
var currentResponse = ((HttpApplication)sender).Response;

//removing headers
//it only works with IIS 7.x's integrated pipeline
_headersToRemoveCache.ForEach(h => currentResponse.Headers.Remove(h));

//modify the "Server" Http Header
currentResponse.Headers.Set("Server", "Test");
}
}
}
در اینجا علاوه بر حذف هدرهای ذکر شده در headersToRemoveCache ، هدر Server در پایان کار با یک مقدار جدید نیز ارسال می‌گردد.
و نهایتا برای استفاده از آن (علاوه بر افزودن ارجاعی به این ماژول جدید) چند سطر زیر را باید به وب کانفیگ برنامه اضافه کرد:

<system.webServer>
<modules>
<add name="SecurityMdl" type="SecurityMdl.SecurityMdl, SecurityMdl"/>
</modules>

با استفاده از این روش تمامی هدرهای مورد نظر بجز هدری به نام X-Powered-By حذف خواهند شد. برای حذف هدر مربوط به X-Powered-By که توسط خود IIS مدیریت می‌شود باید موارد زیر را به web.config برنامه اضافه کرد:

<system.webServer>
<httpProtocol>
<customHeaders>
<remove name="X-Powered-By"/>
</customHeaders>
</httpProtocol>
و یا می‌توان توسط خود IIS‌ نیز این مورد را تغییر داد یا کلا حذف نمود:




اکنون پس از این تغییرات حاصل کار و هدر نهایی دریافت شده از response یک برنامه‌ی ASP.net به شکل زیر درخواهد آمد:





برای مطالعه‌ی بیشتر
Remove the X-AspNet-Version header
Cloaking your ASP.NET MVC Web Application on IIS 7
IIS 7 - How to send a custom "Server" http header
Removing Unnecessary HTTP Headers in IIS and ASP.NET
Remove X-Powered-By: ASP.NET HTTP Response Header