مطالب
معرفی چند پروژه‌ی مهم Typescript
فلسفه‌ی بوجود آمدن زبان Typescript یکی از شنیدنی‌ترین‌ها در دنیای برنامه‌نویسی است. به یاد دارم روزهای اولی که با این زبان آشنا شدم (زمانی که حدوداً ورژن 0.6 منتشر شده بود)، افراد زیادی در مورد این زبان و اینکه آیا اصلاً به این زبان احتیاج داریم یا نه نظرات زیادی دادند. مثلاً Douglas Crockford در مورد این زبان بعد از تعریف و تمجیدهایی که از Anders Hejlsberg کرده گفته :
I think that JavaScript's loose typing is one of its best features and that type checking is way overrated. TypeScript adds sweetness, but at a price. It is not a price I am willing to pay. 

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

چرا باید در مورد Typescript بدانیم؟

زبان Typescript نقاط قوت بسیاری دارد، از جمله‌ی آنها می‌توان به موارد زیر اشاره کرد:

  1. این زبان یکی از مشکلات اصلی JavaScript را که نبودن Type Safety می‌باشد حل کرده‌است. اگر چه زبانی که type safe نباشد بسیاری اوقات مزیت است! زبان typescript در حقیقت یک زبان gradual typing است.
  2. از آنجایی که typescript یک super set از زبان JavaScript است، برنامه‌نویس در لحظه از مزایای زبان JavaScript هم بهره‌مند است. مهم‌تر از آن این است که در زبان typescript به اقیانوس کتابخانه‌های JavaScript دسترسی دارید. این امکان در بسیاری زبان‌های دیگر جایگزین JavaScript وجود ندارد. حتی بهتر از آن، می‌تواند با این کتابخانه‌ها به‌صورت type safe برنامه بنویسید. تصور کنید که وقتی با $ در JQuery کار می‌کنید بتوانید از امکان intellisense استفاده کنید.
  3. بازهم از آنجا که typescript یک super set از JavaScript است، typescript قرار نیست به اسمبلی کامپایل شود؛ بلکه به زبان شناخته شده‌ای به نام JavaScript تبدیل می‌شود. بنابراین حتی می‌توان از آن JavaScript نیز یاد گرفت.
  4. کار با زبان typescript برای کسانی که با java یا سی شارپ آشنا هستند، راحت است. امکاناتی مانند genericها نیز در typescript وجود دارد.
  5. نقشه‌ی راه typescript با EcmaScript هماهنگ است. بنابراین از یادگرفتن این زبان ضرر نمی‌کنید چون قابلیت‌های این زبان را به احتمال زیاد در نسخه‌ی بعدی EcmaScript خواهید دید.
  6. این زبان توسط شرکت مایکروسافت پشتیبانی می‌شود، اوپن سورس است و تجربه‌ی Anders Hejlsberg در زمینه‌ی طراحی زبان‌های برنامه‌نویسی پشتیبان آن!
  7. پروژه‌های جالبی که در ادامه به معرفی آنها می‌پردازیم، با این زبان در حال توسعه هستند.

در این مطلب تعدادی از این پروژه‌ها را که برای خودم جذاب هستند، به شما معرفی می‌کنم.

AngularJS 2

طبیعتاً مهم‌ترین اتفاقی که برای typescript در این روزهای اخیر افتاد این بود که تیم Angular اعلام کرد که نسخه‌ی ۲ این فریم‌ورک (که این روزها در حد JQuery در وب معروف شده و استفاده می‌شود) را با زبان Typescript توسعه می‌دهد و امکاناتی که قرار بود توسط زبان AtScript پیاده‌سازی شوند، به کمک Typescript توسعه پیدا می‌کنند. تیم Typescript هم بلافاصله اعلام کرد که در نسخه‌ی 1.5 که به‌زودی منتشر می‌شود بسیاری از امکانات AtScript قرار خواهد داشت. بنابراین می‌توانید منتظر قابلیتی شبیه به Attributeهای سی‌شارپ در typescript 1.5 باشید.
همانطور که می‌دانید AngularJS مهم‌ترین فریم‌ورک حال حاضر است که برای توسعه‌ی نرم‌افزارهای SPA وجود دارد. اعلام توسعه‌ی Angular 2 به‌وسیله‌ی Typescript مطمئناً خبر خوبی برای برنامه‌نویسان typescript خواهد بود، چون این اتفاق باعث بهبود سریع‌تر این زبان می‌شود.

Definitely Typed

اگرچه نمی‌توان این پروژه را در سطح دیگر پروژه‌هایی که در این مقاله معرفی می‌شود قرار داد، ولی اهمیت آن من را مجبور کرد که در این مقاله در موردش صحبت کنم. پروژه‌ی Definitely Typed در حقیقت استفاده از کتابخانه‌های دیگر JavaScript را در typescript ممکن می‌سازد. این پروژه برای پروژه‌های دیگری مانند JQuery، AngularJS، HighCharts، Underscore و هر چیزی که فکرش را بکنید Type Definition تعریف کرده. اگر هم کتابخانه‌ای که شما می‌خواستید در این پروژه نبود، دلیلش این است که اضافه کردن آن را به شما واگذار کرده‌اند! Type Definitionها در Typescript یکی از قابلیت‌های این زبان هستند برای اینکه بتوان با کتابخانه‌های JavaScript به‌صورت Type safe کار کرد.

shumway

حتماً از شنیدن اینکه این پروژه قرار است چه کاری انجام دهد شوکه خواهید شد! shumway که توسط موزیلا توسعه می‌یابد قرار است همان flash player باشد! البته این پروژه هنوز در مراحل اولیه‌ی توسعه است ولی اگر بخواهید می‌توانید دموی این پروژه را اینجا  ببینید.

Fayde

پروژه‌ی Fayde هم Silverlight را هدف گرفته است. البته مانند shumway موسسه‌ی معروفی از آن حمایت نمی‌کند.

Doppio

پروژه‌ی Doppio در حقیقت یک Java Virtual Machine است که روی Browser هم می‌تواند اجرا شود. از جمله کارهای جالبی که با این پروژه می‌توان کرد، کامپایل کردن کد جاوا، Disassemble کردن یک فایل class، اجرای یک فایل JAR و حتی ارتباط با JavaScript هستند.

TypeFramework

این پروژه برای افرادی خوب است که هم به NodeJS علاقمند هستند و هم به ASP.NET MVC. پروژه‌ی TypeFramework در حقیقت پیاده‌سازی مدل ASP.NET MVC در NodeJS است. Controllerها، Actionها، ActionResultها و حتی ActionFilterها با همان تعریف موجود در ASP.NET MVC در این فریم‌ورک وجود دارند.

MAYHEM

این پروژه یک فریم‌ورک کاملی برای طراحی و توسعه‌ی نرم‌افزارهای Enterprise است. در شرح این پروژه آمده است که بر خلاف اینکه همه‌ی فریم‌ورک‌ها روی حجم فایل، سرعت و... تمرکز دارند این پروژه بر درستی معماری تأکید دارد. احتمالاً استفاده از این فریم‌ورک برای پروژه‌های طولانی مدت و بزرگ مناسب است. اگرچه از طرف دیگر احتمالاً یاد گرفتن این فریم‌ورک هم کار سختی خواهد بود.

حرف آخر

حرف آخر اینکه به نظر می‌رسد Typescript زبانی است که ارزش وقت گذاشتن دارد و اگر خواستید Typescript را یاد بگیرید نگاه کردن به کدهای این پروژه‌ها حتماً کلاس درس پرباری خواهد بود. چه کسی می‌داند، شاید شما بخواهید در توسعه‌ی یکی از این پروژه‌ها مشارکت کنید!
نکته‌ی بعد از آخر هم اینکه اگر خواستید به‌طور جدی با این زبان برنامه‌نویسی کنید نگاهی به tslint و typedoc هم بیاندازید.
مطالب
Blazor 5x - قسمت 16 - کار با فرم‌ها - بخش 4 - تهیه سرویس‌های آپلود تصاویر
در ادامه می‌خواهیم برای هر اتاق ثبت شده، تعدادی تصویر مرتبط را نیز به سرور آپلود کرده و مشخصات آن‌ها را در بانک اطلاعاتی ثبت کنیم. به همین جهت در این قسمت سرویس ثبت اطلاعات تصاویر در بانک اطلاعاتی و سرویس آپلود فایل‌ها را تهیه می‌کنیم.


