نظرات مطالب
طراحی افزونه پذیر با ASP.NET MVC 4.x/5.x - قسمت دوم
با سلام مجدد- ممنون از راهنمایی خیلی خوبتون. با استفاده از elmah تونستم جزئیات خطا رو ثبت کنم اما چیزی ازش متوجه نشدم. فقط انقد فهمیدم که آدرس عکس و به عنوان Action میشناسه! و چون وجود خارجی نداره خطای ControllerFactory میده. نمیدونم چرا!؟ 
مطالب
آشنایی با CLR: قسمت دوم
متادیتاهای یک ماژول مدیریت شده Managed Module 

در قسمت قبلی به اصل وجودی CLR پرداختیم. در این قسمت تا حدودی به بررسی ماژول مدیریت شده managed module که از زبان‌های دیگر، کامپایل شده و به زبان میانی تبدیل گشته است صحبت می‌کنیم.

یک ماژول مدیریت شده شامل بخش‌های زیر است:
 نام بخش
توضیح
 هدر PE32 یا PE32+
CLR باید بداند که برنامه‌ی نوشته شده قرار است روی چه پلتفرمی و با چه معماری، اجرا گردد. این برنامه یک برنامه‌ی 32 بیتی است یا 64 بیتی. همچنین این هدر اشاره می‌کند که نوع فایل از چه نوعی است؛ GUI,CUI یا DLL. به علاوه تاریخ ایجاد یا کامپایل فایل هم در آن ذکر شده است. در صورتیکه این فایل شامل کدهای بومی native CPU هم باشد، اطلاعاتی در مورد این نوع کدها نیز در این هدر ذکر می‌شود و اگر ماژول ارائه شده تنها شامل کد IL باشد، قسمت بزرگی از اطلاعات این هدر در نظر گرفته نمی‌شود.
 CLR Header
اطلاعاتی را در مورد CLR ارائه می‌کند. اینکه برای اجرا به چه ورژنی از CLR نیاز دارد. منابع مورد استفاده. آدرس و اندازه جداول و فایل‌های متادیتا و جزئیات دیگر.
 metadata  هر کد یا ماژول مدیریت شده‌ای، شامل جداول متادیتا است که این جداول بر دو نوع هستند. اول جداولی که نوع‌ها و اعضای تعریف شده در کد را توصیف می‌کنند و دومی جداولی که نوع‌ها و اعضایی را که در کد به آن ارجاع شده است، توصیف می‌کنند.
IL Code
اینجا محل قرار گیری کدهای میانی تبدیل شده است که در زمان اجرا، CLR آن‌ها را به کدهای بومی تبدیل می‌کند.


کامپایلرهایی که بر اساس CLR کار می‌کنند، وظیفه دارند جداول متادیتاها را به طور کامل ساخته و داخل فایل نهایی embed کنند. متادیتاها مجموعه‌ی کاملی از فناوری‌های قدیمی چون فایل‌های COM یا Component Object Model و همچنین IDL یا Interface Definition (Description) Language هستند. گفتیم که متادیتا‌ها همیشه داخل فایل IL که ممکن است DLL باشد یا EXE، ترکیب یا Embed شد‌ه‌اند و جدایی آن‌ها غیر ممکن است. در واقع کامپایلر در یک زمان، هم کد IL و هم متادیتاها را تولید کرده و آن‌ها را به صورت یک نتیجه‌ی واحد در می‌آورد.

متادیتاها استفاده‌های زیادی دارند که در زیر به تعدادی از آنان اشاره می‌کنیم:

  • موقع کامپایل نیاز به هدرهای C و ++C از بین می‌رود؛ چرا که فایل نهایی شامل تمامی اطلاعات ارجاع شده می‌باشد. کامپایلرها می‌توانند مستقیما اطلاعات را از داخل متادیتاها بخوانند.
  • ویژوال استودیو از آن‌ها برای کدنویسی راحت‌تر بهره می‌گیرد. با استفاده از قابلیت IntelliSense، متادیتا‌ها به شما خواهند گفت چه متدهایی، چه پراپرتی‌هایی، چه رویدادهایی و ... در دسترس شماست و هر متد انتظار چه پارامترهایی را از شما دارد.
  • CLR Code Verification از متادیتا برای اینکه اطمینان کسب کند که کدها تنها عملیات type Safe را انجام می‌دهند، استفاده می‌کند.
  • متادیتاها به فیلد یک شیء اجازه می‌دهند که خود را به داخل بلوک‌های حافظ انتقال داده و بعد از ارسال به یک ماشین دیگر، همان شیء را با همان وضعیت، ایجاد نماید.
  • متادیتاها به GC اجازه می‌دهند که طول عمر یک شیء را رصد کند. GC برای هر شیء موجود می‌تواند نوع هر شیء را تشخیص داده و از طریق متادیتاها می‌تواند تشخیص دهد که فیلدهای یک شیء به اشیاء دیگری هم متصل هستند.

در آینده بیشتر در مورد متادیتاها صحبت خواهیم کرد. 

مطالب
روش دیباگ برنامه‌های مبتنی بر Angular CLI در VSCode
در انتهای مطلب «فرم‌های مبتنی بر قالب‌ها در Angular - قسمت پنجم - ارسال اطلاعات به سرور» در مورد «بارگذاری اطلاعات drop down از سرور» بحث شد. در اینجا می‌خواهیم قبل از نمایش اطلاعات این drop down در رابط کاربری، بر روی سطر this.languages = data در VSCode، یک break-point را قرار دهیم و اطلاعات دریافتی از سرور را بررسی کنیم.


پیشنیازها

در اینجا فرض بر این است که موارد ذیل را نصب کرده‌اید:
- آخرین نگارش مرورگر Chrome
- افزونه‌ی Debugger for Chrome که از آن برای دیباگ کدهای جاوا اسکریپتی و تایپ‌اسکریپتی یکپارچه‌ی با VSCode می‌توان استفاده کرد.


تنظیمات VSCode جهت دیباگ برنامه‌های مبتنی بر Angular CLI

کدهای مطلب «فرم‌های مبتنی بر قالب‌ها در Angular - قسمت پنجم - ارسال اطلاعات به سرور» از انتهای بحث آن قابل دریافت هستند. این کدها را دریافت کرده و سپس پوشه‌ی اصلی آن‌را در VSCode باز کنید. اگر در ویندوز هستید با کلیک راست بر روی پوشه، گزینه‌ی Open With Code ظاهر می‌شود و یا حتی می‌توان از طریق خط فرمان به پوشه‌ی اصلی برنامه مراجعه کرده و سپس دستور . code را صادر نمود.

