ارتقاء به ASP.NET Core 1.0 - قسمت 5 - فعال سازی صفحات مخصوص توسعه دهنده‌ها
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: پنج دقیقه

اولین Middleware موجود در بسته‌ی Microsoft.AspNetCore.Diagnostics را در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 3 - Middleware چیست؟» با نمایش welcome page آن، بررسی کردیم. در این مطلب سایر صفحات مخصوص توسعه دهنده‌های موجود در این بسته را مرور خواهیم کرد.



مشاهده‌ی جزئیات اطلاعات سرور و بسته‌های نصب شده‌ی بر روی آن

در نگارش‌های قبل از RTM، با فراخوانی app.UseRuntimeInfoPage در متد Configure کلاس Startup، ریز اطلاعاتی از وضعیت سرور و بسته‌های موجود در آن با مراجعه‌ی به آدرس http://site/runtimeinfo نمایش داده می‌شدند. این مورد خاص از نگارش RTM حذف شده‌است (احتمالا به دلایل امنیتی). البته اگر علاقمند به بررسی کدهای آن باشید، هنوز تاریخچه‌ی آن در GitHub موجود است .


مدیریت خطاها در برنامه‌های ASP.NET Core 1.0

به متد Configure کلاس Startup مراجعه کرد و یک سطر استثناء را به ابتدای کدهای Middleware انتهایی آن اضافه کنید:
public void Configure(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        throw new Exception("Generic Error");
        await context.Response.WriteAsync("Hello DNT!");
    });
}
هدف این است که بررسی کنیم اگر استثنایی در یک Middleware رخ داد، برنامه چه خروجی را نمایش می‌دهد.
در این حالت اگر برنامه را اجرا کنیم، این خروجی را دریافت خواهیم کرد:


و اگر به وضعیت بازگشت داده شده‌ی از طرف سرور دقت کنیم، فقط internal server error است:


در اینجا برخلاف نگارش‌های قبلی ASP.NET، دیگر حتی صفحه‌ی زرد رنگ معروف نمایش خطاها (yellow screen of death) نیز فعال نیستند. برای فعال سازی آن نیاز است Middleware مرتبط با آن‌را به نحو ذیل به برنامه معرفی کنیم:
public void Configure(IApplicationBuilder app)
{
   app.UseDeveloperExceptionPage();
پس از این فعال سازی، اگر مجددا برنامه را اجرا کنید، این خروجی را می‌توان در مرورگر مشاهده کرد:


به دلایل امنیتی و عدم نشت اطلاعات سمت سرور و خصوصا عدم امکان دیباگ از راه دور برنامه توسط مهاجمین، این Middleware به صورت پیش فرض فعال نیست.
بنابراین این سؤال مطرح می‌شود که چگونه می‌توان این صفحه را تنها در حین توسعه‌ی برنامه نمایش داد؟
پاسخ آن به نحوه‌ی طراحی متد Configure در کلاس Startup بر می‌گردد. این متد امضای ثابتی را ندارد. هر تعداد سرویسی را که نیاز داشتید، می‌توانید به عنوان پارامتر این متد معرفی کنید و کار تزریق وابستگی‌ها و نمونه سازی آن‌ها، توسط امکانات توکار ASP.NET Core به صورت خودکار انجام می‌شود. برای مثال سرویس IApplicationBuilder، یکی از سرویس‌های توکار ASP.NET Core است و برای تنظیم آن نیازی نیست تا کار خاصی را انجام دهیم. به همین جهت است که صرفا معرفی اینترفیس آن در این متد، وهله‌ای را از سازنده‌ی برنامه در اختیار ما قرار می‌دهد. سرویس‌ها را در مطلبی جداگانه مورد بررسی قرار خواهیم داد، اما فعلا جهت تکمیل بحث باید درنظر داشت که یکی دیگر از سرویس‌های توکار ASP.NET Core، به نام IHostingEnvironment، اطلاعاتی را در مورد محیطی که برنامه را در آن اجرا می‌کنیم در اختیار ما قرار می‌دهد:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
روش معرفی آن نیز همانند روش معرفی سرویس IApplicationBuilder است و تنها کافی است به عنوان یک پارامتر جدید متد Configure معرفی شود. وهله سازی و تنظیمات آن نیز به صورت خودکار توسط ASP.NET Core انجام خواهد شد. اکنون پس از تزریق این سرویس، می‌توان صفحه‌ی نمایش جزئیات خطاها را تنها محدود به محیط توسعه کرد:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
در مورد انواع محیط‌های توسعه، در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 2 - بررسی ساختار جدید Solution» در انتهای بحث به «نقش فایل launchsetting.json» اشاره شد. اگر بر روی پروژه کلیک راست کرده و به صفحه‌ی properties آن مراجعه کنید و یا دوبار کلیک بر روی گره properties، یک چنین تنظیمی را می‌توان مشاهده کرد:


این متغیر محیطی می‌تواند سه مقدار Development, Staging و Production را داشته باشد و بر اساس این متغیر و مقدار آن است که یکی از سه متد ذیل مفهوم پیدا می‌کنند و true یا false را باز می‌گردانند:
if(env.IsDevelopment()){ }
if(env.IsProduction()){ }
if(env.IsStaging()){ }


نمایش و مدیریت خطاها در حالت Production

از app.UseDeveloperExceptionPage صرفا در حالت توسعه استفاده کنید؛ چون اطلاعات نمایش داده شده‌ی توسط آن، بیش از اندازه برای مهاجمین مفید است. اما در حالت توزیع نهایی بر روی سرور چه باید کرد؟
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler(errorHandlingPath: "/MyControllerName/SomeActionMethodName");
    }