تعریف موجودیت و DbSet تصاویر یک اتاق هتل

برای اینکه بتوان اطلاعات تصاویر آپلودی را در بانک اطلاعاتی ثبت کرد، نیاز است یک رابطه‌ی یک به چند را بین یک اتاق و تصاویر مرتبط با آن برقرار کرد. به همین جهت ابتدا به پروژه‌ی BlazorServer.Entities.csproj مراجعه کرده و موجودیت ثبت اطلاعات تصاویر را تعریف می‌کنیم:
using System.ComponentModel.DataAnnotations.Schema;

namespace BlazorServer.Entities
{
    public class HotelRoomImage
    {
        public int Id { get; set; }

        public string RoomImageUrl { get; set; }

        [ForeignKey("RoomId")]
        public virtual HotelRoom HotelRoom { get; set; }
        public int RoomId { get; set; }
    }
}
که در اینجا باید سر دیگر این رابطه‌ی one-to-many، در جدول HotelRoom نیز تعریف شود:
namespace BlazorServer.Entities
{
    public class HotelRoom
    {
        // ...
        public virtual ICollection<HotelRoomImage> HotelRoomImages { get; set; }
    }
}
در آخر باید این موجودیت جدید را به Context برنامه معرفی کرد. برای اینکار به پروژه‌ی BlazorServer.DataAccess مراجعه کرده و DbSet متناظری را تعریف می‌کنیم:
namespace BlazorServer.DataAccess
{
    public class ApplicationDbContext : DbContext
    {
        public DbSet<HotelRoomImage> HotelRoomImages { get; set; }

        // ...
    }
}
پس از این تغییرات، نیاز است یکبار دیگر عملیات Migrations را اجرا کرد، تا ساختار متناظر بانک اطلاعاتی این تغییرات ایجاد شود. بنابراین توسط خط فرمان به پوشه‌ی پروژه‌ی BlazorServer.DataAccess وارد شده و دستورات زیر را اجرا می‌کنیم. در اینجا نگارش 5.0.3 باید معادل نگارشی از EF-Core باشد که از آن در حال استفاده‌اید:
dotnet tool update --global dotnet-ef --version 5.0.3
dotnet build
dotnet ef migrations --startup-project ../BlazorServer.App/ add Init --context ApplicationDbContext
dotnet ef --startup-project ../BlazorServer.App/ database update --context ApplicationDbContext
در مورد این دستورات در قسمت 13 بیشتر بحث شده‌است.


تعریف مدل UI متناظر با هر تصویر

همانطور که در قسمت 13 نیز عنوان شد، در حین کار با رابط کاربری برنامه، با موجودیت‌های بانک اطلاعاتی، به صورت مستقیم کار نخواهیم کرد و بر اساس نیازهای برنامه، یکسری کلاس DTO را تعریف می‌کنیم. بنابراین به پروژه‌ی BlazorServer.Models مراجعه کرده و DTO متناظر با HotelRoomImage را به صورت زیر اضافه می‌کنیم:
namespace BlazorServer.Models
{
    public class HotelRoomImageDTO
    {
        public int Id { get; set; }

        public int RoomId { get; set; }

        public string RoomImageUrl { get; set; }
    }
}
و همچنین جهت سهولت تبدیل اطلاعات بین موجودیت تعریف شده و DTO ی آن، نگاشت AutoMapper دو طرفه‌ای را در پروژه‌ی BlazorServer.Models.Mappings برقرار می‌کنیم:
using AutoMapper;
using BlazorServer.Entities;

namespace BlazorServer.Models.Mappings
{
    public class MappingProfile : Profile
    {
        public MappingProfile()
        {
            // ...
            CreateMap<HotelRoomImageDTO, HotelRoomImage>().ReverseMap(); // two-way mapping
        }
    }
}

تعریف سرویس کار با HotelRoomImage

در اینجا نیز همانند سرویسی که برای انجام عملیات تجاری مرتبط با یک اتاق هتل، در قسمت 13 پیاده سازی کردیم، سرویس دیگری را در پروژه‌ی BlazorServer.Services برای کار با تصاویر اتاق‌ها تهیه می‌کنیم:
namespace BlazorServer.Services
{
    public interface IHotelRoomImageService
    {
        Task<int> CreateHotelRoomImageAsync(HotelRoomImageDTO imageDTO);

        Task<int> DeleteHotelRoomImageByImageIdAsync(int imageId);

        Task<int> DeleteHotelRoomImageByRoomIdAsync(int roomId);

        Task<List<HotelRoomImageDTO>> GetHotelRoomImagesAsync(int roomId);
    }
}
برای نمونه بر اساس اطلاعات مدل UI برنامه، نیاز است بتوانیم اطلاعات یک تصویر را ثبت و یا حذف کنیم و یا لیست تصاویر یک اتاق را از بانک اطلاعاتی دریافت کنیم؛ با این پیاده سازی:
namespace BlazorServer.Services
{
    public class HotelRoomImageService : IHotelRoomImageService
    {
        private readonly ApplicationDbContext _dbContext;
        private readonly IMapper _mapper;
        private readonly IConfigurationProvider _mapperConfiguration;

        public HotelRoomImageService(ApplicationDbContext dbContext, IMapper mapper)
        {
            _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
            _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
            _mapperConfiguration = mapper.ConfigurationProvider;
        }

        public async Task<int> CreateHotelRoomImageAsync(HotelRoomImageDTO imageDTO)
        {
            var image = _mapper.Map<HotelRoomImage>(imageDTO);
            await _dbContext.HotelRoomImages.AddAsync(image);
            return await _dbContext.SaveChangesAsync();
        }

        public async Task<int> DeleteHotelRoomImageByImageIdAsync(int imageId)
        {
            var image = await _dbContext.HotelRoomImages.FindAsync(imageId);
            _dbContext.HotelRoomImages.Remove(image);
            return await _dbContext.SaveChangesAsync();
        }

        public async Task<int> DeleteHotelRoomImageByRoomIdAsync(int roomId)
        {
            var imageList = await _dbContext.HotelRoomImages.Where(x => x.RoomId == roomId).ToListAsync();
            _dbContext.HotelRoomImages.RemoveRange(imageList);
            return await _dbContext.SaveChangesAsync();
        }

        public Task<List<HotelRoomImageDTO>> GetHotelRoomImagesAsync(int roomId)
        {
            return _dbContext.HotelRoomImages
                            .Where(x => x.RoomId == roomId)
                            .ProjectTo<HotelRoomImageDTO>(_mapperConfiguration)
                            .ToListAsync();
        }
    }
}
پس از این تعاریف، به فایل BlazorServer\BlazorServer.App\Startup.cs مراجعه کرده و این سرویس را به سیستم تزریق وابستگی‌های برنامه معرفی می‌کنیم:
namespace BlazorServer.App
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<IHotelRoomImageService, HotelRoomImageService>();
            // ...


تهیه سرویسی برای آپلود فایل‌های یک برنامه‌ی Blazor Server به سرور

جهت ساده سازی کار آپلود، در برنامه‌های Blazor Server، سرویس جدید FileUploadService را به پروژه‌ی BlazorServer.Services اضافه می‌کنیم:
using Microsoft.AspNetCore.Components.Forms;
using System.Threading.Tasks;

namespace BlazorServer.Services
{
    public interface IFileUploadService
    {
        void DeleteFile(string fileName, string webRootPath, string uploadFolder);
        Task<string> UploadFileAsync(IBrowserFile inputFile, string webRootPath, string uploadFolder);
    }
}
کار آن حذف یک فایل، بر اساس مسیر آن است و همچنین دریافت یک IBrowserFile از کاربر و ذخیره سازی اطلاعات آن در سرور؛ با این پیاده سازی:
using Microsoft.AspNetCore.Components.Forms;
using System;
using System.IO;
using System.Threading.Tasks;