در ادامه نیاز است دیباگر VSCode را تنظیم کنیم. اگر پیشتر هیچ تنظیمی را نداشته باشید، پس از مراجعه‌ی به برگه‌ی debug آن، بر روی دکمه‌ی سبز رنگ Start Debugging آن کلیک کنید. در ادامه در منوی باز شده، گزینه‌ی Chrome را انتخاب کنید:


اینکار سبب می‌شود تا فایل جدید vscode\launch.json. به پوشه‌ی جاری اضافه شده و سپس تنظیمات دیباگر کروم در آن قرار گیرند.

حالت دوم شبیه به حالتی است که برنامه‌ی مورد بحث جاری دارد: پیشتر دیباگری مانند NET Core. در آن تنظیم شده‌است. در این حالت، منوی drop down مربوط به دیباگرهای تنظیم شده را گشوده و سپس گزینه‌ی آخر آن یعنی Add configuration را انتخاب کنید:


در اینجا نیز منوی Intellisense آن ظاهر شده و امکان انتخاب گزینه‌ی Chrome را می‌دهد:


در نهایت هر دو حالت به ایجاد فایل جدید vscode\launch.json. و یا ویرایش آن منتهی می‌شوند. در اینجا تنها کاری را که باید انجام داد، تغییر پورت پیش فرض آن است:
- اگر از دستور ng serve استفاده می‌کنید، این پورت را به 4200 تغییر دهید (پورت پیش فرض این دستور است؛ برای تغییر آن، از پارامتر port-- در دستور ng serve استفاده کنید):
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "chrome",
      "request": "launch",
      "name": "Launch Chrome with ng serve",
      "url": "http://localhost:4200/#",
      "webRoot": "${workspaceRoot}"
    }
  ]
}

- اگر از دستورات dotnet watch run و سپس ng build -watch استفاده می‌کنید (اولی وب سرور آزمایشی NET Core. را راه اندازی می‌کند و دومی کار ساخت پیوسته‌ی برنامه‌ی Angular را انجام می‌دهد)،  این پورت را بر روی 5000 تنظیم کنید:
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "chrome",
      "request": "launch",
      "name": "Launch Chrome for dotnet build & ng build",
      "url": "http://localhost:5000/#",
      "webRoot": "${workspaceRoot}"
    }
  ]
}


آزمایش یک break point و بررسی مقادیر دریافتی از سرور

تا اینجا کاری را که انجام دادیم، به افزودن یک قطعه تنظیم جدید به فایل vscode\launch.json. خلاصه می‌شود.

در ادامه به کامپوننت employee-register.component.ts مراجعه کرده و سطر this.languages = data را تبدیل به یک سطر مستقل درون {} می‌کنیم تا بتوانیم بر روی آن break point قرار دهیم:
  ngOnInit() {
    this.formPoster.getLanguages().subscribe(
      data => {
        this.languages = data;
      },
      err => console.log("get error: ", err)
    );
  }

پس از آن از طریق خط فرمان به ریشه‌ی پروژه وارد شده و دو پنجره‌ی کنسول مجزا را باز کنید. در اولی دستورات
>npm install
>ng build --watch
و در دومی دستورات ذیل را اجرا کنید:
>dotnet restore
>dotnet watch run

سپس به برگه‌ی دیباگ مراجعه کرده و گزینه‌ی جدید Launch Chrome for dotnet build & ng build را انتخاب و سپس بر روی دکمه‌ی سبز رنگ اجرای آن کلیک کنید:


اکنون اگر صفحه‌ی مشاهده‌ی فرم را در مرورگر کروم باز شده درخواست کنیم، به این break point خواهیم رسید؛ اما ... حاوی اطلاعات data نیست. برای رفع این مشکل نیاز است تنظیمات پیش‌فرض را به صورت ذیل بهبود بخشید:
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "chrome",
      "request": "launch",
      "name": "Launch Chrome for dotnet build & ng build",
      "url": "http://localhost:5000",
      "runtimeArgs": [
        "--user-data-dir",
        "--remote-debugging-port=9222",
        "--disable-session-crashed-bubble"
      ],
      "sourceMaps": true,
      "trace": true,
      "webRoot": "${workspaceRoot}/wwwroot/",
      "userDataDir": "${workspaceRoot}/.vscode/chrome"
    }
  ]
}
- در این تنظیمات تکمیلی وجود runtimeArgs و userDataDir جهت مدیریت داشتن چندین وهله‌ی از کروم و باز بودن برگه‌های مختلف آن مفید است.
- تنظیم sourceMaps و همچنین مشخص سازی محل دقیق پوشه‌ی قرارگیری فایل‌های نهایی ng build که همان پوشه‌ی wwwroot است در webRoot سبب خواهند شد تا اینبار break point ما حاوی اطلاعات واقعی data دریافتی از سرور باشند (با نزدیک کردن اشاره‌گر ماوس به data، اطلاعات تکمیلی آن ظاهر می‌شود):

  - اگر از دستور ng serve استفاده می‌کنید، در این تنظیمات پورت را به 4200 و محل پوشه‌ی wwwroot را به dist تغییر دهید (مطابق تنظیمات فایل angular-cli.json.).
مطالب
شروع کار با ASP.NET Web API 2
HTTP تنها برای به خدمت گرفتن صفحات وب نیست. این پروتکل همچنین پلتفرمی قدرتمند برای ساختن API هایی است که سرویس‌ها و داده را در دسترس قرار می‌دهند. این پروتکل ساده، انعطاف پذیر و در همه جا حاضر است. هر پلتفرمی که فکرش را بتوانید بکنید کتابخانه ای برای HTTP دارد، بنابراین سرویس‌های HTTP می‌توانند بازه بسیار گسترده ای از کلاینت‌ها را پوشش دهند، مانند مرورگرها، دستگاه‌های موبایل و اپلیکیشن‌های مرسوم دسکتاپ.

ASP.NET Web API فریم ورکی برای ساختن API‌های وب بر روی فریم ورک دات نت است. در این مقاله با استفاده از این فریم ورک، API وبی خواهیم ساخت که لیستی از محصولات را بر می‌گرداند. صفحه وب کلاینت، با استفاده از jQuery نتایج را نمایش خواهد داد.


یک پروژه Web API بسازید

در ویژوال استودیو 2013 پروژه جدیدی از نوع ASP.NET Web Application بسازید و نام آن را "ProductsApp" انتخاب کنید.