در این حالت از Middleware دیگری به نام ExceptionHandler با فراخوانی app.UseExceptionHandler می‌توان کمک گرفت. کار آن هدایت کاربر به صفحه‌ای خاص از برنامه، در صورت بروز استثنایی است. در اینجا شما می‌توانید یک صفحه‌ی عمومی «خطایی رخ داده‌است» را بدون ذکر هیچ نوع جزئیاتی، به کاربر نمایش دهید.
به علاوه در اینجا (در این قسمت خاص برنامه که توسط پارامتر errorHandlingPath مشخص شده‌است) با استفاده از قطعه کد ذیل، دسترسی کاملی را به اطلاعات خطای رخ داده، جهت ثبت و لاگ آن دارید:
 var feature = HttpContext.Features.Get<IExceptionHandlerFeature>();
var error = feature?.Error;


بررسی میان‌افزار StatusCode

این میان افزار برای مدیریت responseهایی که status code آن‌ها بین 400 تا 600 هستند، طراحی شده‌است. بر اساس این شماره‌ها، می‌توان خطای خاصی را بازگشت داده و یا کاربر را به یک صفحه یا کنترلر خاصی در برنامه، هدایت کرد.
در حالت عادی ثبت آن
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseStatusCodePages();
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler(errorHandlingPath: "/MyControllerName/SomeActionMethodName");
    }
}
تنها یک خروجی متنی را نمایش می‌دهد.


برای نمونه در اینجا مسیری درخواست داده شده‌است که توسط برنامه پردازش نمی‌شود و وجود ندارد.
اگر خواستید تا status code واقعی، به کاربر بازگشت داده شود، اما خروجی نمایش داده شده را سفارشی سازی کنید، می‌توانید از متد UseStatusCodePagesWithReExecute استفاده نمائید:
 app.UseStatusCodePagesWithReExecute("/MyControllerName/SomeActionMethodName/{0}");