namespace BlazorServer.Services
{
    public class FileUploadService : IFileUploadService
    {
        private const int MaxBufferSize = 0x10000;

        public void DeleteFile(string fileName, string webRootPath, string uploadFolder)
        {
            var path = Path.Combine(webRootPath, uploadFolder, fileName);
            if (File.Exists(path))
            {
                File.Delete(path);
            }
        }

        public async Task<string> UploadFileAsync(IBrowserFile inputFile, string webRootPath, string uploadFolder)
        {
            createUploadDir(webRootPath, uploadFolder);
            var (fileName, imageFilePath) = getOutputFileInfo(inputFile, webRootPath, uploadFolder);

            using (var outputFileStream = new FileStream(
                        imageFilePath, FileMode.Create, FileAccess.Write,
                        FileShare.None, MaxBufferSize, useAsync: true))
            {
                using var inputStream = inputFile.OpenReadStream();
                await inputStream.CopyToAsync(outputFileStream);
            }

            return $"{uploadFolder}/{fileName}";
        }

        private static (string FileName, string FilePath) getOutputFileInfo(
                    IBrowserFile inputFile, string webRootPath, string uploadFolder)
        {
            var fileName = Path.GetFileName(inputFile.Name);
            var imageFilePath = Path.Combine(webRootPath, uploadFolder, fileName);
            if (File.Exists(imageFilePath))
            {
                var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName);
                var fileExtension = Path.GetExtension(fileName);
                fileName = $"{fileNameWithoutExtension}-{Guid.NewGuid()}{fileExtension}";
                imageFilePath = Path.Combine(webRootPath, uploadFolder, fileName);
            }
            return (fileName, imageFilePath);
        }

        private static void createUploadDir(string webRootPath, string uploadFolder)
        {
            var folderDirectory = Path.Combine(webRootPath, uploadFolder);
            if (!Directory.Exists(folderDirectory))
            {
                Directory.CreateDirectory(folderDirectory);
            }
        }
    }
}
اگر در ASP.NET Core، اطلاعات فایل ارسالی به سرور، توسط IFormFile به اکشن متدهای کنترلرها ارسال می‌شود، در برنامه‌های Blazor Server اینکار توسط IBrowserFile صورت می‌گیرد. کلیات کار با آن، بسیار شبیه به IFormFile است و اگر به مطلب «بررسی روش آپلود فایل‌ها در ASP.NET Core» مراجعه کنید، تفاوت آنچنانی را مشاهده نخواهید کرد. تنها تفاوت پیاده سازی که در اینجا وجود دارد، نیاز به استفاده‌ی از متد ()inputFile.OpenReadStream جهت دسترسی به محتوای فایل آپلودی، برای ذخیره‌ی آن در سمت سرور است؛ وگرنه مابقی کدهای آپلود آن، با ASP.NET Core یکی است.
همچنین برای دسترسی به IBrowserFile در یک سرویس، نیاز است وابستگی زیر را نیز به پروژه‌ی سرویس‌ها اضافه کرد:
<Project Sdk="Microsoft.NET.Sdk">
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="5.0.3" />
  </ItemGroup>
</Project>
پس از آن، به فایل BlazorServer\BlazorServer.App\Startup.cs مراجعه کرده و این سرویس را به سیستم تزریق وابستگی‌های برنامه معرفی می‌کنیم:
namespace BlazorServer.App
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<IFileUploadService, FileUploadService>();
            // ...
در قسمت بعد، از این سرویس‌ها جهت مدیریت آپلود تصاویر استفاده خواهیم کرد.


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: Blazor-5x-Part-16.zip
مطالب
باگ Directory Traversal در سایت
من فایل‌های سایت جاری رو در مسیر استاندارد app_data ذخیره سازی می‌کنم. علت هم این است که این پوشه، جزو پوشه‌های محافظت شده‌ی ASP.NET است و کسی نمی‌تواند فایلی را مستقیما از آن دریافت و یا سبب اجرای آن با فراخوانی مسیر مرتبط در مرورگر شود.
این مساله تا به اینجا یک مزیت مهم را به همراه دارد: اگر شخصی مثلا فایل shell.aspx را در این پوشه ارسال کند، از طریق مرورگر قابل اجرا و دسترسی نخواهد بود و کسی نخواهد توانست به این طریق به سایت و سرور دسترسی پیدا کند.
برای ارائه این نوع فایل‌ها به کاربر، معمولا از روش خواندن محتوای آن‌ها و سپس flush این محتوا در مرورگر کاربر استفاده می‌شود. برای نمونه اگر به لینک‌های سایت دقت کرده باشید مثلا لینک‌های تصاویر آن به این شکل است:
http://site/file?name=image.png
Image.png نام فایلی است در یکی از پوشه‌های قرار گرفته شده در مسیر app_data.
File هم در اینجا کنترلر فایل است که نام فایل را دریافت کرده و سپس به کمک FilePathResult و return File آن‌را به کاربر ارائه خواهد داد.
تا اینجا همه چیز طبیعی به نظر می‌رسد. اما ... مورد ذیل چطور؟!


لاگ خطاهای فوق مرتبط است به سعی و خطای شب گذشته یکی از دوستان جهت دریافت فایل web.config برنامه!
متدهای Server.MapPath یا متد return File و امثال آن تمامی به کاراکتر ویژه ~ (اشاره‌گر به ریشه سایت) به خوبی پاسخ می‌دهند. به عبارتی اگر این بررسی امنیتی انجام نشده باشد که کاربر چه مسیری را درخواست می‌کند، محتوای کامل فایل web.config برنامه به سادگی قابل دریافت خواهد بود (به علاوه هر آنچه که در سرور موجود است).

چطور می‌شود با این نوع حملات مقابله کرد؟
دو کار الزامی ذیل حتما باید انجام شوند:
الف) با استفاده از متد Path.GetFileName نام فایل را از کاربر دریافت کنید. به این ترتیب تمام زواید وارد شده حذف گردیده و فقط نام فایل به متدهای مرتبط ارسال می‌شود.
ب) بررسی کنید مسیری که قرار است به کاربر ارائه شود به کجا ختم شده. آیا به c:\windows اشاره می‌کند یا مثلا به c:\myapp\app_data ؟
اگر به لاگ فوق دقت کرده باشید تا چند سطح بالاتر از ریشه سایت هم جستجو شده.


نتیجه گیری:
اگر در برنامه‌های وب خود (فرقی نمی‌کند مرتبط به چه فناوری است)، نام فایلی را از کاربر جهت ارائه محتوایی به او دریافت و از این نام فایل بدون هیچ نوع بررسی خاصی، مستقیما در برنامه استفاده می‌کنید، برنامه شما به مشکل امنیتی Directory Traversal مبتلا است.


پ.ن.
1- این باگ امنیتی در سایت وجود داشت که توسط یکی از دوستان در روزهای اول آن گزارش شد؛ ضمن تشکر!
2- از این نوع اسکن‌ها در لاگ‌های خطاهای سایت جاری زیاد است. برای مثال به دنبال فایل‌هایی مانند DynamicStyle.aspx و css.ashx یا theme.ashx می‌گردند. حدس من این است که در یکی از پرتال‌های معروف یا افزونه‌های این نوع پرتال‌ها فایل‌های یاد شده دارای باگ فوق هستند. فایل‌های ashx عموما برای flush یک فایل یا محتوا به درون مرورگر کاربر در برنامه‌های ASP.NET Web forms مورد استفاده قرار می‌گیرند.
 