در دیالوگ New ASP.NET Project قالب Empty را انتخاب کنید و در قسمت "Add folders and core references for" گزینه Web API را انتخاب نمایید.

می توانید از قالب Web API هم استفاده کنید. این قالب با استفاده از ASP.NET MVC صفحات راهنمای API را خواهد ساخت. در این مقاله از قالب Empty استفاده میکنیم تا تمرکز اصلی، روی خود فریم ورک Web API باشد. بطور کلی برای استفاده از این فریم ورک لازم نیست با ASP.NET MVC آشنایی داشته باشید.

افزودن یک مدل

یک مدل (model) آبجکتی است که داده اپلیکیشن شما را معرفی می‌کند. ASP.NET Web API می‌تواند بصورت خودکار مدل شما را به JSON, XML و برخی فرمت‌های دیگر مرتب (serialize) کند، و سپس داده مرتب شده را در بدنه پیام HTTP Response بنویسد. تا وقتی که یک کلاینت بتواند فرمت مرتب سازی داده‌ها را بخواند، می‌تواند آبجکت شما را deserialize کند. اکثر کلاینت‌ها می‌توانند XML یا JSON را تفسیر کنند. بعلاوه کلاینت‌ها می‌توانند فرمت مورد نظرشان را با تنظیم Accept header در پیام HTTP Request مشخص کنند.

بگذارید تا با ساختن مدلی ساده که یک محصول (product) را معرفی میکند شروع کنیم.

کلاس جدیدی در پوشه Models ایجاد کنید.

نام کلاس را به "Product" تغییر دهید، و خواص زیر را به آن اضافه کنید.

namespace ProductsApp.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Category { get; set; }
        public decimal Price { get; set; }
    }
}

افزودن یک کنترلر

در Web API کنترلر‌ها آبجکت هایی هستند که درخواست‌های HTTP را مدیریت کرده و آنها را به اکشن متدها نگاشت می‌کنند. ما کنترلری خواهیم ساخت که می‌تواند لیستی از محصولات، یا محصولی بخصوص را بر اساس شناسه برگرداند. اگر از ASP.NET MVC استفاده کرده اید، با کنترلرها آشنا هستید. کنترلرهای Web API مشابه کنترلر‌های MVC هستند، با این تفاوت که بجای ارث بری از کلاس Controller از کلاس ApiController مشتق می‌شوند.

کنترلر جدیدی در پوشه Controllers ایجاد کنید.

در دیالوگ Add Scaffold گزینه Web API Controller - Empty را انتخاب کرده و روی Add کلیک کنید.

در دیالوگ Add Controller نام کنترلر را به "ProductsController" تغییر دهید و روی Add کلیک کنید.

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

using ProductsApp.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web.Http;

namespace ProductsApp.Controllers
{
    public class ProductsController : ApiController
    {
        Product[] products = new Product[] 
        { 
            new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 }, 
            new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M }, 
            new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M } 
        };

        public IEnumerable<Product> GetAllProducts()
        {
            return products;
        }

        public IHttpActionResult GetProduct(int id)
        {
            var product = products.FirstOrDefault((p) => p.Id == id);
            if (product == null)
            {
                return NotFound();
            }
            return Ok(product);
        }
}
برای اینکه مثال جاری را ساده نگاه داریم، محصولات مورد نظر در یک آرایه استاتیک ذخیره شده اند. مسلما در یک اپلیکیشن واقعی برای گرفتن لیست محصولات از دیتابیس یا منبع داده ای دیگر کوئری می‌گیرید.

کنترلر ما دو متد برای دریافت محصولات تعریف می‌کند:

  • متد GetAllProducts لیست تمام محصولات را در قالب یک <IEnumerable<Product بر می‌گرداند.
  • متد GetProductById سعی می‌کند محصولی را بر اساس شناسه تعیین شده پیدا کند.

همین! حالا یک Web API ساده دارید. هر یک از متدهای این کنترلر، به یک یا چند URI پاسخ می‌دهند:

 URI  Controller Method
api/products/  GetAllProducts
 api/products/id/  GetProductById

برای اطلاعات بیشتر درباره نحوه نگاشت درخواست‌های HTTP به اکشن متدها توسط Web API به این لینک مراجعه کنید.


فراخوانی Web API با جاوا اسکریپت و jQuery

در این قسمت یک صفحه HTML خواهیم ساخت که با استفاده از AJAX متدهای Web API را فراخوانی می‌کند. برای ارسال درخواست‌های آژاکسی و بروز رسانی صفحه بمنظور نمایش نتایج دریافتی از jQuery استفاده میکنیم.

در پنجره Solution Explorer روی نام پروژه کلیک راست کرده و گزینه Add, New Item را انتخاب کنید.

در دیالوگ Add New Item قالب HTML Page را انتخاب کنید و نام فایل را به "index.html" تغییر دهید.

حال محتوای این فایل را با لیست زیر جایگزین کنید.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>Product App</title>
</head>
<body>

  <div>
    <h2>All Products</h2>
    <ul id="products" />
  </div>
  <div>
    <h2>Search by ID</h2>
    <input type="text" id="prodId" size="5" />
    <input type="button" value="Search" onclick="find();" />
    <p id="product" />
  </div>

  <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.3.min.js"></script>
  <script>
    var uri = 'api/products';

    $(document).ready(function () {
      // Send an AJAX request
      $.getJSON(uri)
          .done(function (data) {
            // On success, 'data' contains a list of products.
            $.each(data, function (key, item) {
              // Add a list item for the product.
              $('<li>', { text: formatItem(item) }).appendTo($('#products'));
            });
          });
    });

    function formatItem(item) {
      return item.Name + ': $' + item.Price;
    }

    function find() {
      var id = $('#prodId').val();
      $.getJSON(uri + '/' + id)
          .done(function (data) {
            $('#product').text(formatItem(data));
          })
          .fail(function (jqXHR, textStatus, err) {
            $('#product').text('Error: ' + err);
          });
    }
  </script>
</body>
</html>
راه‌های مختلفی برای گرفتن jQuery وجود دارد، در این مثال از Microsoft Ajax CDN استفاده شده. می‌توانید این کتابخانه را از http://jquery.com دانلود کنید و بصورت محلی استفاده کنید. همچنین قالب پروژه‌های  Web API این کتابخانه را به پروژه نیز اضافه می‌کنند.


گرفتن لیستی از محصولات

