ASP.NET MVC #20
@RenderPage("~/views/path/_partial_view.cshtml", new { Employee = grid.SelectedRow })
mainReport.Pages[0].Enabled = false; mainReport.Pages["PageName"].Enabled = false;
var mrtName = ""; object businessObject = new object(); if (cusomerReportCondition) { mrtName = "CustomersReport.mrt"; businessObject = getCustomerReportData(); } else if (productsReportCondition) { mrtName = "ProductsReport.mrt"; businessObject = getProductsReportData(); } //else if ... mainReport.Load(mrtName); mainReport.RegBusinessObject("businessObjectName", businessObject);
WebStorage
- مکانیزم ذخیره سازی:
- چند نسخه از مرورگر
- محدودیت حجمی
- session storage
- local storage
- SessionStorage
- LocalStorage
با اینکه توصیه نامه W3C از پایان کار پیاده سازی این قابلیت خبر میدهد ولی در حال حاضر که این مقاله تدوین شده است هنوز نهایی اعلام نشده است. برای پشتیبانی مرورگرهای قدیمی از webstorage میتوان از فایل جاوااسکریپتی Store.js کمک گرفت.
مفاهیم امنیتی و محافظت از داده ها
محدودیتهای حمایتی و حفاظتی webstorage دقیقا همانند کوکی هاست. به این معنی که وب سایتهای دیگر توانایی اتصال به webstorage سایت دیگری را ندارند. البته این مورد ممکن است برای وب سایت هایی که بر ساب دومین تکیه کردهاند ایجاد مشکل کند. برای حل این مسائل میتوانید از کتابخانههای سورس بازی چون Cross Storage که توسط Zendesk ارائه شده است، استفاده کرد.
همانند هر مکانیزم ذخیره سازی سمت کلاینت، مواردی توصیه میگردد که رعایت آنها از لحاظ امنیتی پر اهمیت است. به عنوان نمونه ذخیرهی اطلاعات شخصی و موارد حساس توصیه نمیگردد؛ چرا که احتمال دسترسی آسان نفوذگران به دادههای محلی و خواندن آنها وجود دارد.
Data Integrity یا یکپارچگی دادهها نیز در نظر گرفته شده است. باید حفاظتی در برابر عدم موفقیت ذخیره سازی دادهها نیز وجود داشته باشد. این عدم موفقیتها میتواند به دلایل زیر رخ دهد:
- اگر کاربر قابلیت webstorage را غیرفعال کرده باشد.
- اگر فضایی برای کاربر باقی نمانده باشد.
- با محدودیت حجمی webstorage مواجه شده است.
- با مواجه شدن با خطاها یک استثنا صادر میشود که میتوانید آن را دریافت و کنترلی را روی برنامه تحت وب داشته باشید. یک نمونه استثنا QuotaExceededError
IndexedDB
یکی از فرایندهای ذخیره سازی دادهها که همان مزایای webstorage را ارائه میدهد indexed Database API است. این قابلیت از HTML 5 اضافه شده است و قسمتی از مشخصات webstorage شناخته نمیشود. برای همین مستنداتی در حوزهی webstorage برای آن پیدا نخواهید کرد ولی قابلیتهایی فراتر از webstorage دارد.
این قابلیت پیچیدگی بیشتری را نسبت به خود webstorage ایجاد میکند، ولی فرصتهای بسیاری را برای ذخیره سازی دادههایی با معماریهای پیچیدهتر و رابطهها را میدهد. با استفاده از IndexedDB دادهها به شکل دیتابیسهای سمت سرور RDMS ذخیره میشوند و این قابلیت را دارید که به سمت آن کوئری هایی مشابه بانکهای اطلاعاتی سمت سرور را ارسال کنید.
در قسمت آتی نحوه کدنویسی آن را فرا خواهیم گرفت.
مشکل با نوشتن تابع تجمعی سفارشی(از طریق پیاده سازی IAggregateFunction)
public object ProcessingBoundary(IList<SummaryCellData> columnCellsSummaryData) { if (columnCellsSummaryData == null || !columnCellsSummaryData.Any()) return 0; var list = columnCellsSummaryData; var lastItem = list.Last(); return lastItem.CellData.PropertyValue; }
Method 'set_DisplayFormatFormula' in type 'Hezareh.Modules.Accounting.Reporting.ViewModels.MySampleAggregateFunction' from assembly 'Hezareh.Modules.Accounting, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
public class MySampleAggregateFunction : IAggregateFunction { public MySampleAggregateFunction() { } /// <summary> /// Fires before rendering of this cell. /// Now you have time to manipulate the received object and apply your custom formatting function. /// It can be null. /// </summary> public Func<object, string> DisplayFormatFormula { set; get; } #region Fields (6) double _groupAvg; long _groupRowNumber; double _groupSum; double _overallAvg; long _overallRowNumber; double _overallSum; #endregion Fields #region Properties (2) /// <summary> /// Returns current groups' aggregate value. /// </summary> public object GroupValue { get { return _groupAvg; } } /// <summary> /// Returns current row's aggregate value without considering the presence of the groups. /// </summary> public object OverallValue { get { return _overallAvg; } } #endregion Properties #region Methods (4) // Public Methods (1) /// <summary> /// Fires after adding a cell to the main table. /// </summary> /// <param name="cellDataValue">Current cell's data</param> /// <param name="isNewGroupStarted">Indicated starting a new group</param> public void CellAdded(object cellDataValue, bool isNewGroupStarted) { checkNewGroupStarted(isNewGroupStarted); _overallRowNumber++; _groupRowNumber++; double cellValue; if (double.TryParse(cellDataValue.ToSafeString(), NumberStyles.AllowThousands | NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out cellValue)) { groupAvg(cellValue); overallAvg(cellValue); } } // Private Methods (3) private void checkNewGroupStarted(bool newGroupStarted) { if (newGroupStarted) { _groupRowNumber = 0; _groupAvg = 0; _groupSum = 0; } } private void groupAvg(double cellValue) { _groupSum += cellValue; _groupAvg = _groupSum / _groupRowNumber; } private void overallAvg(double cellValue) { _overallSum += cellValue; _overallAvg = _overallSum / _overallRowNumber; } /// <summary> /// A general method which takes a list of data and calculates its corresponding aggregate value. /// It will be used to calculate the aggregate value of each pages individually, with considering the previous pages data. /// </summary> /// <param name="columnCellsSummaryData">List of data</param> /// <returns>Aggregate value</returns> public object ProcessingBoundary(IList<SummaryCellData> columnCellsSummaryData) { if (columnCellsSummaryData == null || !columnCellsSummaryData.Any()) return 0; var list = columnCellsSummaryData; var lastItem = list.Last(); return lastItem.CellData.PropertyValue; } #endregion Methods }
columns.AddColumn(column => { column.PropertyName<VoucherRowPrintViewModel>(x => x.CaclulatedRemains); column.CellsHorizontalAlignment(PdfRpt.Core.Contracts.HorizontalAlignment.Right); column.IsVisible(true); column.Order(5); column.Width(1.5f); column.ColumnItemsTemplate(template => { template.TextBlock(); template.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj)); }); column.AggregateFunction(aggregateFunction => { aggregateFunction.CustomAggregateFunction(new MySampleAggregateFunction()); aggregateFunction.DisplayFormatFormula(obj => obj == null ? string.Empty : string.Format("{0:n0}", obj)); }); column.HeaderCell("مانده"); });
سوال: چگونه این فایل را در Jcenter آپلود کنیم؟
فرآیندی که در این نوشتار قصد داریم دنبال شود شامل مراحل زیر است:
ابتدا کتابخانهی خودمان را روی جی سنتر قرار داده و در صورتیکه علاقه داشته باشیم، آن را به mavenCentral هم انتقال میدهیم.
ابتدا نیاز است در سایت bintray ثبت نام کنید و با حساب جدید وارد شوید و گزینهی maven را انتخاب کنید.
سپس روی گزینهی Add New Package کلیک کنید تا یک پکیج جدید را ایجاد کنیم.
در صفحهای که باز میشود، اطلاعات مربوط به این پکیج را وارد کنید که عموما شامل نام پکیج، مجوز آن، کلمات کلیدی، لینک گزارش باگ و .. میشود. در انتخاب نام پکیج، قانون اجباری یا خاصی وجود ندارد؛ ولی توصیه میشود که از حروف کوچک و - استفاده گردد. بعد از پرکردن فیلدهای الزامی، وارد صفحهی جزئیات پکیج میشوید که در آن فیلدهای اضافهتری نیز وجود دارند که میتوانید در صورت تمایل آنها را پر کنید. همچنین در بالای صفحه لینک به صفحهی اختصاصی این پکیج نیز وجود دارد که در زیر عبارت Edit Package قرار گرفته است.
پی نوشت : اگر قصد آپلود کتابخانهی خود را در این سایت ندارید، میتوانید این سوال و مرحلهی امضای خودکار را از مراحل کاری خود حذف کنید.
سوال: چگونه این فایل را در SonaType آپلود کنیم؟
گام اول: ابتدا باید در سایت ثبت نام کنید. پس به این صفحه رفته و ثبت نام کنید. سپس در یک مرحلهی غیرمنطقی باید یک issue توسط سیستم JIRA ایجاد کنید. برای همین گزینهی Creare را در بالای صفحه بزنید. اطلاعات زیر را به ترتیب پر کنید:
Project: Community Support - Open Source Project Repository Hosting Issue Type: New Project Summary: مثلا نام پروژه خودتان را بنویسید یک نام پکیج که سعی کنید کتابخانههای هم خانواده این اشتراک را داشته باشند که در یک گروه قرار بگیرند Group Id: AndroidBreadCrumb.Plus آدرس جایی که پروژه قرار دارد Project URL: https://github.com/yeganehaym/AndroidBreadCrumb //آدرس سیستم کنترل نسخه SCM url: https://github.com/yeganehaym/AndroidBreadCrumb
فعال سازی امضای خودکار در Bintray
همانطور که در ابتدای مقاله گفتیم، میخواهیم کتابخانهی خود را از طریق jcenter به maven ارسال کنیم. برای همین نیاز داریم که ابتدا کتابخانهی خود را امضا کنیم. برای اینکار باید از طریق GPG یک کلید بسازیم. ساخت کلید به این شیوه، قبلا در مقالهی «ساخت کلیدهای امنیتی با GnuPG» توضیح داده شد و از تکرار آن خودداری میکنیم. تنها به ذکر این نکته بسنده میکنیم که شما باید یک کلید ساخته و آن را به سرور کلیدها ارسال کنید و سپس کلید متنی عمومی و خصوصی آن را در پروفایل bintray برگهی GPG Signing درج کنید.
این تنظیم از این پس بر روی تمامی کتابخانهها اعمال میشود.
سوال : چگونه پروژهی اندرویدی خودم را کامپایل کنم؟
فایل build.gradle پروژه را باز کنید و پلاگین bintray را به آن معرفی کنید:
dependencies { classpath 'com.android.tools.build:gradle:1.2.2' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2' classpath 'com.github.dcendents:android-maven-plugin:1.2' }
bintray.user=YOUR_BINTRAY_USERNAME bintray.apikey=YOUR_BINTRAY_API_KEY bintray.gpg.password=YOUR_GPG_PASSWORD
در مرحلهی بعدی خطوط زیر را بعد از 'Apply Plugin 'com.android.library اضافه کنید و اطلاعاتی که در bintray وارد کردهاید را در اینجا وارد کنید:
apply plugin: 'com.android.library' ext { bintrayRepo = 'maven' bintrayName = 'AndroidBreadCrumb' publishedGroupId = 'com.plus' libraryName = 'AndroidBreadCrumb' artifact = 'AndroidBreadCrumb' libraryDescription = 'create breadcrumb on android to show a path to user and let user to jump on them' siteUrl = 'https://github.com/yeganehaym/AndroidBreadCrumb' gitUrl = 'https://github.com/yeganehaym/AndroidBreadCrumb' libraryVersion = '1.0' developerId = 'yeganehaym' developerName = 'ali yeganeh.m' developerEmail = 'yeganehaym@gmail.com' licenseName = 'The Apache Software License, Version 2.0' licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' allLicenses = ["Apache-2.0"] }
apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle'
compile 'com.plus:AndroidBreadCrumb:1.0'
آپلود فایلها به مخزن
برای آپلود فایلهای ماژول به مخزن، ابتدا ترمینال اندروید استودیو را باز کنید و گامهای زیر را به ترتیب انجام بدهید:
گام اول: با ارسال دستور زیر از صحت کدها و منابع مطمئن میشویم:
gradlew install
BUILD SUCCESSFUL
gradlew bintrayUpload
SUCCESSFUL
حال صفحهی اختصاصی پکیجتان را چک کنید. میبینید که قسمتهایی از آن تغییر کردهاست و قسمت نسخه، به روز شده است:
و قسمت فایلها هم دیگر خالی نیست:
با اینکه کتابخانهی ما روی maven قرار گرفت، ولی هنوز نمیتوان آن را توسط jcenter استفاده کرد و باید bintray maven را با jcenter هماهنگ نماییم. در حال حاضر استفاده از این کتابخانه بدون سینک به شکل زیر است:
گریدل پروژه maven{ url 'https://dl.bintray.com/yeganehaym/maven' } گریدل ماژول dependencies { compile 'com.plus:AndroidbreadCrumb:1.0' }
برای افزودن کتابخانهی خود به سیستم jcenter با کلیک بر روی گزینهی Add to jcenter میتوانید به تیم jcenter درخواست دهید که آن را تایید کنند که بعد از درخواست حدود سه ساعت طول میکشد تا پاسخ شما را بدهند.
به این ترتیب دیگر نیازی به تعریف یک url به maven نخواهد بود.
برای دیدن این کتابخانه در صفحه jcenter به ترتیب شناسههای Group_ID.Artifact.version را دنبال کنید، یعنی برای ما میشود:
com/plus/androidbreadcrumb/1.0
نکته دوم: در صورتی که پکیج خودتان را حذف کنید، چیزی از روی jcenter حذف نمیشود. فقط به یاد داشته باشید که برای حذف آن باید ابتدا نسخههای مختلف آپلود شده را حذف کنید تا پکیج از جی سنتر هم حذف شود.
در این مرحله قصد داریم که این کتابخانه را بر روی mavenCentral هم داشته باشیم. اگر قصدش را ندارید از اینجا به بعد را نیازی نیست انجام بدهید و برای اینکار لازم است همهی مراحل بالا انجام گرفته باشد.
قبل از اینکه این عمل ارسال انجام گیرد، باید دو عمل زیر از قبل صورت گرفته باشند:
- پکیج شما در jcenter تایید شده باشد.
- با مخزن شما در sonatype موافقت شده باشد.
در صورتیکه دو مرحلهی بالا صورت گرفته باشند، در صفحهی پکیج اختصاصی، بر روی گزینهی mavenCentral کلیک کنید:
پس از آن باید نام کاربری و کلمهی عبورتان را در SonaType، وارد کنید و گزینهی sync را بفشارید:
در صورتیکه پیام موفقیت در سینک را بدهد، پکیج شما منتقل شدهاست. در غیر این صورت خطای آن را اعلام میکند و باید برای رفع آن تلاش کنید تا خطاها از بین بروند. برای اینکه بتوانید این پکیج را در لیست mavenCentral ببینید، مثل همان چیزی که در بالاتر گفته شد، شناسهی گریدل را دنبال کنید.
SELECT DEPARTMENTNAME, GENDER, COUNT(1) AS COUNT FROM DBO.DIMEMPLOYEE GROUP BY DEPARTMENTNAME,GENDER ORDER BY DEPARTMENTNAME,GENDER
SELECT EMPLOYEEKEY,FIRSTNAME,LASTNAME, MIDDLENAME,TITLE,HIREDATE, BIRTHDATE,EMAILADDRESS,PHONE,GENDER FROM DBO.DIMEMPLOYEE WHERE DEPARTMENTNAME=@DEPARTMENTNAME AND GENDER=@GENDER
در این بین راه حل دیگری نیز وجود دارد که با تمام مرورگرها سازگار است؛ اما تنها گزارش درصد آپلود را توسط آن نخواهیم داشت. در اینجا به صورت پویا یک IFrame مخفی در صفحه تشکیل میشود، مقادیر معمولی فرم (تمام المانها، منهای file) به صورت Ajax ایی به سرور ارسال خواهند شد. المان file آن در این IFrame مخفی، به صورت معمولی به سرور Postback میشود. البته کاربر در این بین چیزی را مشاهده یا احساس نخواهد کرد و تمام عملیات از دیدگاه او Ajax ایی به نظر میرسد. برای انجام اینکار تنها کافی است از افزونهی AjaxFileUpload استفاده کنیم که در ادامه نحوهی استفاده از آنرا بررسی خواهیم کرد.
پیشنیازها
در ادامه فرض بر این است که افزونهی AjaxFileUpload را دریافت کرده و به فایل Layout برنامه افزودهاید:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>@ViewBag.Title - My ASP.NET Application</title> <link href="~/Content/Site.css" rel="stylesheet" type="text/css" /> </head> <body> <div> @RenderBody() </div> <script src="~/Scripts/jquery-1.11.1.min.js"></script> <script src="~/Scripts/jquery.unobtrusive-ajax.js"></script> <script src="~/Scripts/ajaxfileupload.js"></script> @RenderSection("Scripts", required: false) </body> </html>
مدل، کنترلر و View برنامه
مدل برنامه مشخصات یک محصول است:
namespace MVCAjaxFormUpload.Models { public class Product { public int Id { set; get; } public string Name { set; get; } } }
کنترلر آن از سه متد تشکیل شدهاست:
using System.Threading; using System.Web; using System.Web.Mvc; using MVCAjaxFormUpload.Models; namespace MVCAjaxFormUpload.Controllers { public class HomeController : Controller { public ActionResult Index() { return View(); } [HttpPost] public ActionResult Index(Product product) { var isAjax = this.Request.IsAjaxRequest(); return Json(new { result = "ok" }, JsonRequestBehavior.AllowGet); } [HttpPost] public ActionResult UploadFiles(HttpPostedFileBase image1, int id) { var isAjax = this.Request.IsAjaxRequest(); Thread.Sleep(3000); //شبیه سازی عملیات طولانی return Json(new { FileName = "/Uploads/filename.ext" }, "text/html", JsonRequestBehavior.AllowGet); } } }
Index دوم کار پردازش Ajax ایی اطلاعات ارسالی به سرور را به عهده دارد. HttpPost آن Ajax ایی است.
متد UploadFiles، کار پردازش اطلاعات ارسالی از طرف IFrame مخفی را انجام میدهد. HttpPost آن معمولی است.
و کدهای View این مثال نیز به شرح زیر است:
@model MVCAjaxFormUpload.Models.Product @{ ViewBag.Title = "Index"; } <h2>Ajax Form Upload</h2> @using (Ajax.BeginForm(actionName: "Index", controllerName: "Home", ajaxOptions: new AjaxOptions { HttpMethod = "POST" }, routeValues: null, htmlAttributes: new { id = "uploadForm" })) { <label>Name:</label> @Html.TextBoxFor(model => model.Name) <br /> <label>Image:</label> <br /> <input type="file" name="Image1" id="Image1" /> <br /> <input type="submit" value="Submit" /> <img id="loading" src="~/Content/Images/loading.gif" style="display:none;"> } @section Scripts { <script type="text/javascript"> $(function () { $('#uploadForm').submit(function () { $("#loading").show(); $.ajaxFileUpload({ url: "@Url.Action("UploadFiles", "Home")", // مسیری که باید فایل به آن ارسال شود secureuri: false, fileElementId: 'Image1', // آی دی المان ورودی فایل dataType: 'json', data: { id: 1, data: 'test' }, // اطلاعات اضافی در صورت نیاز success: function (data, status) { $("#loading").hide(); if (typeof (data.FileName) != 'undefined') { alert(data.FileName); } }, error: function (data, status, e) { $("#loading").hide(); alert(e); } }); }); }); </script> }
در ادامه نحوهی فعال سازی ajaxFileUpload را دقیقا در زمان submit فرم، مشاهده میکنید. در اینجا url آن به اکشن متدی که اطلاعات المان file را باید دریافت کند، اشاره میکند. fileElementId آن مساوی Id المان فایل فرم Ajax ایی صفحهاست. از قسمت data جهت ارسال اطلاعات اضافهتری به اکشن متد UploadFiles استفاده میشود. سایر قسمتهای آن نیز مشخص هستند. اگر عملیات موفقیت آمیز بود، success آن و اگر خیر، error آن اجرا میشوند.
فقط باید دقت داشت که content type دریافتی توسط آن باید text/html باشد، که این مورد در اکشن متدهای کنترلر مشخص هستند.
به این ترتیب دیگر کاربر نیازی ندارد ابتدا یکبار بر روی دکمهی دومی کلیک کرده و فایل را ارسال کند و سپس بار دیگر بر روی دکمهی submit فرم کلیک نماید. هر دو کار توسط یک دکمه انجام میشوند.
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید
MVCAjaxFormUpload.zip
معرفی ELMAH
عموما کاربران نمیتوانند گزارش خطای خوبی را ارائه بدهند و البته انتظاری هم از آنان نیست. تنها گزارشی که از یک کاربر دریافت میکنید این است: "برنامه کار نمیکنه!" و همین!
روشهای متعددی برای لاگ کردن خطاهای یک برنامه ASP.Net موجود است؛ چه خودتان آنها را توسعه دهید و یا از ASP.NET health monitoring استفاده کنید.
روش دیگری که این روزها در وبلاگهای متعددی در مورد آن مطلب منتشر میشود، استفاده از ELMAH است. (البته ELMAH به تازگی منتشر نشده ولی تا کیفیت محصولی به عموم ثابت شود مدتی زمان میبرد)
ELMAH یک ماژول رایگان و سورس باز لاگ کردن خطاهای مدیریت نشده برنامههای ASP.Net است. برای استفاده از این ماژول نیازی نیست تا تغییری در برنامه خود ایجاد کنید یا حتی آنرا کامپایل مجدد نمائید. یک فایل dll دارد به همراه کمی تغییر در web.config برنامه جهت معرفی آن و این تمام کاری است که برای برپایی آن لازم است صورت گیرد. این ماژول تمامی خطاهای مدیریت نشدهی برنامه شما را لاگ کرده (در حافظه سرور، در یک فایل xml ، در یک دیتابیس اس کیوال سرور یا اوراکل ، در یک دیتابیس اکسس و یا در یک دیتابیس اس کیوال لایت) و برای مرور آنها یک صفحهی وب سفارشی یا فیدی مخصوص را نیز در اختیار شما قرار میدهد. همچنین این قابلیت را هم دارد که به محض بروز خطایی یک ایمیل را نیز به شما ارسال نماید.
با توجه به اینکه این ماژول در Google code قرار گرفته احتمالا دسترسی به آن مشکل خواهد بود. سورس و فایلهای کامپایل شده آنرا از آدرسهای زیر نیز میتوان دریافت نمود:
نحوه استفاده از ELMAH :
برای استفاده از ELMAH دو کار را باید انجام دهید:
الف) کپی کردن فایل Elmah.dll در پوشه bin برنامه
ب) تنظیم وب کانفیگ برنامه
بهترین مرجع برای آشنایی با نحوه بکار گیری این ماژول، مراجعه به فایل web.config موجود در پوشه samples آن است. بر اساس این فایل نمونه:
- ابتدا باید configSections آن را به وب کانفیگ خود اضافه کنید.
- سپس تگ elmah باید اضافه شود. در این تگ موارد زیر مشخص میشوند:
الف) آیا خطاها توسط آدرس elmah.axd توسط کاربران راه دور قابل مشاهده شود یا خیر.
ب) خطاها کجا ذخیره شوند؟ موارد زیر پشتیبانی میشوند:
دیتابیسهای اس کیوال سرور ، اوراکل ، حافظه سرور، فایلهای xml ، دیتابیس SQLite ، دیتابیس اکسس و یا دیتابیسی از نوع VistaDB
ج) آیا خطاها ایمیل هم بشوند؟ اگر بلی، تگ مربوطه را تنظیم کنید.
د) آیا خطاها به اکانت twitter شما نیز ارسال شوند؟
- در ادامه تگ مربوط به معرفی این httpModules باید تنظیم شود.
- سپس httpHandlers ایی به نام elmah.axd که جهت مرور خطاها میتوان از آن استفاده نمود معرفی میگردد.
- از IIS7 استفاده میکنید؟ قسمت system.webServer را نیز باید اضافه نمائید.
- و در آخر نحوهی دسترسی به elmah.axd مشخص میشود. اگر اجازه دسترسی از راه دور را داده باشید، به این طریق میشود دسترسی را فقط به کاربران مجاز و تعیین اعتبار شده، اعطاء کرد و یا به نقشی مشخص مانند ادمین و غیره.
برای نمونه، اگر بخواهید از دیتابیس SQLite جهت ذخیره سازی خطاهای حاصل شده استفاده نمائید و نیز از ارسال ایمیل صرفنظر کنید، وب کانفیگ برنامه شما باید به شکل زیر تغییر یابد:
<?xml version="1.0"?>
<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>
<elmah>
<security allowRemoteAccess="1" />
<errorLog type="Elmah.SQLiteErrorLog, Elmah" connectionStringName="cn1" />
</elmah>
<appSettings/>
<connectionStrings>
<add name="cn1" connectionString="data source=~/ErrorsLog/Errors.db" />
</connectionStrings>
<system.web>
<httpModules>
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah"/>
</httpModules>
<httpHandlers>
<add verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
</httpHandlers>
<compilation debug="true"/>
<authentication mode="Windows" />
</system.web>
</configuration>
سادهترین تنظیم این ماژول استفاده از حالت xml است که به ازای هر خطا یک فایل xml را تولید کرده و نیاز به اسمبلی دیگری بجز ماژول مربوطه نخواهد داشت.
تذکر:
از لحاظ امنیتی مثال فوق توصیه نمیشود زیرا allowRemoteAccess آن 1 است و قسمت authorization ذکر نشده است. این مثال فقط جهت راه اندازی و آزمایش اولیه ارائه گردیده است. (همچنین بهتر است این نام پیش فرض را به نامی دیگر مثلا myloggermdl.axd تغییر داده و در قسمت httpHandlers تنظیم نمائید. سپس این نام را به تگ location نیز اضافه کنید)
اکنون برای مشاهده خروجی این ماژول به انتهای آدرس سایت خود، elmah.axd را اضافه کرده و سپس enter کنید:
همانطور که در تصویر مشخص است، تمامی خطاهای لاگ شده گزارش داده شدهاند. همچنین دو نوع فید به همراه امکان دریافت خطاها به صورت CSV نیز موجود است. با کلیک بر روی لینک details ، صفحهی بسیار ارزندهای ارائه میشود که تقریبا نحوهی وقوع ماجرا را بازسازی میکند.
نکتهی مهمی که در صفحهی جزئیات ارائه میشود (علاوه بر stack trace و مشخصات کاربر)، مقادیر تمامی فیلدهای یک صفحه هنگام بروز خطا است (قسمت Raw/Source data in XML or in JSON در این صفحه) :
به این صورت دیگر نیازی نیست از کاربر بپرسید چه چیزی را وارد کرده بودید که خطا حاصل شد. دقیقا مقادیر فیلدهای همان صفحهی زمان بروز خطا نیز برای شما لاگ میگردد.
نکته:
ماژول SQLite ایی که به همراه مجموعه ELMAH ارائه میشود 32 بیتی کامپایل شده (64 بیتی آن نیز موجود است که باید از آن در صورت لزوم استفاده شود). بنابراین برای اینکه در یک سرور 64 بیتی به مشکل برنخورید و خطای BadImageFormat را دریافت نکنید نیاز است تا به این نکته دقت داشت.
برای مطالعه بیشتر:
Error Logging Modules And Handlers
Sending ELMAH Errors Via GMail
Exception-Driven Development
Using HTTP Modules and Handlers to Create Pluggable ASP.NET Components