نظرات مطالب
Blazor 5x - قسمت یازدهم - مبانی Blazor - بخش 8 - کار با جاوا اسکریپت
روش استفاده از TypeScript در پروژه‌های Blazor
شاید علاقمند باشید تا اسکریپت‌های مورد نیاز یک پروژه‌ی Blazor را با TypeScript تهیه کنید؛ تا از مزایای بررسی نوع‌ها، intellisense قوی، null checking و غیره بهره‌مند شوید و سپس توسط کامپایلر آن، حاصل را به کدهای نهایی js تبدیل کنید. برای اینکار می‌توان مراحل زیر را طی کرد:

الف) تهیه فایل تنظیمات کامپایلر TypeScript
نیاز است فایل tsconfig.json را در ریشه‌ی پروژه، جائیکه فایل csproj قرار دارد، با محتوای زیر ایجاد کرد:
{
  "compilerOptions": {
    "strict": true,
    "removeComments": false,
    "sourceMap": false,
    "noEmitOnError": true,
    "target": "ES2020",
    "module": "ES2020",
    "outDir": "wwwroot/scripts"
  },
  "include": [
    "Scripts/**/*.ts"
  ],
  "exclude": [
    "node_modules"
  ]
}
در این حالت فرض بر این است که فایل‌های ts. در پوشه‌ی scripts قرار گرفته‌اند و فایل‌های نهایی کامپایل شده در پوشه‌ی wwwroot/scripts تولید خواهند شد.

ب) فعالسازی کامپایلر TypeScript به ازای هر بار build برنامه
برای اینکار نیاز است فایل csproj را به صورت زیر تکمیل کرد:
<Project Sdk="Microsoft.NET.Sdk.Razor">
  <ItemGroup>
    <PackageReference Include="Microsoft.TypeScript.MSBuild" Version="4.3.5">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>
  <ItemGroup>
    <Content Remove="tsconfig.json" />
  </ItemGroup>
  <ItemGroup>
    <TypeScriptCompile Include="tsconfig.json">
      <CopyToOutputDirectory>Never</CopyToOutputDirectory>
    </TypeScriptCompile>
  </ItemGroup>
</Project>
با اینکار ابزار TypeScript.MSBuild اضافه شده، بر اساس tsconfig.json قسمت الف، کار کامپایل فایل‌های ts را به صورت خودکار انجام می‌دهد.

ج) یک مثال از تبدیل کدهای js به ts
فرض کنید کدهای سراسری زیر را داریم که به شیء window اضافه شده‌اند:
window.exampleJsFunctions = {
  showPrompt: function (message) {
    return prompt(message, 'Type anything here');
  }
};
اکنون برای تبدیل آن به ts.، می‌توان به صورت زیر، فضای نام و کلاسی را ایجاد کرد:
namespace JSInteropWithTypeScript {
   export class ExampleJsFunctions {
        public showPrompt(message: string): string {
            return prompt(message, 'Type anything here');
        }
    }
}

export function showPrompt(message: string): string {
   var fns = new JSInteropWithTypeScript. ExampleJsFunctions();
   return fns.showPrompt(message);
}
قسمت مهم آن، export function انتهایی است. این موردی است که توسط Blazor قابل شناسایی و استفاده است.

د) روش استفاده از خروجی کامپایل شده‌ی TypeScript در کامپوننت‌های Blazor
پس از کامپایل قطعه کد فوق، ابتدا مسیر قابل دسترسی به فایل js قرار گرفته شده در پوشه‌ی wwwroot را مشخص می‌کنیم که همواره با الگوی زیر است. همچنین اینبار IJSObjectReference است که امکان دسترسی به export function یاد شده را میسر می‌کند:
private const string ScriptPath = "./_content/----namespace-here---/scripts/file.js";
private IJSObjectReference scriptModule;
دو تعریف فوق، فیلدهایی هستند که در سطح کامپوننت تعریف می‌شوند. سپس مقدار دهی آن‌ها در OnAfterRenderAsync صورت می‌گیرد تا کار import ماژول را انجام دهد:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
  if (scriptModule == null)
    scriptModule = await JSRuntime.InvokeAsync<IJSObjectReference>("import", ScriptPath);
پس از این مرحله، امکان کار با ماژول بارگذاری شده، به صورت متداولی میسر می‌شود و می‌توان export function‌ها را در اینجا فراخوانی کرد:
await scriptModule.InvokeVoidAsync("exported fn name", params);
در آخر کار هم باید آن‌را dispose کرد؛ که روش آن به صورت زیر است:
- ابتدا باید این کامپوننت، IAsyncDisposable را پیاده سازی کند:
public partial class MyComponent : IAsyncDisposable
سپس پیاده سازی آن به صورت زیر انجام می‌شود:
public async ValueTask DisposeAsync()
{
  if (scriptModule != null)
  {
    await scriptModule.DisposeAsync();
  }
}
پاسخ به بازخورد‌های پروژه‌ها
Helper AjaxDialog چطوری کار میکنه؟
سؤال شما «همون AjaxDialog هست که شامل LinkDialog و ButtonDialog می‌باشد» بود. یعنی این متدها را پیشتر مشاهده کردید.
حاصل جستجوی این‌ها در کل پروژه مانند Ajax.LinkDialog و Ajax.ButtonDialog فایل‌های استفاده شده را مشخص می‌کنند. مثال‌های استفاده را در این فایل‌ها پیدا کنید. پیشنیاز آن که jQuery UI Dialog هست پیشتر عنوان شد و باید به پروژه شما اضافه شود. نحوه‌ی استفاده نهایی از این متدهای کمکی در فایل adminjs.js وجود دارد.
Find all "Ajax.ButtonDialog", Subfolders, Find Results 1, Entire Solution, ""
Matching lines: 20    Matching files: 10    Total files searched: 750
مطالب
آشنایی با ساختار IIS قسمت یازدهم
در این مطلب و همینطور مطلب بعدی قرار است به مبحث لاگ فایل‌ها Logfile بپردازیم. همانطور که می‌دانید سیستم IIS مثل هر سیستم دیگری لاگ هایی دارد که به مرور زمان این لاگ‌ها میتوانند مقدار زیادی از ظرفیت دیسک سخت را به خود اختصاص بدهند و این عمل میتواند موجب بروز مشکلاتی در سرور شود. به خوبی به یاد دارم که برای یکی از مشتریانم VPS تهیه نموده بودیم و بعد از یک سال با من تماس گرفت که سایت بالا نمی‌آید و وقتی بررسی شد، دیدم که از فضای دیسک سخت چند گیگابایتی، تنها چند مگابایت به طور ناچیز فضا برایش باقی مانده است و باعث شده است سرور از کار بیفتد. پس از بررسی متوجه شدیم تمام این فضاها توسط لاگ فایل‌ها پر شده است و از آنجا که سرویس دهنده تا مبلغی را به عنوان مدیریت سرور، ماهانه دریافت نکند، مدیریت این سرور مجازی را به عهده نداشته اند که البته بعدها با انتقال به یک سرور دیگر از یک سرویس دهنده دیگر مشکلات ما در مورد سرور برای همیشه حل شد.
پس این داستان به خوبی روشن می‌کند که مدیریت این لاگ‌ها چقدر میتواند مهم و حیاتی باشد. آقای تیم وَن از تیم تحریریه مایکروسافت در بخش IIS موارد زیر را برای مدیریت لاگ‌ها بر می‌شمارد:
  • فعال سازی فشرده سازی
  • انتقال لاگ‌ها به یک سیستم راه دور
  • حذف لاگ فایل‌های قدیمی از طریق اسکریپت نویسی
  • حذف لاگ فایل‌های قدیمی توسط IIS Log File Cleaner

فشرده سازی دایرکتوری لاگ فایل ها
مسیر ذخیره لاگ فایل‌ها در آدرس زیر می‌باشد. به این آدرس رفته و Properties دایرکتوری مورد نظر  را باز کنید و در برگه‌ی General آن، گزینه‌ی Advanced را زده تا در کادر جدیدی که باز می‌شود، گزینه‌ی Compress contents to save disk space را انتخاب کنید تا محتویات در هنگام ذخیره روی دیسک سخت فشرده شوند.
%SystemDrive%\inetpub\logs\LogFiles

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


انتقال لاگ فایل‌ها به یک سیستم راه دور