برای گرفتن لیستی از محصولات، یک درخواست HTTP GET به آدرس "api/products/" ارسال کنید.

تابع getJSON یک درخواست آژاکسی ارسال می‌کند. پاسخ دریافتی هم آرایه ای از آبجکت‌های JSON خواهد بود. تابع done در صورت موفقیت آمیز بودن درخواست، اجرا می‌شود. که در این صورت ما DOM را با اطلاعات محصولات بروز رسانی می‌کنیم.

$(document).ready(function () {
    // Send an AJAX request
    $.getJSON(apiUrl)
        .done(function (data) {
            // On success, 'data' contains a list of products.
            $.each(data, function (key, item) {
                // Add a list item for the product.
                $('<li>', { text: formatItem(item) }).appendTo($('#products'));
            });
        });
});

گرفتن محصولی مشخص

برای گرفتن یک محصول توسط شناسه (ID) آن کافی است یک درخواست HTTP GET به آدرس "api/products/id/" ارسال کنید.

function find() {
    var id = $('#prodId').val();
    $.getJSON(apiUrl + '/' + id)
        .done(function (data) {
            $('#product').text(formatItem(data));
        })
        .fail(function (jqXHR, textStatus, err) {
            $('#product').text('Error: ' + err);
        });
}
برای این کار هنوز از getJSON برای ارسال درخواست آژاکسی استفاده می‌کنیم، اما اینبار شناسه محصول را هم به آدرس درخواستی اضافه کرده ایم. پاسخ دریافتی از این درخواست، اطلاعات یک محصول با فرمت JSON است.


اجرای اپلیکیشن

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

برای گرفتن محصولی مشخص، شناسه آن را وارد کنید و روی Search کلیک کنید.

اگر شناسه نامعتبری وارد کنید، سرور یک خطای HTTP بر می‌گرداند.


استفاده از F12 برای مشاهده درخواست‌ها و پاسخ ها

هنگام کار با سرویس‌های HTTP، مشاهده‌ی درخواست‌های ارسال شده و پاسخ‌های دریافتی بسیار مفید است. برای اینکار می‌توانید از ابزار توسعه دهندگان وب استفاده کنید، که اکثر مرورگرهای مدرن، پیاده سازی خودشان را دارند. در اینترنت اکسپلورر می‌توانید با F12 به این ابزار دسترسی پیدا کنید. به برگه Network بروید و روی Start Capturing کلیک کنید. حالا صفحه وب را مجددا بارگذاری (reload) کنید. در این مرحله اینترنت اکسپلورر ترافیک HTTP بین مرورگر و سرور را تسخیر می‌کند. می‌توانید تمام ترافیک HTTP روی صفحه جاری را مشاهده کنید.

به دنبال آدرس نسبی "api/products/" بگردید و آن را انتخاب کنید. سپس روی Go to detailed view کلیک کنید تا جزئیات ترافیک را مشاهده کنید. در نمای جزئیات، می‌توانید header‌ها و بدنه درخواست‌ها و پاسخ‌ها را ببینید. مثلا اگر روی برگه Request headers کلیک کنید، خواهید دید که اپلیکیشن ما در Accept header داده‌ها را با فرمت "application/json" درخواست کرده است.

اگر روی برگه Response body کلیک کنید، می‌توانید ببینید چگونه لیست محصولات با فرمت JSON سریال شده است. همانطور که گفته شده مرورگرهای دیگر هم قابلیت‌های مشابهی دارند. یک ابزار مفید دیگر Fiddler است. با استفاده از این ابزار می‌توانید تمام ترافیک HTTP خود را مانیتور کرده، و همچنین درخواست‌های جدیدی بسازید که این امر کنترل کاملی روی HTTP headers به شما می‌دهد.


قدم‌های بعدی

  • برای یک مثال کامل از سرویس‌های HTTP که از عملیات POST,PUT و DELETE پشتیبانی می‌کند به این لینک مراجعه کنید.
  • برای اطلاعات بیشتر درباره طراحی واکنش گرا در کنار سرویس‌های HTTP به این لینک مراجعه کنید، که اپلیکیشن‌های تک صفحه ای (SPA) را بررسی می‌کند.
نظرات مطالب
بررسی خطاهای ممکن در حین راه اندازی اولیه برنامه‌های ASP.NET Core در IIS
روشی دیگر برای لاگ کردن خطاهای آغاز برنامه
<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.4" />
    <PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
    <PackageReference Include="Serilog.Sinks.RollingFile" Version="3.3.0" />
و سپس تغییر فایل program.cs جهت لاگ کردن خطاهای آغاز برنامه:
public class Program
{
    public static int Main(string[] args)
    {
        Log.Logger = new LoggerConfiguration()
       .MinimumLevel.Debug()
       .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
       .Enrich.FromLogContext()
       .WriteTo.RollingFile("logs/log-{Date}.txt")
       .CreateLogger();
 
        try
        {
            Log.Information("Starting web host");
            BuildWebHost(args).Run();
            return 0;
        }
        catch (Exception ex)
        {
            Log.Fatal(ex, "Host terminated unexpectedly");
            return 1;
        }
        finally
        {
            Log.CloseAndFlush();
        }
 
    }
 
    public static IWebHost BuildWebHost(string[] args) =>
          WebHost.CreateDefaultBuilder(args)
          .UseStartup<Startup>()
              .UseSerilog() // <-- Add this line
              .Build();
 
}
نظرات مطالب
نحوه استفاده از ViewModel در ASP.NET MVC
با سلام؛ لطفا درباره @section speakers  یکم توضیح بدین. این @Section به چه کار آید ؟
حدس من اینه که با این دستور بشه توی یک مدل کلی بخش (مثل ماژول) رو تعریف کرد و توی یک ویو بتونیم در جاهای مختلف این Section‌ها رو نمایش بدیم ؟ (عدم نیاز به چند Partial View و نوشتن همه توی یک ویو)
مطالب
نحوه‌ی فعال سازی library caching زمانیکه یک Silverlight library را تولید کرده‌ایم