در اینجا کاربر هنوز status code مساوی 404 را دریافت می‌کند (مناسب برای موتورهای جستجو)، اما اکشن متد خاصی در برنامه، سبب بازگشت یک View سفارشی به کاربر خواهد شد (بجای نمایش یک متن ساده). پارامتر {0} آن نیز همان شماره status code بازگشتی است.
  • #
    ‫۸ سال و ۱ ماه قبل، پنجشنبه ۲۸ مرداد ۱۳۹۵، ساعت ۲۱:۴۶
    در تکمیل بحث، پیشنهاد میکنم این صفحه رو هم ببینید: پشتیبانی Elmah از dot net core
  • #
    ‫۷ سال و ۸ ماه قبل، چهارشنبه ۲۹ دی ۱۳۹۵، ساعت ۱۷:۴۴
    یک نکته‌ی تکمیلی
    شبیه سازی customErrors در نگارش‌های دیگر ASP.NET که در فایل web.config قابل تنظیم است:
    <customErrors mode="On" defaultRedirect="error">
            <error statusCode="404" redirect="error/notfound" />
            <error statusCode="403" redirect="error/forbidden" />
    </customErrors>
    در ASP.NET Core چنین شکلی را پیدا می‌کند. ابتدا در متد Configure کلاس آغازین برنامه، میان افزارهای مطلب فوق را اضافه می‌کنیم:
            public void Configure(IApplicationBuilder app)
            {
                if (env.IsDevelopment())
                {
                    app.UseDatabaseErrorPage();
                    app.UseDeveloperExceptionPage();
                }
                app.UseExceptionHandler("/error/index/500");
                app.UseStatusCodePagesWithReExecute("/error/index/{0}");
    در اینجا ذکر مسیر کامل اکشن متد Index و کنترلر Error ضروری هستند. سپس این کنترلر چنین محتوایی را خواهد داشت:
        public class ErrorController : Controller
        {
            private readonly ILogger<ErrorController> _logger;
    
            public ErrorController(ILogger<ErrorController> logger)
            {
                _logger = logger;
            }
    
            public IActionResult Index(int? id)
            {
                var logBuilder = new StringBuilder();
    
                var statusCodeReExecuteFeature = HttpContext.Features.Get<IStatusCodeReExecuteFeature>();
                logBuilder.AppendLine($"Error {id} for {Request.Method} {statusCodeReExecuteFeature?.OriginalPath ?? Request.Path.Value}{Request.QueryString.Value}\n");
    
                var exceptionHandlerFeature = this.HttpContext.Features.Get<IExceptionHandlerFeature>();
                if (exceptionHandlerFeature?.Error != null)
                {
                    var exception = exceptionHandlerFeature.Error;
                    logBuilder.AppendLine($"<h1>Exception: {exception.Message}</h1>{exception.StackTrace}");
                }
    
                foreach (var header in Request.Headers)
                {
                    var headerValues = string.Join(",", value: header.Value);
                    logBuilder.AppendLine($"{header.Key}: {headerValues}");
                }
                _logger.LogError(logBuilder.ToString());
    
                if (id == null)
                {
                    return View("Error");
                }
    
                switch (id.Value)
                {
                    case 401:
                    case 403:
                        return View("AccessDenied");
                    case 404:
                        return View("NotFound");
    
                    default:
                        return View("Error");
                }
            }
        }
    - در اینجا اگر UseExceptionHandler فعال شده باشد، امکان دسترسی به سرویس IExceptionHandlerFeature خواهد بود.
    - و اگر UseStatusCodePagesWithReExecute فعال شده باشد، سرویس IStatusCodeReExecuteFeature اطلاعات مسیر اصلی درخواستی را ارائه می‌دهد.
    - سپس بر اساس id ارسالی به این اکشن متد می‌توان برای مثال صفحه‌ی 404 (یافت نشد) و یا سایر صفحات دلخواه دیگری را به صورت انتخابی نمایش داد.
  • #
    ‫۷ سال و ۳ ماه قبل، شنبه ۲۷ خرداد ۱۳۹۶، ساعت ۲۱:۱۵
    تفاوت staging و production چیه؟
    • #
      ‫۷ سال و ۳ ماه قبل، شنبه ۲۷ خرداد ۱۳۹۶، ساعت ۲۲:۱۶
      staging به یک محیط کاری آزمایشی که یک کپی مطابق اصل از محیط کاری واقعی باشد گفته می‌شود. هدف آن پیش آزمایش برنامه در یک محیط نسبتا واقعی، پیش از ارائه‌ی نهایی برنامه به کاربران آن است.
  • #
    ‫۷ سال قبل، سه‌شنبه ۲۱ شهریور ۱۳۹۶، ساعت ۱۶:۵۰
    جهت فعال سازی نمایش خطاهای بانک اطلاعاتی (برای مثال هنگام به روز رسانی خودکار برنامه )

    ابتدا بسته Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore را دریافت کنید.
    سپس برای فعال سازی آن نیاز است Middleware مرتبط با آن‌را به نحو ذیل به برنامه معرفی کنیم:

     app.UseDeveloperExceptionPage();
     app.UseDatabaseErrorPage();

     
  • #
    ‫۵ سال و ۶ ماه قبل، شنبه ۱۱ اسفند ۱۳۹۷، ساعت ۲۲:۰۷
    من از این میدلور استفاده کردم و زمانیکه خطایی رخ دهد فرضا آیدی درخواست بشه که توی رنج ایدیهای ثبت شده نباشد وارد پیج ارور میشود .
    اما زمانیکه کاربر درخواست یک صفحه بیهوده را داشته باشد فرضا https://localhost:44330/mmmnbb  یک صفحه 404 نمایش میدهد و به صفحه error/index هدایت نمی‌شود .
  • #
    ‫۵ سال و ۱ ماه قبل، چهارشنبه ۱۶ مرداد ۱۳۹۸، ساعت ۰۰:۱۲
    ارتقاء به ASP.NET Core 3.0

    در کلاس آغازین برنامه‌های وب نگارش 3، بجای متد
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    اینبار متد زیر با معرفی IWebHostEnvironment مشاهده می‌شود (چون این نوع قبلی در دو اسمبلی Microsoft.Extensions.Hosting و Microsoft.AspNetCore.Hosting تعریف شده بوده، جهت رفع ابهام مجبور به این تغییرات شده‌اند):
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    و برای اینکه متدهای الحاقی مانند ()env.IsDevelopment  شناسایی شوند، نیاز به افزودن فضای نام زیر می‌باشد:
    using Microsoft.Extensions.Hosting;
    البته امضای قبلی، هنوز هم در نگارش 3 کار می‌کند؛ اما در نگارش‌های بعدی حذف خواهد شد.
    به صورت خلاصه هرجائی در برنامه‌ی خود IHostingEnvironment دارید، باید به IWebHostEnvironment تبدیل شود. همچنین اگر از سرویس IApplicationLifetime در جائی استفاده کرده‌اید، باید به IHostApplicationLifetime تبدیل گردد.
    • #
      ‫۴ سال و ۹ ماه قبل، سه‌شنبه ۱۲ آذر ۱۳۹۸، ساعت ۱۶:۱۲
      یک نکته تکمیلی: استفاده از IsDevelopment  و فضای نام IWebHostEnvironment   

      در صورتیکه می‌خواهید از متد IsDevelopment استفاده کنید، باید فضای نام Microsoft.Extensions.Hosting را نیز اضافه کنید.
  • #
    ‫۴ سال و ۸ ماه قبل، دوشنبه ۱۶ دی ۱۳۹۸، ساعت ۲۱:۵۲
    با سلام؛ من این خط رو اضافه کردم و در صورت تایپ مسیر اشتباه که ارور 404 است ، میخواهم در صفحه Error.cshtml پارامتر {0} را نمایش دهم. چه کد Razor ای نیاز است؟
    app.UseStatusCodePagesWithReExecute("/Home/Error/{0}");
    • #
      ‫۴ سال و ۸ ماه قبل، دوشنبه ۱۶ دی ۱۳۹۸، ساعت ۲۲:۰۸
      نمونه‌ی پردازش این پارامتر کمی بالاتر با «نکته‌ی شبیه سازی customErrors در نگارش‌های دیگر ASP.NET که در فایل web.config قابل تنظیم است، در ASP.NET Core» عنوان شده. در انتهای public IActionResult Index(int? id) ذکر شده، یک سری return View هست که در اینجا می‌توانید برای مثال یک model را هم به viewها در صورت نیاز ارسال کنید.
      • #
        ‫۴ سال و ۸ ماه قبل، پنجشنبه ۱۹ دی ۱۳۹۸، ساعت ۲۱:۰۱
        ممنون
        در کنترلر Home اکشن Error : 
        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
                public IActionResult Error(int? id)
                {
                    string ErrorText = "";
                    ErrorText = (id.Value == 401 || id.Value == 403) ? "Access Denied"
                    : (id.Value == 404 ? "Not Found" : "Error Occured");
        
                    return View(new ErrorViewModel
                    {
                        RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier,
                        ErrorId = id,
                        ErrorText = ErrorText
                    });
                }
        در Error.cshtml : 
        <h1 class="text-danger">@Model.ErrorId : @Model.ErrorText</h1>
        کلاس مدل :
        public class ErrorViewModel
            {
                public string RequestId { get; set; }
        
                public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
        
                public int? ErrorId { get; set; }
        
                public string ErrorText { get; set; }
            }

  • #
    ‫۴ سال و ۷ ماه قبل، یکشنبه ۲۹ دی ۱۳۹۸، ساعت ۱۶:۲۷
    با سلام؛ آیا امکانش هست آدرس به‌صورت هارد کد نباشه؛ مثلا آدرس رو از دیتابیس یا یک فایل بخواند. چون اگر در یک سایت، صفحه اولش با این ادرس باشد www.site.com/main/، وقتی به خطا می‌خورد این مسیر رو نشون می‌دهد www.site.com/home/error که وجود ندارد.
     app.UseExceptionHandler(errorHandlingPath: "/MyControllerName/SomeActionMethodName");
    • #
      ‫۴ سال و ۷ ماه قبل، یکشنبه ۲۹ دی ۱۳۹۸، ساعت ۱۷:۱۱
      در متد public void Configure کلاس آغازین برنامه، امکان تزریق وابستگی‌های تنظیمات برنامه وجود دارد:
      public class Startup
      {
          private readonly IConfiguration _config;
      
          public Startup(IConfiguration config)
          {
              _config = config;
          }
      
          public void ConfigureServices(IServiceCollection services)
          {
              var value = _config["key"];
          }
      
          public void Configure(IApplicationBuilder app, IConfiguration config)
          {
              var value = config["key"];
          }
      }