همانطور که در بالا اشاره کردیم محل پیش فرض ذخیره سازی لاگ‌ها درمسیر

%SystemDrive%\inetpub\logs\LogFiles

قرار دارد و این محل ذخیره سازی برای هر سرور یا حتی یک وب سایت خاص در صفحه تنظیمات Logging مشخص شده است و شما در میتوانید این لاگ‌ها را حتی برای کل سرور یا مربوط به یک سایت خاص، به سروری دیگر انتقال دهید. این امکان می‌تواند به امنیت سیستم هم کمک فراوانی کند تا اگر دیسک محلی Local Disk هم دچار مشکل شد، باز خواندن لاگ فایل‌ها میسر باشد و با استفاده از ابزارهای تحلیل لاگ فایل ها، آن‌ها را مورد بررسی قرار دهیم. برای تغییر محل ذخیره سازی لاگ‌ها به یک سیستم راه دور، راه حل زیر را طی کنید.

در IIS وب سایتی را که میخواهید لاگ آن انتقال یابد، انتخاب کنید؛ یا اگر لاگ کل سیستم IIS را میخواهید انتقال بدهید نام سرور را در لیست درختی انتخاب کنید و از ماژول‌های سمت راست، ماژول Logging را انتخاب کنید و در قسمت Directory که محل ذخیره سازی فعلی لاگ‌ها را نوشته شده است، به صورت UNC آدرس دهی کنید. در آدرس زیر اولی نام سرور است Contoso-server1\\ و دومی هم Logs نام پوشه‌ای که به اشتراک گذاشته شده است.


حذف لاگ فایل‌های قدیمی با استفاده از اسکریپت

با این روش میتوانید لاگ فایل هایی را که بعد از مدتی معین که دلخواه شما هست، از سیستم حذف نمایید و اگر این اسکریپت را زمان بندی خودکار نمایید، می‌توانید از مراقبت مداوم و ثابت این کار نیز رها شوید.

با ستفاده از VBScript بررسی می‌کنیم که اگر مثلا عمر لاگ فایل به 30 روز رسیده است، باید حذف شوند. خط دوم کد زیر نهایت عمر یک لاگ فایل را مشخص می‌کند:

sLogFolder = "c:\inetpub\logs\LogFiles"
iMaxAge = 30   'in days
Set objFSO = CreateObject("Scripting.FileSystemObject")
set colFolder = objFSO.GetFolder(sLogFolder)
For Each colSubfolder in colFolder.SubFolders
        Set objFolder = objFSO.GetFolder(colSubfolder.Path)
        Set colFiles = objFolder.Files
        For Each objFile in colFiles
                iFileAge = now-objFile.DateCreated
                if iFileAge > (iMaxAge+1)  then
                        objFSO.deletefile objFile, True
                end if
        Next
Next

اسکریپت بالا تمامی subfolder‌ها را برای همه سایت‌ها بررسی کرده و لاگ‌های آنان را حذف می‌کند. ولی اگر دوست دارید این عملیات را تنها به یک وب سایت محدود کنید، باید مسیر را در خط اول دقیق‌تر مشخص کنید.

برای اجرای دستی اسکریپت در cmd تایپ کنید:

cscript.exe c:\scripts\retentionscript.vbs

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


زمان بندی اجرای اسکریپت

server manager (قابل تست در ویندوزهای سرور)  را باز کرده و از منوی Tools گزینه Task Scheduler را انتخاب کنید و در قسمت Actions گزینه Create Task را انتخاب نمایید. در کادر باز شده نام "Delete Log Files " را برای مثال برگزینید و در قسمت Security هم کاربری که اجازه اجرای اسکریپت را دارد مشخص کنید.

برگه Triggers را انتخاب کرده و گزینه New را انتخاب کنید و عملیات زمان بندی را تنظیم کنید و حتما بعد از زمان بندی مطمئن باشید که تیک Enabled فعال است.

در برگه Actions هم گزینه New را انتخاب کنید؛ در کادر باز شده از لیست Start a program را انتخاب کرده و در قسمت Program\script، دستور cscript را ذکر نمایید و به عنوان آرگومان ورودی Add arguments  هم مسیر اسکریپت خود را ذکر نمایید و کادر را تایید کنید.

برای آغاز زمان بندی در لیست وظیفه‌های فعال active task pane، وظیفه ای که الان ساخته اید را اجرا کرده و به مسیر ذخیره لاگ‌ها رفته و می‌بینید که لاگ‌های مورد نظر حذف شده‌اند؛ پس از صحت اجرای اسکریپت مطمئن می‌شویم. دوباره به لیست وظایف رفته و گزینه End را بزنید تا وظیفه، در حالت Ready قرار گیرد تا از همین الان فرایند زمان بندی اجرای اسکریپت آغاز شود.


حذف لاگ فایل‌ها با استفاده از IIS Log Cleaner Tools

ساده‌ترین ابزار برای مدیریت حذف لاگ فایل هاست که هر یک ساعت یکبار اجرا شده و لاگ فایل‌های تاریخ گذشته را که زمانش را شما تعیین می‌کنید، به سمت سطل زباله که البته درستش بازیافت است Recycle Bin انتقال میدهد تا از ضرر از دست دادن لاگ‌ها جلوگیری کند که بعدا شما میتوانید آن‌ها را به صورت دستی حذف کنید. همچنین عملیات خودکار حذف را نیز می‌توان متوقف نمود.

ابتدا برنامه را از اینجا  دانلود کنید. موقعیکه برنامه را اجرا کنید، در نوتیفیکیشن taskbar می‌نشیند و برنامه با یک پیغام به شما اعلام می‌کند، این اولین بار است که برنامه را باز کرده‌اید. پس یک سر به setting آن بزنید؛ با انتخاب گزینه‌ی settings برنامه بسته شده و فایل Settings.txt برای شما باز می‌شود که مدت زمان عمر لاگ فایل و مسیر ذخیره آن‌ها، از شما پرسیده می‌شود که مقدار عمر هر لاگ فایل به طور پیش فرض 30 روز و مسیر ذخیره‌ی لاگ‌ها همان مسیر پیش فرض IIS است که اگر شما دستی آن را تغییر داده اید، با پرسیدن آن، از محل لاگ‌ها اطمینان کسب می‌کند. در صورتی که قصد تغییری را در فایل، دارید آن را تغییر داده و ذخیره کنید و برنامه را مجددا اجرا کنید.

نکات نهایی در مورد این برنامه :

  • اگر از ابزار IIS Cleaner Tool استفاده می‌کنید باید دستی سطل بازیافت را هم پاک کنید و هم اینکه میتوانید یک محدودیت حجمی برای Recycle Bin قرار دهید که اگر به یک حدی رسید، خودکار پاک کند تا مشکلی برای سیستم عامل ایجاد نشود که البته به طور پیش فرض چنین است.
  • برنامه بالا به طور پیش فرض ریشه‌ی لاگ‌ها را حذف می‌کند. پس اگر میخواهید فقط سایت خاصی را مد نظر داشته باشد، آدرس دایرکتوری آن را اضافه کنید. البته چون این برنامه فقط روی یک دایرکتوری کار می‌کند و شما چند وب سایت دارید و مثلا میخواهید سه تای آن‌ها را پاکسازی کنید، چاره‌ی جز استفاده از اسکریپت‌های با زمان بندی ندارید.
  • برنامه‌ی بالا فقط فایل هایی با پسوند log را به سطل بازیافت انتقال می‌دهد.
  • برنامه‌ی بالا یک سرویس نیست و باید به طور دستی توسط کاربر اجرا گردد. پس اگر ریست هم شد باید دستی اجرا شود یا آن را به داخل پوشه startup بکشید.
  • برنامه برای اجرایش نیاز به لاگین کاربر و مجوز نوشتن در آن پوشه را دارد تا به درستی کار کند.