در مورد کاهش حجم فایل‌های XAP سیلورلایت زمانیکه از اسمبلی‌های کتابخانه‌های دیگر مانند Silverlight toolkit استفاده می‌شود، در این فصل بحث شده است و راه حل، استفاده از گزینه‌ی reduce XAP size by using application library caching است. به این صورت کاربران دیگر به ازای هر بار مشاهده‌ی سایت نیازی نخواهند داشت تا یک سری کتابخانه‌ی کمکی را که هیچ تغییری در آن‌ها حاصل نخواهد شد، دریافت کنند و اطلاعات آن‌ها از cache مرورگر خوانده می‌شود. این مورد با کتابخانه‌ها و ابزارهای کمکی تولید شده توسط مایکروسافت کار می‌کند. اما اگر خودتان یک Silverlight library را تولید کنید، چنین اتفاقی رخ نخواهد داد و باز هم فایل اسمبلی کتابخانه‌ی شما درون فایل XAP اصلی برنامه قرار گرفته و خبری از caching مجزای آن نیست. چرا اینطور است؟ چکار باید کرد؟!
علت آن بر می‌گردد به نحوه‌ی پیاده سازی library caching در VS.NET و Silverlight . برای این منظور چند مرحله باید طی شود تا این قابلیت برای کتابخانه‌های ساخت خودمان نیز فعال گردد:
الف) به کتابخانه‌ی خود باید امضای دیجیتال اضافه کنید:
اینکار با استفاده از امکانات خود VS.NET بسیار ساده است. به خواص پروژه مراجعه کنید. سپس برگه‌ی Signing را باز کرده و گزینه‌ی Sign the assembly را انتخاب کنید (شکل زیر). در قسمت choose a strong name key file ، گزینه‌ی new را انتخاب کرده و پس از وارد کردن یک نام دلخواه و گذر واژه‌ای، فایل pfx امضای دیجیتال اسمبلی شما تولید خواهد شد. اکنون تنها کافی است یکبار دیگر برنامه را کامپایل کنید.


ب) به یک فایل extMap.xml هم نیاز است:
هنگام پیاده سازی قابلیت library caching ، VS.NET به دنبال فایلی به نام AssemblyFileName.extmap.xml دقیقا در کنار فایل اسمبلی مورد نظر می‌گردد. ساختار عمومی این فایل XML به صورت زیر است:

<?xml version="1.0"?>
<manifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<assembly>
<name>SLHelper</name>
<version>1.0.0.0</version>
<publickeytoken>f265933def965412</publickeytoken>
<relpath>SLHelper.dll</relpath>
<extension downloadUri="SLHelper.zip" />
</assembly>
</manifest>

نام، شماره نگارش، مسیر قرارگیری فایل اسمبلی مورد نظر و همچنین نام نهایی آن حین جدا سازی آن از XAP برنامه باید مشخص گردد. گزینه‌ی publickeytoken مهم‌ترین تنظیم این فایل است و قسمت الف را به همین منظور نیاز داشتیم. این عدد را به سادگی با استفاده از برنامه‌ی reflector می‌توان بدست آورد (شکل زیر).



جهت ساده سازی قسمت (ب)، برنامه‌ی کمکی را از آدرس ذیل می‌توانید دریافت کنید:
Utility: Extmap Maker

برای مطالعه بیشتر
Silverlight 3: Cached Assemblies and you can to

مطالب
ارسال فایل در ASP.NET MVC و اعتبار سنجی سمت کاربر
پیشنیازها:
- نحوه ارسال فایل به سرور توسط ASP.NET MVC
- نحوه اعتبار سنجی سمت سرور ارسال فایل‌ها

در ASP.NET MVC برای آپلود فایل‌ها عموما عنوان می‌شود که از تگ input به نحو زیر استفاده شود:
<input type="file" name="file" />
مشکلی که با این روش وجود دارد، عدم فعال شدن اعتبار سنجی سمت کاربر در حد مثلا «لطفا یک فایل را انتخاب کنید» است. برای فعال سازی آن می‌توان از همان روش unobtrusive معرفی شده در ASP.NET MVC 3 به نحو زیر به صورت دستی استفاده کرد:
<input type="file" data-val="true" data-val-required="لطفا فایلی را انتخاب کنید" name="file" />
@Html.ValidationMessage("file")
در اینجا ویژگی‌های data-val و data-val-required به صورت خودکار اعتبار سنجی سمت کاربر را فعال خواهند کرد (شبیه به required field validator عمل می‌کنند).

از این نکته خصوصا در طراحی html helperهای سفارشی نیز می‌توان استفاده کرد.
برای مثال فرض کنید مشغول طراحی یک کنترل captcha هستید. قسمتی از آن مربوط به دریافت ورودی از کاربر است. به علاوه، پیغام خطایی هم که باید نمایش داده شود نیز باید توسط کاربر قابلیت سفارشی شدن را داشته باشد (و نمی‌توان آن‌را توسط یک attribute به سادگی به یک مدل خاص انتساب داد).
در این حالت تنظیم data-val و data-val-required به کمک anonymously typed objects پارامتر htmlAttributes میسر نیست (چون بین حروف آن dash وجود دارد) و باید از overload و نوع dictionary دار آن به نحو زیر استفاده کرد:

htmlHelper.TextBox("CaptchaInputText", string.Empty,
                          new Dictionary<string, object>
                          {
                            { "data-val", "true"},
                            { "data-val-required", validationErrorMessage},
                          })

مطالب
ماژول‌ها در ES 6
ماژول‌ها در ES 6

هدف از سیستم ماژول‌ها در ES 6، مدیریت بهتر تعدادی قطعه کد جاوا اسکریپتی، به صورت یک واحد مشخص است. همچنین ماژول‌ها امکان مخفی کردن قسمت‌هایی از کد را که نباید به صورت عمومی در دسترس قرارگیرند، نیز میسر می‌کنند. این مسایل سال‌ها آرزوی برنامه نویسان جاوا اسکریپت بوده‌اند و برای برآورده کردن آن‌ها به روش‌های غیراستاندارد و کتابخانه‌های ثالثی روی آورده بودند. به همین جهت برای آشنایی بهتر با ماژول‌ها در ES 6، ابتدا نیاز است با روش‌های متداول فعلی بسته بندی کدها در جاوا اسکریپت آشنا شد.


روش IIFE Module

الگوی ماژول‌ها، سال‌ها است که در جاوا اسکریپت مورد استفاده‌است:
(function(target){
  var privateDoWork = function(name) {
    return name +" is working";
  };

  var Employee = function(name) {
      this.name = name;
  }

  Employee.prototype = {
     doWork: function() {
       return privateDoWork(this.name);
     }
  }

  target.Employee = Employee;

}(window));
IIFE یا immediately invoked function expression، متدی خود اجرا شونده است. به صورت پیش فرض هر متغیری که داخل IIFE تعریف می‌شود، سطح دید آن محدود است به بدنه‌ی همان ماژول و دیگر دسترسی عمومی ندارد. برای مثال شیء Employee فقط داخل این ماژول قابل دسترسی است. تنها زمانیکه توسط target.Employee، این شیء را به window متصل می‌کنیم، خارج از این ماژول قابل دسترسی می‌شود.
بنابراین با روش IIFE به مزیت‌های یک سیستم ماژول می‌رسیم:
الف) امکان مدیریت کدها را به صورت یک unit و واحد فراهم می‌کند.
ب) همچنین در اینجا امکان کنترل میدان دید متغیرها و متدها نیز میسر است.


