ASP.NET MVC #18
LocalDB FAQ
اگر در ویژوال استودیو گزینهی View -> SQL Server Object Explorer را مشاهده نمیکنید (جهت امکان کار بهتر با بانکهای اطلاعاتی LocalDB)، نیاز است آخرین نگارش SQL Server Data Tools (SSDT) را نصب کنید. فایل ISO آن برای VS 2015
نحوهی ذخیره سازی لاگها
سطح سایت
- Client IP address
- User name
- Date
- Time
- Service and instance
- Server name
- Server IP address
- Time taken
- Client bytes sent
- Server bytes sent
- (Service status code (A value of 200 indicates that the request was fulfilled successfully
- (Windows status code (A value of 0 indicates that the request was fulfilled successfully.
- Request type
- Target of operation
- (Parameters (the parameters that are passed to a script
192.168.114.201, -, 03/20/01, 7:55:20, W3SVC2, SERVER, 172.21.13.45, 4502, 163, 3223, 200, 0, GET, /DeptLogo.gif, -,
نام فیلد | نوع حالت مقداردهی | توضیح اتفاقات افتاده | |
Client IP address | 192.168.114.201 | آی پی کلاینت | |
User name | - | کاربر ناشناس است | |
Date | 03/20/01 | تاریخ فعالیت | |
Time | 7:55:20 | ساعت فعالیت | |
Service and instance | W3SVC2 | لاگی که مربوط به سایت خاصی میشود به صورت #W3SVC نمایش داده میشود که علامت # شماره سایت میباشد که در اینجا این لاگ مربوط به سایت شماره 2 است | |
Server name | SERVER | نام سرور | |
Server IP | 172.21.13.45 | آی پی سرور | |
Time taken | 4502 | چقدر انجام عملیات این درخواست به طول انجامیده است که بر حسب میلی ثانیه است. | |
Client bytes sent | 163 | تعداد بایت هایی که از طرف کلاینت به سرور ارسال شده است | |
Server bytes sent | 3223 | تعداد بایت هایی که از طرف سرور به سمت کلاینت ارسال شده است | |
Service status code | 200 | درخواست کاملا موفقیت آمیز بوده است | |
Windows status code | 0 | درخواست کاملا موفقیت آمیز بوده است | |
Request type | GET | نوع درخواست کاربر | |
Target of operation | /DeptLogo.gif | کاربر قصد دانلود یک فایل تصویری GIF داشته است که نامش Deptlogo است | |
Parameters | - | پارامتری ارسال نشده است |
فرمت NCSA: این فرمت توسط مرکز علمی کاربردهای ابرمحاسباتی National Center for Supercomputing Applications ایجاد شده و دقیقا مانند قبلی نمیتوان در آن نوع فیلدها را مشخص کرد و برای جدا سازی، از فاصله space استفاده میکند و ثبت مقدار زمان در آن هم به صورت محلی و هم UTC میباشد.
- Remote host address
- (Remote log name (This value is always a hyphen
- User name
- Date, time, and Greenwich mean time (GMT) offset
- Request and protocol version
- (Service status code (A value of 200 indicates that the request was fulfilled successfully
- Bytes sen
نمونه ای از یک لاگ ثبت شده:
172.21.13.45 - Microsoft\JohnDoe [08/Apr/2001:17:39:04 -0800] "GET /scripts/iisadmin/ism.dll?http/serv HTTP/1.0" 200 3401
نام فیلد | مقدار ثبت شده | توضیح اتفاق افتاده |
Remote host address | 172.21.13.45 | آی پی کلاینت |
Remote log name | - | نامی وجود ندارد |
User name | Microsoft\JohnDoe | نام کاربری |
Date, time, and GMT offset | [08/Apr/2001:17:39:04 -0800] | تاریخ و ساعت فعالیت به صورت محلی که 8 ساعت از مبدا گرینویچ بیشتر است |
Request and protocol version | GET /scripts/iisadmin/ism.dll?http/serv HTTP/1.0 | کاربر با متد GET و Http نسخهی یک، درخواست فایل ism.dll را کرده است. |
Service status code | 200 | عملیات کاملا موفقیت آمیز بود. |
Bytes sent | 3401 | تعداد بایتهای ارسال شده به سمت کاربر |
امنیت در برابر کاربران مهاجم مانند همان فرمت قبلی صورت گرفته است.
فرمت W3C: توسط W3C توسط کنسرسیوم جهانی وب ارائه شده است و یک فرمت customizable ASCII text-based است. به این معنی که میتوان فیلدهایی که در گزارش نهایی میآید را خودتان مشخص کنید، که برای اینکار در کنار لیست، دکمهی Select وجود دارد که میتوانید هر کدام از فیلدهایی را که خواستید، انتخاب کنید تا به ترتیب در خط لاگ ظاهر شوند. تاریخ ثبت به صورت UTC است.
نام فیلد | توضیح | به طور پیش فرض انتخاب شده است |
Date | تاریخ رخ دادن فعالیت | بله |
Time | ساعت زخ دادن فعالیت بر اساس UTC | بله |
Client IP Address | آی پی کلاینت | بله |
User Name | نام کاربری که هویت آن تایید شده و در صورتی که هویت تایید شده نباشد و کاربر ناشناس باشد، جای آن - قرار میگیرد | بله |
Service Name and Instance Number | نام و شماره سایتی که درخواست در آن صورت گرفته است | خیر |
Server Name | نام سروری که لاگ روی آن ثبت میشود | خیر |
Server IP Address | آی پی سرور که لاگ روی آن ثبت میشود | بله |
Server Port | شمره پورتی که سرویس مورد نظر روی آن پورت اعمال میشود. | بله |
Method | متد درخواست مثل GET | بله |
URI Stem | هدف درخواست یا Target مثل index.htm | بله |
URI Query | کوئری ارسال شده برای صفحات داینامیک | بله |
HTTP Status | کد وضیعینی HTTP status | بله |
Win32 Status | کد وضعیتی ویندوز | خیر |
Bytes Sent | تعداد بایتهای ارسال شده به سمت کلاینت | خیر |
Bytes Received | تعداد بایتهای دریافت شده از سمت کلاینت | خیر |
Time Taken | زمان به طول انجامیدن درخواست بر حسب میلی ثانیه | خیر |
Protocol Version | درخواست با چه نسخهای از پروتکل http یا ftp ارسال شده است | خیر |
Host | اگر در هدر درخواست ارسالی این گزینه بوده باشد، نوشته خواهد شد. | خیر |
User Agent | اطلاعات را از هدر درخواست میگیرد. | بله |
Cookie | اگر کوکی رد و بدل شده باشد، محتویات کوکی ارسالی یا دریافت شده | خیر |
Referrer | کاربر از چه سایتی به سمت سایت ما آمده است. | خیر |
Protocol Substatus | در صورت رخ دادن خطا در IIS ، کد خطا بازگردانده میشود. در IIS به منظور امنیت بیشتر و کاهش حملات، محتوای خطاهای رخ داده در IIS به صورت متنی نمایش داده نمیشوند و شامل کد خطایی به اسم Substatus Code هستند تا مدیران شبکه با ردیابی لاگها پی به دلیل خطا و درخواستهای ناموفق ببرند. برای مثال Error 404.2 به این معنی است که فایل درخواستی به دلیل قوانین محدود کنده، قفل شده و قابل ارائه نیست. ولی هکر تنها با خطای 404 یعنی وجود نداشتن فایل روبرو میشود. در حالت substatus code، کد شماره 2 را هم خواهید داشت که در لاگ ثبت میشود. هر شخصی که در سرور توانایی دسترسی به لاگها را داشته باشد، میتواند کد دوم خطا را نیز مشاهده کند. برای مثال مدیر سرور متوجه میشود که یکی از فایلهای مورد نظر به کاربران، خطای 404 نمایش میدهد و با بررسی لاگها متوجه میشود که کد خطا 404.9 هست. از آنجا که ما همهی کدها را حفظ نیستیم به این صفحه رجوع میکنیم و متوجه میشویم تعداد کاربرانی که برای این فایل، اتصال connection ایجاد کردهاند بیش از مقدار مجاز است و مدیر میتواند این وضع را کنترل کند. برای مثال تعداد اتصالات مجاز را نامحدود unlimited تعیین کند. | بله |
- uri-query
- host
- (User-Agent)
- Cookie
- Referrer
- substatus
گزینه Custom : موقعی که شما این گزینه را انتخاب کنید ماژول logging غیرفعال خواهد شد. زیرا این امکان در IIS قابل پیکربندی نیست و نوشتن ماژول آن بر عهده شما خواهد بود؛ با استفاده از اینترفیس های ILogPlugin ، ILogPluginEx و ILogUIPlugin آن را پیاده سازی کنید.
ذخیره اطلاعات به انکدینگ UTF-8 و موضوع امنیت
تاکنون سیلورلایت به سکوهای کاری مختلفی منتقل شده است و با توجه به مزایای استفاده از یک نسخهی ساده شدهی دات نت فریم ورک (برای اینکه حجم افزونهی آن زیاد نشود)، فراهم بودن اکثر امکانات طرح بندی WPF در آن، امکان استفاده از همان زبانهای آشنای دات نت مانند CS و VB برای توسعهی Silverlight، امکان اجرای برنامههای آن در خارج از مرورگر همانند یک برنامهی Desktop (البته با قیود امنیتی بسیار زیاد)، امکان دسترسی کامل به وب سرویسها و امکانات WCF، این روزها کم کم دارد جای بسیاری از فناوریهای دههی قبل را میگیرد و حداقل اگر به لیست تازههای VS.NET 2010 و موارد مرتبط به آن دقت کنیم، Silverlight جزو مواردی است که بیشترین حجم توجه و نوآوری را به خود اختصاص داده است.
در ادامه لیستی از سکوهای کاری را که سیلورلایت به آنها منتقل شده است یا خواهد شد، با هم مرور خواهیم کرد:
- ویندوز : +
- ویندوز مدفون شده : +
- ویندوز فون 7 : +
- Macintosh OS 10.4.11+ Intel-based : + و + (جالب اینجا است که از طرف خود مایکروسافت پشتیبانی میشود)
- لینوکس : +
- تلاش اینتل برای انتقال به لینوکس : +
- Xbox 360 : +
- سیمبین: + و +
public enum Grade { Failing, // = 0 BelowAverage, // = 1 Average, // = 2 VeryGood, // = 3 Excellent // = 4 }
- در این حالت متد ()ToString نوع داده Enum عنوان مقادیر ثابت را بر میگرداند.
- جهت برگشت مقدار عددی و شماره مقادیر ثابتهای تعریف شده از متد ()ToString با فرمت D (شماره مقدار را بصورت Decimal نشان میدهد) و فرمت X جهت نمایش بصورت هگزا میتوان استفاده کرد.
- روش عرف برای نمایش مقدار عددی استفاده از تبدیل نوع صریح به int است.
static void Main(string[] args) { Grade grade = Grade.Average; Console.WriteLine(grade.ToString()); // Print Avarage Console.WriteLine(grade.ToString("D")); // Print 2 Console.WriteLine(grade.ToString("X")); // Print 00000002 Console.WriteLine((int) grade); //Print 2 Console.ReadKey(); }
public enum Grade { Failing = 5, BelowAverage = 10, Average = BelowAverage + 5, // = 15 VeryGood = 18, Excellent = 20 }
public enum Grade : byte { Failing = 5, BelowAverage = 10, Average = BelowAverage + 5, // = 15 VeryGood = 18, Excellent = 20 }
Console.WriteLine(grade.ToString("X")); // Print 0F
Console.WriteLine("Underlying type: {0}", Enum.GetUnderlyingType(grade.GetType())); // Print System.Byte Console.WriteLine("Type Code : {0}", grade.GetTypeCode()); // Print Byte
Console.WriteLine("Value : {0}", (int)grade); // Print 15
استفاده از 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
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));
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)'));
3- ملاحظات
در صورت فعال شدن Trigger میتوان برخی موارد مانند محدودیت زمانی، کاربر اجرا کننده و ... را اضافه نمود. برای مثال در دستور زیر اجازه تغییرات در این زمان ( بین 7:00 A.M. تا .8:00 P.M ) امکان پذیر نیست و در صورت اقدام پیغام خطا دریافت میکنید و دستورات Create لغو خواهند شد و اگر خارج از زمان فوق دستورات DDL را اجرا کنید دستورات به طور موفقیت آمیز اجرا میشود و البته تغییرات نیز Log میشوند.
پس از 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_EVENTSDDL_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
به همین جهت افزونه farsiInput، که کدهای آنرا در ادامه مشاهده میکنید، تهیه گردید. این افزونه تا این تاریخ با IE، فایرفاکس، کروم و اپرا سازگار است و توسط آن کاربر بدون نیاز به داشتن یک صفحه کلید فارسی میتواند فارسی تایپ کند. برای سوئیچ به حالت انگلیسی، دکمه Scroll lock باید روشن شود و این مورد توسط پارامتر changeLanguageKey قابل تغییر است.
// <![CDATA[ (function ($) { $.fn.farsiInput = function (options) { var defaults = { changeLanguageKey: 145 /* Scroll lock */ }; var options = $.extend(defaults, options); var lang = 'fa'; var keys = new Array(1711, 0, 0, 0, 0, 1608, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1705, 1572, 0, 1548, 1567, 0, 1616, 1571, 8250, 0, 1615, 0, 0, 1570, 1577, 0, 0, 0, 1569, 1573, 0, 0, 1614, 1612, 1613, 0, 0, 8249, 1611, 171, 0, 187, 1580, 1688, 1670, 0, 1600, 1662, 1588, 1584, 1586, 1740, 1579, 1576, 1604, 1575, 1607, 1578, 1606, 1605, 1574, 1583, 1582, 1581, 1590, 1602, 1587, 1601, 1593, 1585, 1589, 1591, 1594, 1592); var substituteChar = function (charCode, e) { if (navigator.appName == "Microsoft Internet Explorer") { window.event.keyCode = charCode; } else { insertAtCaret(String.fromCharCode(charCode), e); } }; var insertAtCaret = function (str, e) { var obj = e.target; var startPos = obj.selectionStart; var endPos = obj.selectionEnd; var scrollTop = obj.scrollTop; obj.value = obj.value.substring(0, startPos) + str + obj.value.substring(endPos, obj.value.length); obj.focus(); obj.selectionStart = startPos + str.length; obj.selectionEnd = startPos + str.length; obj.scrollTop = scrollTop; e.preventDefault(); }; var keyDown = function (e) { var evt = e || window.event; var key = evt.keyCode ? evt.keyCode : evt.which; if (key == options.changeLanguageKey) { lang = (lang == 'en') ? 'fa' : 'en'; return true; } }; var fixYeKeHalfSpace = function (key, evt) { var originalKey = key; var arabicYeCharCode = 1610; var persianYeCharCode = 1740; var arabicKeCharCode = 1603; var persianKeCharCode = 1705; var halfSpace = 8204; switch (key) { case arabicYeCharCode: key = persianYeCharCode; break; case arabicKeCharCode: key = persianKeCharCode; break; } if (evt.shiftKey && key == 32) { key = halfSpace; } if (originalKey != key) { substituteChar(key, evt); } }; var keyPress = function (e) { if (lang != 'fa') return; var evt = e || window.event; var key = evt.keyCode ? evt.keyCode : evt.which; fixYeKeHalfSpace(key, evt); var isNotArrowKey = (evt.charCode != 0) && (evt.which != 0); if (isNotArrowKey && (key > 38) && (key < 123)) { var pCode = (keys[key - 39]) ? (keys[key - 39]) : key; substituteChar(pCode, evt); } } return this.each(function () { var input = $(this); input.keypress(function (e) { keyPress(e); }); input.keydown(function (e) { keyDown(e); }); }); }; })(jQuery); // ]]>
<html> <head> <title>تکست باکس فارسی</title> <script type="text/javascript" src="jquery-1.9.1.min.js"></script> <script type="text/javascript" src="jquery.farsiInput.js"></script> <style type="text/css"> input, textarea { font-family: tahoma; font-size: 9pt; } </style> </head> <body> <input dir="rtl" id='text1' /> <br /> <textarea dir="rtl" id='text2' rows="15" cols="84"></textarea> <script type="text/javascript"> $(function () { $("#text1, #text2").farsiInput(); }); </script> </body> </html>
دریافت کدهای کامل افزونه farsiInput
farsi_input.zip
ASP.NET MVC #16
مدیریت خطاها در یک برنامه ASP.NET MVC
استفاده از فیلتر HandleError
یکی از فیلترهای توکار ASP.NET MVC به نام HandleError، میتواند کار هدایت کاربر را به یک صفحهی خطای عمومی، در حین بروز استثنایی در برنامه، انجام دهد. برای آزمایش آن یک برنامه خالی جدید ASP.NET MVC را آغاز کنید. سپس یک کنترلر جدید را با محتوای زیر به آن اضافه نمائید:
using System;
using System.Web.Mvc;
namespace MvcApplication13.Controllers
{
public class HomeController : Controller
{
[HandleError]
public ActionResult Index()
{
throw new InvalidOperationException();
return View();
}
}
}
در اینجا جهت آزمایش برنامه، به عمد یک استثنای دستی را صادر میکنیم. برای آزمایش برنامه هم نیاز است آنرا خارج از دیباگر VS.NET اجرا کرد (آدرس برنامه را مستقیما خارج از VS.NET در یک مرورگر وارد کنید). همچنین یک سطر زیر را نیز لازم است به فایل web.config برنامه اضافه نمائید:
<system.web>
<customErrors mode="On" />
اکنون اگر برنامه را خارج از مرورگر اجرا کنید، با توجه به استفاده از ویژگی HandleError و همچنین بروز یک استثنا در متد Index، خودبخود صفحه Views\Shared\Error.cshtml به کاربر نمایش داده خواهد شد. در غیراینصورت صفحه زرد رنگ پیش فرض خطای ASP.NET به کاربر نمایش داده میشود که محتوای آنها بیشتر برای برنامه نویسها مناسب است و نه کاربران نهایی سیستم.
اگر علاقمند باشید که این ویژگی به صورت خودکار به تمام متدهای کنترلرهای برنامه اعمال شود، کافی است یک سطر زیر را به متد Application_Start فایل Global.asax.cs اضافه نمائید:
GlobalFilters.Filters.Add(new HandleErrorAttribute());
البته نیازی به انجام اینکار نیست زیرا اگر به متد RegisterGlobalFilters فایل Global.asax.cs دقت کنیم، اینکار پیشتر توسط قالب پیش فرض VS.NET انجام شده است. فقط برای فعال سازی آن نیاز است تگ customErrors در فایل وب کانفیگ برنامه مقدار دهی و تنظیم شود.
استفاده از صفحه خطای سفارشی دیگری بجای فایل Error.cshtml
امکان تنظیم نمایش صفحه خطای سفارشی دیگری نیز وجود دارد. برای مثال استفاده از فایل Views\Shared\CustomErrorView.cshtml :
[HandleError(View = "CustomErrorView")]
استفاده از صفحات خطای متفاوت به ازای استثناهای مختلف
میتوان فیلتر HandleError را تنها به یک نوع استثنای خاص محدود کرد. همچنین امکان استفاده از چندین ویژگی HandleError برای یک متد نیز وجود دارد:
[HandleError(ExceptionType = typeof(NullReferenceException), View = "ErrorHandling")]
دسترسی به اطلاعات استثناء در صفحه نمایش خطاها
زمانیکه برنامه به صفحه خطا هدایت میشود، نوع Model آن System.Web.Mvc.HandleErrorInfo میباشد:
@model System.Web.Mvc.HandleErrorInfo
@{
ViewBag.Title = "DbError";
}
<h2>An Error Has Occurred</h2>
@if (Model != null)
{
<p>@Model.Exception.GetType().Name<br />
thrown in @Model.ControllerName @Model.ActionName</p>
}
البته این نکته را صرفا به عنوان اطلاعات عمومی در نظر داشته باشید. زیرا اگر قرار باشد مجددا اصل استثناء را نمایش دهیم، همان صفحه زرد رنگ ASP.NET شاید بهتر باشد.
استفاده از تگ customErrors در فایل Web.config برنامه
ویژگی حالت تگ customErrors در فایل web.config برنامه، سه مقدار را میتواند بپذیرد:
الف) Off : صفحه زرد رنگ معرفی خطای ASP.NET را به همراه تمام اطلاعات مرتبط با استثنای رخ داده نمایش میدهد.
ب) RemoteOnly : همان حالت الف است با این تفاوت که صفحه خطا را فقط در کامپیوتری که وب سرور بر روی آن نصب است نمایش خواهد داد.
ج) On : یک صفحه خطای سفارشی شده را نمایش میدهد.
بنابراین هیچگاه از حالت Off استفاده نکنید. زیرا خطاهای نمایش داده شده، علاوه بر برنامه نویس، برای مهاجم به یک سایت نیز بسیار دلپذیر است!
حالت RemoteOnly در زمان توسعه برنامه توصیه میشود.
حالت On حین توزیع برنامه باید بکارگرفته شود.
مدیریت خطاهای رخ داده خارج از MVC Pipeline
HandleErrorAttribute تنها استثناهای رخ داده داخل ASP.NET MVC Pipeline را مدیریت میکند (یا خطاهایی از نوع 500). اگر این نوع استثناها خارج از آن رخ دهند مثلا فایلی یافت نشود (خطای 404) و امثال آن، باید به روش زیر عمل کرد:
<customErrors mode="On" defaultRedirect="error">
<error statusCode="404" redirect="error/notfound" />
<error statusCode="403" redirect="error/forbidden" />
</customErrors>
در اینجا اگر فایلی یافت نشد، کاربر به کنترلری به نام error و متدی به نام notfound هدایت خواهد شد. بنابراین نیاز به کنترلر زیر وجود دارد؛ به علاوه به ازای هر متد هم یک View متناظر باید اضافه شود (کلیک راست روی نام متد و انتخاب گزینه افزودن View جدید).
using System.Web.Mvc;
namespace MvcApplication13.Controllers
{
public class ErrorController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult NotFound()
{
return View();
}
public ActionResult Forbidden()
{
return View();
}
}
}
برای آزمایش این قسمت، برنامه را اجرا کرده و سپس مثلا آدرس غیرموجود http://localhost/xyz را وارد کنید.
استفاده از فیلتر HandleError اجباری نیست
در همین قسمت قبل پس از افزودن customErrors و defaultRedirect آن که به نام یک کنترلر اشاره میکند، کلیه فیلترهای HandleError اضافه شده به برنامه را حذف کنید. سپس برنامه را خارج از محیط VS.NET اجرا کنید. باز هم متد Index کنترلر Error اجرا خواهد شد. به عبارتی الزاما نیازی به استفاده از فیلتر HandleError نیست و به کمک مقدار دهی صحیح تگ customErrors، کار نمایش خودکار صفحه سفارشی خطاها به کاربر انجام خواهد شد.
البته بدیهی است که گزینههای نمایش یک View خاص به ازای استثنایی ویژه، یکی از مزیتهای استفاده از فیلتر HandleError میباشد که امکان تنظیم آن در فایل web.config وجود ندارد.
ثبت اطلاعات استثناهای رخ داده به کمک ELMAH
نمایش صفحهی خطای سفارشی به کاربر، یکی از موارد ضروری تمام برنامههای ASP.NET است، اما کافی نیست. ثبت اطلاعات جزئیات استثناهای رخ داده در طول زمان میتوانند به بالا بردن کیفیت برنامه به شدت کمک کنند. برای این منظور میتوان همانند سابق از متد Application_Error قابل تعریف در فایل Global.asax.cs کمک گرفت؛ اما با وجود افزونهای به نام ELMAH اینکار اتلاف وقت است و اصلا توصیه نمیشود. همچنین به کمک ELMAH میتوان مشکلات را تبدیل به ایمیلهای خودکار کرد یا از آنها فید RSS درست نمود.
برای دریافت ELMAH یا به سایت اصلی آن مراجعه نمائید و یا به کمک NuGet هم به سادگی قابل دریافت است. پس از دریافت، ارجاعی را به اسمبلی آن (Elmah.dll) اضافه نمائید. در ادامه فایل web.config برنامه را گشوده و چند سطر زیر را به آن در قسمت configuration اضافه کنید:
<configuration>
<configSections>
<sectionGroup name="elmah">
<section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah"/>
<section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah"/>
<section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah"/>
<section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah"/>
<section name="errorTweet" requirePermission="false" type="Elmah.ErrorTweetSectionHandler, Elmah"/>
</sectionGroup>
</configSections>
سپس ذیل قسمت appSettings، تنظیمات پروایدر ذخیره سازی اطلاعات آنرا وارد نمائید. مثلا در اینجا از فایلهای XML برای ذخیره سازی اطلاعات استفاده خواهد شد (که امنترین حالت ممکن است؛ از این لحاظ که اگر بانک اطلاعاتی را انتخاب کنید، ممکن است مشکل اصلی از همانجا ناشی شده باشد. بنابراین خطایی ثبت نخواهد شد. همچنین در این حالت نیازی به سایر DLLهای همراه ELMAH هم نیست). در اینجا مسیر ذخیره سازی اطلاعات در پوشه app_data/errorslog تنظیم شده است:
<elmah>
<security allowRemoteAccess="1"/>
<errorLog type="Elmah.XmlFileErrorLog, Elmah" logPath="~/App_Data/ErrorsLog"/>
</elmah>
در ادامه در قسمت system.web، دو تعریف زیر را اضافه نمائید. به این ترتیب امکان دسترسی به آدرس http://server/elmah.axd مهیا میگردد:
<httpModules>
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah"/>
</httpModules>
<httpHandlers>
<add verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah"/>
</httpHandlers>
البته برای IIS7 تنظیمات ذیل نیز باید اضافه شوند:
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="true">
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah"/>
</modules>
<handlers>
<add name="Elmah" verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah"/>
</handlers>
</system.webServer>
و به این ترتیب تنظیمات اولیه ELMAH به پایان میرسد (و با ASP.NET Web forms هیچ تفاوتی ندارد).
مرحله بعد، تنظیمات مسیریابی ASP.NET MVC است برای اینکه آدرس http://server/elmah.axd را وارد سیستم پردازشی خود نکند. البته اینکار پیشتر انجام شده است:
public static void RegisterRoutes(RouteCollection routes)
{
//routes.IgnoreRoute("elmah.axd");
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
بنابراین همین تنظیمات، به همراه قالب پیش فرض یک پروژه جدید ASP.NET MVC برای استفاده از ELMAH کفایت میکند. اکنون پروژه جاری را یکبار دیگر خارج از VS.NET اجرا کرده و سپس به مسیر http://localhost/elmah.axd جهت مشاهده خطاهای لاگ شده به همراه جزئیات کامل آنها مراجعه کنید.
مشکل: استثناهای برنامه توسط ELMAH لاگ نمیشوند!
فیلتر HandleError با ELMAH سازگار نیست. زیرا با استفاده از آن، متدهای کنترلرها به صورت خودکار داخل یک try/catch اجرا شده و به این ترتیب استثناهای رخ داده، مدیریت گردیده و به ELMAH هدایت نمیشوند. بنابراین نیاز است به متد RegisterGlobalFilters فایل Global.asax.cs مراجعه کرده و سطر زیر را حذف کنید:
filters.Add(new HandleErrorAttribute());
و یا اگر قصد نداشتید اینکار را انجام دهید، میتوان به نحو زیر نیز مشکل را حل کرد:
using System.Web.Mvc;
using Elmah;
namespace MvcApplication13.CustomFilters
{
public class ElmahHandledErrorLoggerFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
if (context.ExceptionHandled)
ErrorSignal.FromCurrentContext().Raise(context.Exception);
// all other exceptions will be caught by ELMAH anyway
}
}
}
در اینجا یک فیلتر سفارشی به برنامه اضافه شده است تا خطاهای مدیریت شده برنامه (خطاهای مدیریت شده توسط فیلتر HandleError توکار) را به موتور ELMAH هدایت کند. سایر خطاهای مدیریت نشده به صورت خودکار توسط ELMAH ثبت خواهند شد و نیازی به انجام کار اضافی در این مورد نیست.
سپس این فیلتر جدید را به صورت سراسری تعریف کنید:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new ElmahHandledErrorLoggerFilter());
filters.Add(new HandleErrorAttribute());
}
ترتیب اینها هم مهم است. ابتدا باید ElmahHandledErrorLoggerFilter معرفی شود.
تذکر مهم!
حین استفاده از ELMAH یک نکته را فراموش نکنید:
اگر allowRemoteAccess آنرا به عدد 1 تنظیم کردهاید، به هیچ عنوان از نام پیش فرض elmah.axd استفاده نکنید (هر نام اختیاری دیگری را که علاقمند بودید و به سادگی قابل حدس زدن نبود، در فایل web.config وارد کنید).
خلاصه بحث
1- در ASP.NET MVC نیازی نیست تا متدهای کنترلرها را با try/catch شلوغ کنید.
2- حتما قسمت customErrors فایل وب کانفیگ برنامه را دهی کنید (این مورد را به چک لیست اجباری تهیه یک برنامه ASP.NET MVC اضافه کنید).
3- استفاده از فیلتر HandleError اختیاری است. اگر از قابلیت فیلتر کردن استثناهای ویژه آن استفاده نمیکنید، مقدار دهی customErrors وب کانفیگ برنامه هم همان کار را انجام میدهد.
4- برای ثبت جزئیات دقیق استثناهای رخ داده در برنامه، از ELMAH استفاده کنید و بیجهت وقت خودتان را صرف بازنویسی این افزونه ارزشمند نکنید.
مطالب مشابه
معرفی ELMAH
ثبت استثناهای مدیریت شده توسط ELMAH