در قسمت بعدی مبحث لاگ‌ها را ادامه خواهیم داد و با ماژول Logging در IIS و تنظیماتش آشنا خواهیم شد.
مطالب
React 16x - قسمت 1 - معرفی و شروع به کار
React یک کتابخانه‌ی جاوا اسکریپتی، برای ساخت رابط‌های کاربری سریع و تعاملی است. توسعه‌ی آن از سال 2011 در فیسبوک شروع شد و در حال حاضر محبوب‌ترین کتابخانه‌ی جاوا اسکریپتی در این رده‌است:


به همین جهت اگر می‌خواهید رزومه‌ی غنی‌تری را ارائه دهید، فراگیری React می‌تواند موقعیت‌های شغلی بیشتری را نصیب شما کند.


ساختار کلی یک برنامه‌ی React

کامپوننت‌ها (جزئی از یک رابط کاربری) قلب هر برنامه‌ی React ای را تشکیل می‌دهند. برای ساخت یک برنامه‌ی React، تعدادی کامپوننت مستقل را تهیه و با هم ترکیب می‌کنیم تا به رابط کاربری نهایی برسیم.
هر برنامه‌ی React، حداقل از یک کامپوننت تشکیل می‌شود که به آن Root component هم می‌گویند. این کامپوننت بیانگر کل برنامه‌است و دربرگیرنده‌ی مابقی Child components برنامه است. بنابراین ساختار هر برنامه‌ی React، شبیه به درختی از کامپوننت‌ها است. اگر با Angular 2 به بعد کار کرده باشید، این مفهوم برای شما آشنا است.
یک مثال: فرض کنید می‌خواهیم UI برنامه‌ای را به مانند رابط کاربری Twitter، ایجاد کنیم. هر قسمت یک صفحه‌ی توئیتر، به کامپوننت‌هایی شکسته می‌شود؛ مانند منوی راهبری، نمایش پروفایل شخص، نمایش لیست آخرین اخبار مورد علاقه‌ی شخص و نمایش فید. اگر بخواهیم این ساختار را توسط یک برنامه‌ی React شبیه سازی کنیم، در بالاترین سطح، کامپوننت root را خواهیم داشت که کار ترکیب و نمایش سایر کامپوننت‌های برنامه مانند nav bar ، trends ، profile و feed را انجام می‌دهد. اکنون در این ساختار ایجاد شده، برای مثال کامپوننت feed نیز می‌تواند از چندین کامپوننت مجزا تشکیل شود؛ مانند کامپوننت‌های tweet و like.
بنابراین هر کامپوننت، قسمتی از UI را تشکیل می‌دهد. هر کدام از آن‌ها به صورت مجزای از دیگری ساخته شده و سپس در کنار هم قرار می‌گیرند تا UI نهایی را شکل دهند:



هر کامپوننت در React به صورت یک کلاس ES6، با ساختاری که دارای یک شیء state و متد render است، تشکیل می‌شود:
class Tweet {
 state = {};
 
 render() {
 } 
}
state در اینجا همان اطلاعاتی است که قرار است در زمان نمایش این کامپوننت، رندر شود. کار متد render نیز همانطور که از نام آن نیز مشخص است، بیان نحوه‌ی تشکیل و رندر UI است. خروجی این متد، یک React Element است که در حقیقت یک شیء جاوا اسکریپتی خالص است و در نهایت به المان‌های DOM، نگاشت می‌شود. یک React Element، یک DOM Element واقعی نیست؛ بلکه تنها یک شیء جاوا اسکریپتی بیانگر DOM Element، در حافظه‌است. بنابراین یک برنامه‌ی React تشکیل شده‌است از لیستی از React Elementها در حافظه که به آن Virtual DOM هم گفته می‌شود.
مزیت کارکردن با Virtual DOM، سادگی ایجاد، تغییر و به روز رسانی آن در مقایسه با DOM واقعی است که در نهایت کار رندر عناصر UI را در مرورگر انجام می‌دهد. زمانیکه در state کامپوننتی تغییری رخ می‌دهد، یک React Element جدید تولید می‌شود. سپس React این شیء جدید را با نمونه‌ی قبلی آن مقایسه کرده و تغییرات رخ‌داده را محاسبه می‌کند. در آخر این تغییرات را به DOM واقعی اعمال می‌کند تا با Virtual DOM موجود هماهنگ شود.
بنابراین در حین کار با React، دیگر همانند کار با جاوا اسکریپت خالص و یا jQuery، مستقیما عناصر UI و DOM واقعی را تغییر نمی‌دهیم. در اینجا فقط state یک کامپوننت را تغییر می‌دهیم و سپس React، کار ایجاد شیء UI درون حافظه‌ای متناظر با آن و سپس اعمال آن‌را به UI نهایی قابل مشاهده‌ی در مرورگر، انجام می‌دهد. به همین جهت به این کتابخانه React می‌گویند! چون به تغییرات state کامپوننت‌ها واکنش نشان می‌دهد و سپس DOM واقعی را به روز می‌کند.


Angular یا React؟!

هر دوی React و Angular از لحاظ طراحی کامپوننت‌ها بسیار شبیه به هم هستند؛ اما Angular یک فریم‌ورک است و React تنها یک کتابخانه. تنها کاری را که React انجام می‌دهد، رندر View است و هماهنگ نگه داشتن آن با state کامپوننت‌ها. این تمام کاری است که React انجام می‌دهد؛ نه بیشتر و نه کمتر! بنابراین یادگیری React، بسیار سریع‌تر و ساده‌تر از Angular است. بدیهی است یک برنامه‌ی تک صفحه‌ای وب، از اجزای دیگری مانند مسیریابی و یا کار با سرویس‌های HTTP نیز تشکیل می‌شود. در React شما مختار هستید که کتابخانه‌های جانبی فراهم شده‌ی برای آن‌را خودتان انتخاب کرده و استفاده کنید؛ برخلاف روشی که در Angular مرسوم است و به صورت مشخص و ثابتی به همراه این فریم‌ورک ارائه می‌شوند.


برپایی محیط توسعه‌ی React

اولین برنامه‌ای را که برای کار با React باید نصب کنید، node.js است. البته ما در این سری قرار نیست با node.js کار کنیم؛ اما از یکی از اجزای آن به نام node package manager یا npm، برای نصب کتابخانه‌ی جاوا اسکریپتی ثالث، زیاد استفاده خواهیم کرد. پس از نصب آن، به خط فرمان مراجعه کرد و دستور زیر را صادر کنید:
> npm install -g npm@latest
این دستور npm قدیمی موجود بر روی سیستم را به روز رسانی می‌کند (اگر پیشتر یک node.js قدیمی را نصب و اکنون آن‌را به روز رسانی کرده‌اید).

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

در ادامه در خط فرمان و توسط npm، قالب create-react-app را نصب خواهیم کرد:
> npm i -g create-react-app
در اینجا سوئیچ i به معنای install است و g یعنی نصب global و سراسری بسته‌ی create-react-app. نصب سراسری یک بسته یعنی در هر پوشه‌ای می‌توان به امکانات آن دسترسی یافت و از آن استفاده کرد. اگر از سوئیچ g استفاده نمی‌شد، این بسته تنها در پوشه‌ی جاری و با سطح دید مختص به آن، نصب و قابل استفاده می‌شد.

ابزار دیگری که در این سری از آن استفاده خواهیم کرد، ادیتور بسیار معروف و محبوب VSCode است. پس از دریافت و نصب آن، چند افزونه‌ی زیر را نیز به آن اضافه خواهیم کرد:
برای نصب آن‌ها، پنل extensions را در VSCode، از نوار ابزار کنار صفحه‌ی آن، انتخاب کرده و نام‌های فوق را در آن جستجو و سپس نصب کنید.

و یا می‌توانید این فایل را اجرا کرده و تعدادی از افزونه‌های مفید VSCode را یکجا نصب کنید: install-addons.zip

همچنین قابلیت فرمت‌کردن پس از Save را نیز در VSCode فعال کنید تا پس از هربار Save، اعمال این افزونه‌ها به صورت خودکار صورت گیرد. برای این منظور گزینه‌ی file->preferences->settings را در VSCode انتخاب کرده و سپس save را جستجو کرده و Format On Save را انتخاب کنید:


علاوه بر این‌ها، جهت کار بهتر با VSCode، بهتر است بررسی کننده‌های کدهای جاوا اسکریپتی (static code analyzers) را نیز با اجرای دستور زیر نصب کنید:
> npm i -g typescript eslint tslint eslint-plugin-react-hooks

پس از این تغییرات، نیاز است یکبار VSCode را بسته و مجددا باز کنید. سپس مجددا گزینه‌ی file->preferences->settings را در VSCode انتخاب کرده و ابتدا eslint را در اینجا جستجو کنید. در صفحه‌ی نمایش تنظیمات آن، گزینه‌ی Auto fix on save آن‌را انتخاب نمائید. در آخر در همین قسمت settings، عبارت prettier را انتخاب کنید. در اینجا اگر گزینه‌ی قدیمی یکپارچگی با eslint آن هنوز وجود دارد، آن‌را از حالت انتخاب شده خارج کنید (به صورت قرمز و deprecated نمایش داده می‌شود) تا افزونه‌ی prettier بدون مشکل و خطا کار کند (disable Prettier ESLint integration).


ایجاد قالب اولین برنامه‌ی React

در ادامه برای ایجاد اولین برنامه‌ی React، از بسته‌ی create-react-app که پیشتر آن‌را نصب کردیم، استفاده می‌کنیم. برای این منظور در خط فرمان دستور زیر را صادر کنید:
> create-react-app sample-01
در اینجا sample-01 یک نام دلخواه است و در حین اجرای این دستور باید به اینترنت متصل باشید تا وابستگی‌های مرتبط با پروژه را نیز دریافت کند. برای بار اول، اجرای آن ممکن است کمی طول بکشد. اما از دفعات آتی، چون بسته‌های مرتبط را در npm-cache سیستم نیز ذخیره می‌کند، اجرای آن بسیار سریع خواهد بود.
این قالب نه تنها React را نصب می‌کند، بلکه یک development server را برای اجرا و مشاهده‌ی سریع برنامه، webpack را برای یکی کردن فایل‌ها (bundling & minification)، Babel را برای کامپایل کدهای فایل‌های JSX و ... نیز نصب می‌کند. بنابراین به این ترتیب، یک پروژه‌ی تنظیم شده و آماده‌ی استفاده و توسعه را شاهد خواهیم بود که نیازی به تنظیمات اولیه‌ی آن نیست.
پس ایجاد برنامه، وارد پوشه‌ی sample-01 شده و دستور npm start را صادر کنید:
> cd sample-01
> npm start
به این ترتیب برنامه بر روی پورت 3000، قابل دسترسی و مشاهده می‌شود:


development server آن، تغییرات فایل‌های برنامه را تحت نظر قرار می‌دهد و با هر تغییری، به صورت خودکار برنامه را در مرورگر بارگذاری مجدد خواهد کرد.


بررسی ساختار اولین پروژه‌ی React ایجاد شده

ساختار پوشه‌ها و فایل‌های مثال اولیه‌ی ایجاد شده توسط قالب create-react-app به صورت زیر است:


البته شما در این تصویر پوشه‌ی node_modules را که در کنار این پوشه‌ها قرار دارد، مشاهده نمی‌کنید. وجود یک چنین پوشه‌ی سنگینی با هزاران فایل داخل آن، کار نمایشی IDEها را با مشکل مواجه می‌کند (مصرف حافظه‌ی بالا، به همراه کند شدن شدید آن). اگر نمی‌خواهید این پوشه نمایش داده شود، در مسیر file->preferences->settings، عبارت npm را جستجو کرده و سپس در قسمت npm: exclude آن، بر روی لینک edit in settings.json کلیک کنید:


 و سپس در فایل باز شده، یک چنین تنظیمی را می‌توانید اضافه و یا ویرایش و تکمیل کنید:
  "files.exclude": {
    "**/.git": true,
    "**/.svn": true,
    "**/.hg": true,
    "**/CVS": true,
    "**/.DS_Store": true,
    "**/node_modules": true,
    "**/wwwroot": true,
    "**/bower_components": true,
    "**/**/bin": true,
    "**/**/obj": true,
    "**/packages": true
  },

در ادامه پوشه‌ی public این پروژه را مشاهده می‌کنید. تمام فایل‌هایی که قرار است به صورت عمومی توسط برنامه ارائه شوند، مانند favicon.ico و غیره، در این پوشه قرار می‌گیرند.
در این پوشه بر روی فایل index.html آن کلیک کنید تا بتوان محتوای آن‌را بهتر بررسی کرد. برای مثال در ابتدای آن، درج تعدادی متادیتا را که یکی از آن‌ها ذکر manifest.json است، مشاهده می‌کنید. کار فایل manifest.json، ارائه‌ی یک سری متادیتای خاص مخصوص دستگاه‌های موبایل است که در آن‌ها بجای favicon.ico، می‌توان از تصاویر و یا آیکن‌های بزرگتری مانند فایل‌های png موجود در پوشه‌ی public، استفاده کرد. در ادامه‌ی این فایل، به تنظیم زیر می‌رسیم:
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
div با id مساوی root، محل ارائه‌ی کل برنامه‌ی React ما است.

در پوشه‌ی src و فایل App.js آن، شاهد یک کامپوننت ابتدایی هستید که کار رندر صفحه‌ی مشکی پیش‌فرض این قالب را انجام می‌دهد. در این فایل، شاهد بازگشت یک چنین تگ‌هایی هستیم:
  return (
    <div className="App">
      <header className="App-header">
       ... 
      </header>
    </div>
  );
احتمالا تابحال چنین return ای را در برنامه‌های جاوا اسکریپتی مشاهده نکرده‌اید؛ چون درج آن‌ها در فایل‌های js به این نحو، غیرمجاز است. این تگ‌ها نه رشته‌ای هستند و نه HTML خالص. به آن jsx گفته می‌شود که مخفف JavaScript XML می‌باشد. کار آن ارائه‌ی ساختار UI ای است که قرار است رندر شود. یک چنین کدی برای اینکه قابل تفسیر و اجرا باشد، از درون کامپایلر ویژه‌ای به نام Babel عبور می‌کند و تبدیل به کدهای جاوا اسکریپتی خالصی می‌شود که برای مرورگرها قابل درک و اجرا است.
برای درک بهتر آن به آدرس https://babeljs.io/repl مراجعه کنید. سپس در سمت چپ صفحه، یک قطعه کد jsx را به یک ثابت انتساب دهید:
const element = <h1>Hello World!</h1>;


همانطور که مشاهده می‌کنید، این قطعه کد jsx (که یک رشته‌ی معمولی نیست)، توسط Babel به یک قطعه کد کاملا جاوا اسکریپتی قابل درک برای مرورگر تبدیل شده‌است:
"use strict";

var element = React.createElement("h1", null, "Hello World!");

بدیهی است نوشتن کدهای jsx، ساده‌تر از نوشتن قطعه کد فوق است و درک آن نیز به علت شباهت آن به HTML، آسان‌تر است. به همین جهت در کدهای React، ما از jsx استفاده می‌کنیم و تفسیر آن‌را به Babel واگذار خواهیم کرد.

در پوشه‌ی src، فایل مهم دیگری که وجود دارد، index.js است. این فایل نقطه‌ی آغازین برنامه را مشخص می‌کند. در قسمت‌های بعدی، محتویات این فایل را بیشتر بررسی خواهیم کرد.
در اینجا فایل serviceWorker.js را نیز مشاهده می‌کنید. این فایل به صورت خودکار توسط قالب create-react-app ایجاد شده‌است و کار آن کمک به ارائه‌ی محلی برنامه، توسط development server آن است. بنابراین ما کاری با این فایل نخواهیم داشت.


نوشتن اولین برنامه‌ی React