روش CommonJS

از سال 2009 استفاده از جاوا اسکریپت به خارج از مرورگرها گسترش یافت؛ برای مثال نوشتن برنامه‌های سمت سرور NodeJS یا MongoDB با جاوا اسکریپت. در یک چنین حالتی برای مدیریت پیچیدگی برنامه‌های گسترده‌ی سمت سرور و پرهیز از متغیرها و اشیاء عمومی، پروژه‌ی CommonJS شکل گرفت. در CommonJS نحوه‌ی تعریف ماژول‌ها بسیار شبیه است به IIFE. با این تفاوت که دیگر خبری از متد خود اجرا شونده وجود ندارد و همچنین بجای target از exports، جهت درمعرض دید قرار دادن اشیاء استفاده می‌کند.
  var privateDoWork = function(name) {
    return name +" is working";
  };

  var Employee = function(name) {
      this.name = name;
  }

  Employee.prototype = {
     doWork: function() {
       return privateDoWork(this.name);
     }
  }

exports.Employee = Employee;
این تعاریف در یک فایل مجزای JS قرار می‌گیرند. سپس برای دسترسی به آن‌ها در فایلی دیگر، از روش ذیل استفاده می‌کنند:
 var Employee = require("./Employee").Employee;
var e1 = new Employee("Vahid");
console.log(e1.doWork());
در متد require، مسیر فایل و ماژول تعریف شده، مشخص می‌شود؛ بدون ذکر پسوند .js فایل.


روش AMD

از CommonJS بیشتر در برنامه‌های جاوا اسکریپتی که خارج از مرورگر اجرا می‌شوند، استفاده می‌شود. برای حالت‌های اجرای برنامه‌ها درون مرورگرها و خصوصا بلاک نشدن ترد نمایش صفحه در حین پردازش ماژول‌ها، روش دیگری به نام AMD API و یا Asynchronous module definition به وجود آمد. پیاده سازی محبوب این API عمومی، توسط کتابخانه‌ای به نام RequireJS انجام شده‌است.
define(function(){

  var privateDoWork = function(name) {
    // ...
  };

  var Employee = function(name) {
    // ...
  }

  return Employee;
});
در اینجا یک ماژول تعریف شده‌ی در یک فایل مجزای جاوا اسکریپتی با define function شروع می‌شود و در نهایت یک return دارد.
تفاوت مهم این روش با روش IIFE این است که در روش IIFE تمام کد باید مهیا بوده و همچنین بلافاصله قابل اجرا باشد. در اینجا تنها زمانیکه نیاز به کار با ماژولی باشد، اطلاعات آن بارگذاری شده و استفاده می‌شود.
برای استفاده‌ی از این ماژول‌ها نیز از همان define استفاده می‌شود و پارامتر اول ارسالی، آرایه‌ای است که ماژول‌های مورد نیاز را تعریف می‌کند (تعریف وابستگی‌ها). برای مثال employee تعریف شده در اینجا سبب بارگذاری فایل employee.js می‌شود و سپس امکانات آن به صورت یک پارامتر، به متدی که به آن نیاز دارد ارسال می‌گردد:
define(["employee"], function(Employee){
      var e = new Employee("Vahid");
});


ماژول‌ها در ES 6

سیستم تعریف ماژول‌ها در ES 6 بسیار شبیه است به روش‌های CommonJS و AMD API. در اینجا یک نمونه از روش تعریف ماژول‌ها را در نگارش جدید جاوا اسکریپت مشاهده می‌کنید:
export class Employee {
  constructor(name) {
    this[s_name] = name;
  }

  get name() {
    return this[s_name];
  }

  doWork() {
    return `${this.name} is working`;
  }
}
در اینجا واژه‌ی کلیدی export سبب در دسترس قرار گرفتن تعریف یک کلاس تعریف شده‌ی در این ماژول که در اینجا یک فایل جاوا اسکریپتی است، می‌شود. در یک فایل می‌توان چندین export را داشت؛ اما بهتر است یک export را به ازای هر فایل درنظر گرفت.
پس از این export، اکنون برای استفاده‌ی از آن در یک فایل js دیگر، از واژه‌ی کلیدی import کمک گرفته می‌شود:
 import {Employee} from "./employee";
var e1 = new Employee("Vahid");
console.log(e1.doWork());
در اینجا پس از from، مسیر فایل js، بدون ذکر پسوند آن مشخص می‌شود.

و یا برای ارائه‌ی یک متد خروجی، به نحو ذیل عمل می‌شود:
export function multiply (x, y) {
   return x * y;
};
و اگر یک متغیر و یا متد تعریف شده‌ی در سطح ماژول را بخواهیم عمومی کنیم، باید از {} استفاده شود:
var hello = 'Hello World',
multiply = function (x, y) {
   return x * y;
};
export { hello, multiply };


حالت پیش فرض ماژول‌های ES 6 همان strict mode است

در سیستم ماژول‌های ES 6، حالت strict به صورت پیش فرض روشن است. برای مثال متغیرها حتما باید تعریف شوند.


امکان تعریف خروجی‌های متفاوت از یک ماژول در ES 6

در همان فایلی که export class Employee فوق را در آن تعریف کرده‌ایم، یک چنین تعریف‌هایی را نیز می‌توان ارائه داد:
export let log = function(employee) {
   console.log(employee.name);
}

export let defaultRaise = 0.03;

export let modelEmployee = new Employee("Vahid");
در اینجا نحوه‌ی export متد log و یا متغیر defaultRaise و همچنین شیء modelEmployee را مشاهده می‌کنید. سپس برای استفاده‌ی از این خروجی‌ها، قسمت import نیز باید به نحو ذیل تغییر کند:
 import {Employee, log, defaultRaise, modelEmployee} from "./employee";
log(modelEmployee);
برای ساده سازی دریافت تمام خروجی‌های یک ماژول ES 6، می‌توان از واژه‌ی کلیدی module استفاده کرد:
 module m from "./employee";