به پوشه‌ی src ایجاد شده مراجعه کرده و تمام فایل‌های موجود و پیش‌فرض آن‌را حذف کنید. در ادامه خودمان آن‌ها را از صفر ایجاد خواهیم کرد. برای این منظور فایل جدید و خالی src\index.js را ایجاد می‌کنیم. در ابتدای کار نیاز است تعدادی ماژول React را import کنیم.
import React from "react";

const element = <h1>Hello World!</h1>;
console.log(element);
در اینجا شیء React از ماژول react دریافت شده و سپس یک ثابت را با یک عبارت jsx مقدار دهی کرده‌ایم. چون از jsx استفاده می‌کنیم، ذکر import ابتدای فایل الزامی است؛ از این جهت که Babel به کمک آن است که می‌تواند معادل React.createElement را تولید کند.
اگر هنوز برنامه توسط دستور npm start در حال اجرا است، هر بار که فایل index.js را ذخیره می‌کنیم، خروجی نهایی را در مرورگر نمایش می‌دهد (اگر هم آن‌را بسته‌اید، یکبار از طریق خط فرمان، دستور npm start را در ریشه‌ی پروژه، صادر کنید). به این قابلیت hot module reloading هم گفته می‌شود.
در این حالت اگر به مرورگر مراجعه کنید، یک صفحه‌ی سفید را مشاهده خواهید کرد. اکنون دکمه‌ی F12 را فشرده (و یا ctrl+shift+i) و developer console مرورگر را باز کنید.


شیءای را که در اینجا مشاهده می‌کنید، همان حاصل console.log کدهای فوق است؛ به عبارتی Babel، عبارت jsx ما را تبدیل به یک شیء جاوا اسکریپتی قابل فهم برای مرورگر کرده‌است که از دیدگاه React، جزئی از همان Virtual DOM ای است که پیشتر معرفی شد (نمایش درون حافظه‌ای DOM مختص React، جهت محاسبه‌ی تغییرات، با تغییر state هر کامپوننت و سپس اعمال آن‌ها به DOM اصلی در مرورگر).
اکنون می‌خواهیم این المان را در DOM اصلی، رندر کرده و نمایش دهیم:
import React from "react";
import ReactDOM from "react-dom";

const element = <h1>Hello World!</h1>;
console.log(element);

ReactDOM.render(element, document.getElementById("root"));
برای این منظور نیاز است از متد ReactDOM.render استفاده کرد. این شیء در ماژول react-dom قرار دارد؛ به همین جهت در ابتدای فایل import شده‌است. سپس در متد render آن، ابتدا المانی که قرار است رندر شود ذکر خواهد شد و سپس محل درج آن‌را مشخص می‌کنیم که دقیقا به همان div با id مساوی root در فایل public\index.html اشاره می‌کند.
اکنون پس از ذخیره سازی فایل index.js، اگر به مرورگر مراجعه کنید، عبارت Hello World! را مشاهده خواهید کرد:


همانطور که در این تصویر نیز مشخص است، المان h1 ما را داخل div ای با id مساوی root، درج کرده‌است.

هدف از این مثال ساده، نمایش نحوه‌ی کارکرد React، در پشت صحنه بود. در یک برنامه‌ی واقعی، بجای رندر یک المان ساده در DOM، کار رندر App component را انجام خواهیم داد. کامپوننت App، کامپوننت ریشه‌ای برنامه بوده و می‌تواند شامل درختی از کامپوننت‌ها که UI نهایی را تشکیل می‌دهند، شود.


نگاهی به تنظیمات پروژه‌ی ایجاد شده

اگر فایل package.json پروژه را باز کنید، یک چنین بسته‌هایی در آن درج شده‌است:
{
  "name": "sample-01",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.11.0",
    "react-dom": "^16.11.0",
    "react-scripts": "3.2.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}
در اینجا صرفا سه بسته‌ی react، react-dom و react-scripts را در قسمت dependencies مشاهده می‌کنید که کل Importهای ما را تشکیل می‌دهند.
بسته‌ی react-scripts است که کار مدیریت چهار جزء قسمت scripts این فایل را انجام می‌دهد. برای نمونه دستور npm start ای که در اینجا تعریف شده، سبب اجرای react-scripts start می‌شود. در ادامه اگر دستور npm run build را اجرا کنیم، یک بسته‌ی نهایی بهینه سازی شده را تولید می‌کند.
آخرین دستور آن eject است. اگر دستور npm run eject را اجرا کنید، امکان سفارشی سازی پشت صحنه‌ی create-react-app را خواهید داشت؛ اما در نهایت به یک فایل package.json بسیار شلوغ خواهیم رسید (اینبار ارجاعات به Babel، Webpack و تمام ابزارهای دیگر نیز ظاهر می‌شوند). همچنین این عملیات نیز یک طرفه‌است. یعنی از این پس قرار است کنترل تمام این پشت صحنه، در اختیار ما باشد و به روز رسانی‌های بعدی create-react-app را با مشکل مواجه می‌کند. این گزینه صرفا مختص توسعه دهندگان پیشرفته‌ی React است.


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

در قسمت بعد، پیشنیازهای جاوا اسکریپتی شروع به کار با React را بررسی می‌کنیم.
پاسخ به بازخورد‌های پروژه‌ها
طراحی ماژولار با MVC و EF

با سلام و خسته نباشین
ما یک پروژه داریم تحت عنوان ModuleCore , که در یک سولوشن جداگانه است(که از نوع MVC Application است) و قصد داریم این Application رو توسعه بدیم که این ماژول کور شامل فرم‌های نصب ماژول, ایجاد صفحات بصورت مجازی و انتخاب بلوک‌ها بعنوان اجزای صفحات مجازی(بلوک‌ها همان اکشن‌ها و کنترلر‌های هر ماژول می‌باشد) است .
ماژول‌های این پروژه, هرکدام در سولوشن‌های جداگانه هستند.حالا در حال ساخت یک ماژول, بعنوان مثال ماژول نظرسنجی هستیم.ما یک سولوشن جداگانه برای این ماژول ایجاد کردیم (PoolModule). ما یک کنترلی با نام PolController ایجاد کرده ایم و یک اکشنی با نام AddPoll جهت افزودن نظرسنجی توسط مدیریت ایجاد کرده ایم.حال ما پروژه PoolModule را بیلد کرده ایم و ما   فایل دی ال ال PolModule و تمام وابستگی هایش و اسمبلی‌های پیش فرض MVC را در اختیار داریم و تمام ویو‌های مربوط به پروژه PolModule  و تمامی اسکریپت‌ها و استایل‌ها و ... را نیز دارا می‌باشد که مجموع اینها را بسته نصبی ماژول نظرسنجی می‌گوییم .
اطلاعات مربوط به نصب این ماژول را در یک فایل xml ثبت کرده ایم. حال جهت نصب این ماژول در اپلیکیشن(ماژول) ModuleCore فایل xml را به فرم نصبی پاس می‌دهیم(فرم نصبی, شامل یک کنترل فایل آپلود می‌باشد که از طریق آن ما تمام اسمبلی‌ها و فایل‌های مربوط به ماژول نظرسنجی را که در یک فایل فشرده قرار داده ایم را آپلود میکنیم). پس از آپلود فایل xml را خوانده و در یک مسیر مشخص(پوشه Modules/ModuleName/Bin  در پروژه ModuleCore ) ذخیره میکنیم. حال می‌خواهیم در RunTime از اسمبلی PoolModule  در پروژه ModuleCore استفاده کنیم .
چگونه باید اسمبلی‌های ماژول نظرسنجی را به ModuleCore بشناسونیم(می خواهیم به کنترلر‌ها و اکشن‌های ماژول نظرسنجی دسترسی داشته باشیم) بطوریکه اسمبلی‌های هر ماژول(مثلا ماژول نظرسنجی) در bin پروژه ModuleCore  نباشد و اسمبلی‌های هر ماژول در پوشه مخصوص خود ( پوشه Modules/ModuleName/Bin در پروژه ModuleCore ) باشد ؟

ممنون میشم راهنمایی کنین یا اگه لینک منابعی رو معرفی کنین. با تشکر