در اینجا متغیر m امکان دسترسی به Employee, log, defaultRaise, modelEmployee را بدون نیاز به ذکر آن‌ها در قسمت import میسر می‌کند. در یک چنین حالتی برای دسترسی به خروجی‌ها، از .m استفاده می‌شود. برای مثال:
 console.log(m.defaultRaise);
و یا
 var e1 = new m.Employee("Vahid");
console.log(e1.doWork());

روش دیگر انجام اینکار، استفاده از * است برای درخواست تمام وابستگی‌های مورد نیاز:
 import * from "./employee";


امکان استفاده از یک ماژول در ماژولی دیگر

برای اینکه از امکانات یک ماژول در ماژولی دیگر استفاده کنیم نیز می‌توان از همان روش تعریف import در ابتدای ماژول استفاده کرد:
 import {Employee} from "./employee";


امکان تعریف ماژول پیش فرض در ES 6

اگر ماژول شما (همان فایل js) تنها دارای یک export است، می‌توانید آن‌را با واژه‌ی کلیدی default مشخص کنید:
  export default class Employee {
به این ترتیب برای استفاده‌ی از این ماژول تنها کافی است بنویسیم:
 import factory from "./employee";
var e1 = new factory("Vahid");
console.log(e1.doWork());
در اینجا factory یک نام متغیر دلخواه است و هر نام دیگری را نیز می‌تواند داشته باشد.

البته باید دقت داشت که یک چنین تعریف‌هایی نیز مجاز است و می‌توان خروجی پیش فرض و همچنین نامداری را نیز با هم ترکیب کرد:
export hello = 'Hello World';
export default function (x, y) {
   return x * y;
};
در این حالت تعریف ذیل به این معنا است که pow2 به متد پیش فرض بدون نام و hello به متغیر hello اشاره می‌کنند:
 import pow2, { hello } from 'modules';


امکان مخفی سازی اطلاعات در ماژول‌های ES 6

یکی از انتظارات از سیستم ماژول، امکان مخفی سازی اطلاعات است. در اینجا تنها کافی است شیء، متد و یا متغیر تعریف شده، با واژه‌ی کلیدی export مزین نشوند:
let privateFunction = function() {

}

 export default class Employee {
در این مثال، متد privateFunction در ماژول employee تعریف شده‌است؛ اما چون دارای واژه‌ی کلیدی export نیست، سطح دسترسی آن خصوصی است.

یک نکته: اگر در کلاس export شده، خواستید تا دسترسی به s_name را محدود کنید، از Symbol ها به نحو ذیل کمک بگیرید:
let s_name = Symbol();

export class Employee {
  constructor(name) {
    this[s_name] = name;
  }

  get name() {
    return this[s_name];
  }

  doWork() {
   return `${this.name} is working`;
  }
}
مطالب
React 16x - قسمت 34 - توزیع برنامه
در قسمت آخر این سری، نگاهی خواهیم داشت به نحوه‌ی توزیع برنامه‌های React و نکات مرتبط با آن.


افزودن متغیرهای محیطی

در برنامه‌ی نمایش لیست فیلم‌هایی که تا قسمت 29 آن‌را بررسی کردیم، از فایل src\config.json برای ذخیره سازی اطلاعات تنظیمات برنامه استفاده شد. هرچند این روش کار می‌کند اما بر اساس محیط‌های مختلف توسعه، متغیر نیست. اغلب برنامه‌ها باید بتوانند حداقل در سه محیط توسعه، آزمایش و تولید، بر اساس متغیرها و تنظیمات خاص هر کدام، کار کنند. برای مثال بر روی سیستمی که کار توسعه در آن انجام می‌شود، می‌خواهیم apiUrl متفاوتی را نسبت به حالتیکه برنامه توزیع می‌شود، داشته باشیم.
برای رفع این مشکل، برنامه‌هایی که توسط create-react-app تولید می‌شوند، دارای پشتیبانی توکاری از متغیرهای محیطی هستند. برای این منظور نیاز است در ریشه‌ی پروژه (جائیکه فایل package.json قرار دارد) فایل جدید env. را ایجاد کرد. در ویندوز برای ایجاد یک چنین فایل‌هایی که فقط از یک پسوند تشکیل می‌شوند، باید نام فایل را به صورت .env. وارد کرد؛ سپس خود ویندوز نقطه‌ی نهایی را حذف می‌کند. البته اگر از ادیتور VSCode برای ایجاد این فایل استفاده می‌کنید، نیازی به درج نقطه‌ی انتهایی نیست. در این فایل environment ایجاد شده می‌توان تمام متغیرهای محیطی مورد نیاز را با مقادیر پیش‌فرض آن‌ها درج کرد. همچنین می‌توان این مقادیر پیش‌فرض را بر اساس محیط‌های مختلف کاری، بازنویسی کرد. برای مثال می‌توان فایل env.development. را اضافه کرد؛ به همراه فایل‌های env.test. و env.production.


متغیرهای محیطی به صورت key=value درج می‌شوند. این کلیدها نیر باید با REACT_APP_ شروع شوند؛ در غیر اینصورت، کار نخواهند کرد. برای مثال در فایل env.، دو متغیر پیش‌فرض زیر را تعریف می‌کنیم:
REACT_APP_NAME=My App
REACT_APP_VERSION=1
اکنون برای خواندن این متغیرها برای مثال در فایل index.js (و یا هر فایل جاوا اسکریپتی دیگری در برنامه)، سطر زیر را درج می‌کنیم:
console.log(process.env);
process به معنای پروسه‌ی جاری برنامه‌است (و مرتبط است به پروسه‌ی node.js ای که برنامه‌ی React را اجرا می‌کند) و خاصیت env، به همراه تمام متغیرهای محیطی برنامه می‌باشد. در این حالت اگر برنامه را اجرا کنیم، در کنسول توسعه دهندگان مرورگر، به یک چنین خروجی خواهیم رسید:


در این خروجی، متغیر "NODE_ENV: "development به صورت خودکار با تولید بسته‌های مخصوص ارائه‌ی نهایی، به production تنظیم می‌شود. سایر متغیرهای محیطی تعریف شده را نیز در اینجا ملاحظه می‌کنید. با توجه به خواص شیء env، برای مثال جهت دسترسی به نام برنامه می‌توان از مقدار process.env.REACT_APP_NAME استفاده کرد.


یک نکته: با هر تغییری در مقادیر متغیرهای محیطی، نیاز است یکبار دیگر برنامه را از ابتدا توسط دستور npm start، راه اندازی مجدد کرد؛ چون این فایل‌ها به صورت خودکار ردیابی نمی‌شوند.


نحوه‌ی پردازش متغیرهای محیطی درج شده‌ی در برنامه

اگر همان سطر لاگ کردن خروجی process.env را به صورت زیر تغییر دهیم:
console.log("My App Name", process.env.REACT_APP_NAME);
و برنامه را مجددا اجرا کنیم، با مراجعه‌ی به برگه‌ی Sources و انتخاب مسیر localhost:3000/static/js/main.chunk.js و سپس جستجوی "My App Name" ای که در اینجا اضافه کردیم (با فشردن دکمه‌های Ctrl+F)، به خروجی زیر خواهیم رسید:


همانطور که مشاهده می‌کنید، فراخوانی console.log ما، دیگر به همراه متغیر process.env.REACT_APP_NAME نیست؛ بلکه مقدار اصلی این متغیر در اینجا درج شده‌است. بنابراین اگر در در حین توسعه‌ی برنامه، از متغیرهای محیطی استفاده شود، این متغیرها با مقادیر اصلی آن‌ها در حین پروسه‌ی Build نهایی، جایگزین می‌شوند.


Build برنامه‌های React برای محیط تولید

اجرای دستور npm start، سبب ایجاد یک Build مخصوص محیط توسعه می‌شود که بهینه سازی نشده‌است و به همراه اطلاعات اضافی قابل توجهی جهت دیباگ ساده‌تر برنامه‌است. برای رسیدن به یک خروجی بهینه سازی شده‌ی مخصوص محیط تولید و ارائه‌ی نهایی باید دستور npm run build را در خط فرمان اجرا کرد. خروجی نهایی این دستور، در پوشه‌ی جدید build واقع در ریشه‌ی پروژه، قرار می‌گیرد. اکنون می‌توان کل محتویات این پوشه را جهت ارائه‌ی نهایی در وب سرور خود، مورد استفاده قرار داد.
پس از پایان اجرای دستور npm run build، پیام «امکان ارائه‌ی آن توسط static server زیر نیز وجود دارد» ظاهر می‌شود:
> npm install -g serve
> serve -s build
اگر علاقمند باشید تا خروجی حالت production تولید شده را نیز به صورت محلی آزمایش کنید، ابتدا باید static server یاد شده را توسط دستور npm install فوق نصب کنید. سپس ریشه‌ی پروژه را در خط فرمان باز کرده و دستور serve -s build را صادر کنید (البته اگر با خط فرمان به پوشه‌ی build وارد شدید، دیگر نیازی به ذکر پوشه‌ی build نخواهد بود). اکنون می‌توانید برنامه را در آدرس http://localhost:5000 در مرورگر خود بررسی نمائید.

البته با توجه به اینکه backend سرور برنامه‌های ما نیز در همین آدرس قرار دارد و در صورت ورود این آدرس، به صورت خودکار به https://localhost:5001/index.html هدایت خواهید شد، می‌توان این پورت پیش‌فرض را با اجرای دستور  serve -s build -l 1234 تغییر داد. اکنون می‌توان آدرس جدید http://localhost:1234 را در مرورگر آزمایش کرد که ... با خطای زیر کار نمی‌کند:
Access to XMLHttpRequest at 'https://localhost:5001/api/genres' from origin 'http://localhost:1234' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
روش رفع این مشکل را در قسمت 23 بررسی کردیم و در اینجا جهت بهبود آن می‌توان متد WithOrigins فایل Startup.cs را به صورت زیر تکمیل کرد:
WithOrigins("http://localhost:3000", "http://localhost:1234")

یک نکته: زمانیکه از دستور npm start استفاده می‌شود، متغیرهای محیطی از فایل env.development. خوانده خواهند شد و زمانیکه از دستور npm run build استفاده می‌شود، این متغیرها از فایل env.production. تامین می‌شوند. در این حالت‌ها اگر متغیری در این دو فایل درج نشده بود، از مقدار پیش‌فرض موجود در فایل env. استفاده می‌گردد. از فایل env.test. با اجرای دستور npm test، به صورت خودکار استفاده می‌شود.


آماده سازی برنامه‌ی React، برای توزیع نهایی

تا اینجا برنامه‌ی React تهیه شده، اطلاعات apiUrl خودش را از فایل config.json دریافت می‌کند. اکنون می‌خواهیم بر اساس حالات مختلف توسعه و تولید، از apiUrlهای متفاوتی استفاده شود. به همین جهت به فایل env.production. مراجعه کرده و تنظیمات ذیل را به آن اضافه می‌کنیم:
REACT_APP_API_URL=https://localhost:5001/api
REACT_APP_ADMIN_ROLE_NAME=Admin
البته فعلا همین متغیرها را به فایل env.development. نیز می‌توان اضافه کرد؛ چون backend سرور ما در هر دو حالت، در این مثال، در آدرس فوق قرار دارد.

اکنون به برنامه مراجعه کرده و در هرجائی که ارجاعی به فایل config.json وجود دارد، سطر import آن‌را حذف می‌کنیم. با این تغییر، تمام آدرس‌هایی مانند:
const apiEndpoint = apiUrl + "/users";
را به صورت زیر ویرایش می‌کنیم:
const apiEndpoint =  "/users";
در ادامه برای تامین مقدار apiUrl به صورت خودکار، به فایل src\services\httpService.js مراجعه کرده و در ابتدای آن، یک سطر زیر را اضافه می‌کنیم:
 axios.defaults.baseURL = process.env.REACT_APP_API_URL;
به این ترتیب تمام درخواست‌های ارسالی توسط Axios، دارای baseURL ای خواهند شد که از فایل متغیر محیطی جاری تامین می‌شود. همانطور که پیش‌تر نیز عنوان شد، این مقدار در زمان Build، با مقدار ثابتی که از فایل env جاری خوانده می‌شود، جایگزین خواهد شد.
همچنین adminRoleName مورد نیاز در فایل src\services\authService.js را نیز از همان فایل env جاری تامین می‌کنیم:
const adminRoleName =  process.env.REACT_APP_ADMIN_ROLE_NAME;
پس از این تغییرات، نیاز است برای حالت توسعه، یکبار دیگر دستور npm start و یا برای حالت تولید، دستور npm run build را اجرا کرد تا اطلاعات درج شده‌ی در فایل‌های env.، پردازش و جایگزین شوند.


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: sample-34-frontend.zip و sample-34-backend.zip