روزنوشته های فربد صالحی

درباره زندگی و برنامه نویسی

۵ مطلب با موضوع «برنامه نویسی» ثبت شده است

پیاده‌سازی CORS برای اَپ‌های موبایلی Ionic در Asp.Net Core

کارکرد SOP

فرض می‌کنیم از طریق یه مرورگر به وبسایت http://www.yyy.com مراجعه و در اون لاگین می‌کنیم، و این وبسایت هم شناسه‌ی این لاگین رو به صورت کوکی روی دستگاه ما ذخیره می‌کنه. تا زمانیکه این کوکی روی دستگاه ما ذخیره شده، هر وقت از این دستگاه و با همون مرورگر به این وبسایت مراجعه کنیم، با هر request این کوکی به این وبسایت ارسال میشه و وبسایت می‌تونه حساب کاربری ما رو بشناسه.

اما نکته اینجاست که حتی وقتی ما روی این دستگاه و با اون مرورگر مشخص، وبسایتی متفاوت مثل http://www.xxx.com  رو هم باز می‌کنیم، اگه این وبسایت در بخشی از عملیاتش یه request‌ به دامنه‌ی yyy بفرسته، مثلا API اون رو فراخوانی کنه، باز هم مرورگر همه‌ی کوکی‌های ذخیره شده توسط وبسایت yyy، از جمله کوکی لاگین ما رو، بدون توجه به مبدا ارسال کننده request که در اینجا xxx هستش، به yyy ارسال می‌کنه. در واقع مرورگر برای ارسال کوکی‌ها، به مقصد request توجه می‌کنه و نه ارسال‌کننده‌ی request. به این ترتیب وبسایت yyy ممکنه request ارسالی از سمت وبسایت xxx رو ارسال شده از طرف حساب کاربری ما در yyy تشخیص بده و اطلاعات محرمانه‌ی ما رو برگردونه یا دسترسی‌هایی به این وبسایت بده که نباید. 

سیاست مرورگرهای امروزی برای جلوگیری از این مشکل، Same Origin Policy یا "SOP" هستش. به طور کلی این سیاست به این صورت پیاده میشه که اسکریپت‌های بارگذاری شده توسط یک «منشاء»، نمی‌تونن به منابعی مثل API از منشاء متفاوت دسترسی داشته باشن. اما معیار یکسان بودن منشاء چیه؟

وقتی دو منبع دارای "scheme"، "host" و "port" کاملا یکسان باشند، اونوقت دارای منشاء یکسان درنظر گرفته میشن. به نظرم درک سریع موضوع با دیدن سه دسته‌ی زیر با منشاء‌های متفاوت امکان‌پذیر خواهد بود. دوتای اول فقط scheme های متفاوت دارن (http , https)، سه تای بعدی host های متفاوت دارن (example, www.example, myapp.example) و دوتای آخری پورت‌های متفاوت (8080 و 80 که مقدار پیشفرضه). بنابرین هیچکدوم از سه دسته منشاء یکسانی ندارن.

 

 

حالا میشه متوجه شد که وقتی در حال توسعه‌‌ی یه برنامه روی سیستم خودمون هستیم، با اینکه "backend"  و "frontend" هر دو دارن روی http://localhost اجرا میشن، چرا در حالت پیش فرض frontend  نمی‌تونه API رو از backend فراخوانی کنه. چون در واقع روی پورت‌های متفاوتی در حال اجرا هستن، یکی به فرض روی http://localhost:4200 و دیگری روی http://localhost:44353. بنابراین طبق توضیحات بالا منشاء یکسانی ندارن، با اینکه پروتکل (http) و host اونها (localhost) یکسانه. 

 

کارکرد CORS

گاهی لازم میشه که از محدودیت‌های اعمال شده بوسیله‌ی SOP کم کنیم. مثلا یه اپ تحت وب با انگولار نوشتیم و روی دامنه‌ی https://www.aaa.com قرار دادیم که باید API قرار گرفته روی دامنه‌ای با منشاء متفاوت مثل https://api.aaa.com رو فراخوانی کنه. برای این منظور از مفهوم Cross Origin Resource Sharing یا "CORS" استفاده می‌کنیم. در واقع ما با استفاده از CORS به تعدادی از دامنه‌ها اجازه‌ی عبور از محدودیت‌های اعمال شده بوسیله‌ی SOP رو میدیم. برای این کار، اگه API رو با Asp.Net Core پیاده کرده باشیم، در فایل "Startup.cs" و در تابع "ConfigureServices" خواهیم داشت:

public void ConfigureServices(IServiceCollection services)
        {
            services.AddCors(options =>
            {
                options.AddPolicy("AllowMyOrigin",
                builder => builder.WithOrigins("https://www.aaa.com").AllowAnyHeader());
            });

           ....
        }

در واقع ما یه Policy با اسم دلخواه "AllowMyOrigin" که هر اسم دیگه‌ای هم می‌تونه باشه تعریف کردیم که میگه به "https://www.aaa.com" اجازه‌ی دسترسی به API رو بده، با اینکه منشاء متفاوتی داره. حالا در همون فایل و در تابع "Configure" این Policy رو اِعمال می‌کنیم:

 public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            ....

            app.UseCors("AllowMyOrigin");

            .....
        }

به این ترتیب، محدودیت SOP برای https://www.aaa.com برداشته میشه. 

 

CORS برای اپ Ionic

می‌دونیم که SOP عملا برای برنامه‌‌هایی که روی مرورگر اجرا میشن مهمه و در واقع اپ‌های native موبایل دچار محدودیت در این زمینه نیستن. برنامه‌های تحت ‌وب هم که یه دامنه‌ی مشخص دارن و دیدیم که می‌تونیم  این دامنه رو استثنا کنیم. اما اپ‌های موبایل تولید شده با Ionic با توجه به «هیبریدی» بودنشون چه وضعی دارن؟

می‌دونیم که اپ‌های Ionic عملا داخل WebView اجرا میشن. بنابراین نه مثل برنامه‌های تحت وب تو دامنه‌ی مشخصی قرار دارن که اون رو استثنا کنیم،‌ و نه کاملا native هستن که SOP شاملشون نشه. بر طبق مستندات وبسایت Ionic، برای رفع این مشکل میشه از روش زیر استفاده کرد:

public void ConfigureServices(IServiceCollection services)
        {
            services.AddCors(options =>
            {
                options.AddPolicy("AllowMyOrigin",
                builder => builder.WithOrigins("capacitor://localhost", // capacitor: ios
                                               "http://localhost",      // capacitor: android | ionic webview 3.x on cordova: android
                                               "ionic://localhost",     // ionic webview 3.x on cordova : ios
                                               "http://localhost:8080", // ionic webview 2.x on cordova : ios & android
                                               ).AllowAnyHeader());

            });

            ....
        }

 

در واقع به جای اینکه یه دامنه خاص رو استثنا کنیم، چهار منشاء بالا رو که هرکدوم یک یا چند حالت رو برای اپ‌های Ionic پوشش میدن استثنا می‌کنیم و به این ترتیب محدودیت SOP برای اپ‌های Ionic برداشته میشه. 

۰ نظر
فربد صالحی

خطا در هنگام ایجاد پروژه با Ionic و اجرای اولیه

وقتی اولین پروژه‌‌‌ی Ionic رو خواستم ایجاد کنم، بعد از اینکه کتابخونه و فریمورک جاوااسکریپتی رو انتخاب و برای  پروژه اسم تعیین کردم، قالب پروژه رو باید از گزینه‌هایی مثل tab، blank و .. انتخاب می‌کردم. بعد از این مرحله کار تو مرحله‌ی زیر متوقف می‌شد:

/ Downloading and extracting blank starter ionic

و گاهی هم خطای timeout در اتصال به 10.10.34.35:443 اعلام می‌کرد. 

خیلی جستجو کردم، راه‌حل‌هایی پیشنهاد شده بود ولی مشکل رو حل نکردن. یه چیزی که به ذهنم اومده بود ولی بهش بی‌توجهی می‌کردم رو امتحان کردم و مساله حل شد. بله! روشن کردن وی.پی.ان. 

حالا اینکه چرا باید اینطور باشه و مشکل از طرف «دوستان» داخلی هستش یا خارجی رو نمی‌دونم. فقط امیدوارم در ادامه‌ی کار با Ionic از این نوع مشکلات بوجود نیاد، یا حداقل کم باشه تعدادشون.  

بعد از ایجاد پروژه، برای اجرای اولیه‌ و بررسی اینکه همه‌چی خوب پیش رفته، دستور زیر رو تو ترمینالِ VS Code اجرا کردم:

ionic serve

اینبار خطای دیگه‌ای ظاهر شد:

ionic : File C:\...\AppData\Roaming\npm\ionic.ps1 cannot be loaded because running scripts is disabled on this system. For 
more information, see about_Execution_Policies

باز هم با جستجو مشخص شد که این به سیاست‌های امنیتی سیستم عامل ویندوز برای پیشگیری از اجرای اسکریپت‌های مخرب مربوطه. توضیحات کامل رو می‌شه از اینجا مطالعه کرد.

اما برای غیرفعال کردن این ویژگی، یک راه اینه که Windows PowerShell رو تو حالت Administrator اجرا و در اون دستور زیر رو اجرا کنیم:

 powershell Set-ExecutionPolicy RemoteSigned

برای برگردوندن این ویژگی به حالت پیش‌فرض هم می‌تونیم از دستور زیر استفاده کنیم:

powershell Set-ExecutionPolicy Restricted

 

۰ نظر
فربد صالحی

انگولار : two-way data binding و ngFor

 می‌دونیم که «بایندینگ دوطرفه» در «انگولار» با استفاده از "ngModel" کار راحتیه. به این صورت که اگه متغیری به اسم username رو تو کامپوننت مربوطه تعریف کرده باشیم، برای بایندینگ دوطرفه‌ی اون در سمت template می‌نویسیم:

<input [(ngModel)]="username" />

حالا اگه ورودی این input تغییر کنه مقدارش به متغیر username منتقل میشه و هرجای برنامه هم که مقدار این متغیر رو تغییر بده، مقدار جدید توی input نمایش داده میشه.

اما گاهی نیاز میشه که بایندینگ دوطرفه رو روی آرایه‌ای از متغیرها اجرا کنیم. فرض کنیم آرایه‌ای به نام cities داریم:

let cities = ['Tehran','Rasht','urmieh'];

نمایش عناصر این آرایه بوسیله‌ی input ها ساده است:

 <div *ngFor="let item of cities">
     <input [(ngModel)]="item">
 </div>

همونطور که مشخصه در اینجا هم از ngModel برای بایندینگ دوطرفه استفاده شده، اما اگه این کد رو اجرا کنیم نتیجه‌ی دلخواه به دست نمیاد. یعتی اگه کاربر مقدار یک یا چندتا از input ها رو تغییر بده، مقادیر در آرایه‌ی cities تغییر نمی‌کنن. برای رفع این مشکل باید تغییرات زیر رو در کد بالا اعمال کنیم (بخش پر رنگ کد):

<div *ngFor="let item of cities;let index = index;">
  <input [(ngModel)]="cities[index]">
</div>

یعنی به جای متغیر item از اندیس‌های آرایه برای مقداردهی به ngModel استفاده می‌کنیم. به این ترتیب مشکل قبلی حل میشه، ولی در زمان اجرا کاربر در تغییر مقدار input مشکل خواهد داشت. فرض کنید کاربر می‌خواد مقدار urmieh (عنصر سوم آرایه) رو به urmia تغییر بده، تصمیم می‌گیره اول eh رو از انتهای کلمه پاک کنه و بعد a رو اضافه کنه. ولی همینکه h رو پاک می‌کنه، مکان‌نما (curser) از input مربوطه خارج میشه، در واقع focus از روی input برداشته میشه و کاربر باید دوباره روی اون input کلیک کنه تا بتونه تغییر بعدی رو اعمال کنه. این روند با هر تغییری در ورودی‌ها تکرار میشه.

علت اینه که با هر تغییر، انگولار مجددا DOM رو بازسازی می‌کنه و به این ترتیب focus روی input قبلی ازدست میره. برای رفع این مشکل، ابتدا تابعی رو تو کامپوننت مربوطه به صورت زیر تعریف می‌کنیم:

trackByIndex(index: number, item: any) {
  return index;
}

و مجددا کد مربوط به حلقه‌ی ngFor رو تغییر می‌دیم (بخش پر رنگ اضافه شده):

<div *ngFor="let item of cities;let index = index;trackBy:trackByIndex;">
  <input [(ngModel)]="cities[index]">
</div>

در اینجا ما از ویژگی trackBy در حلقه‌ی ngFor استفاده کردیم. به طور کلی، استفاده از این ویژگی یکی از توصیه‌ها برای افزایش کارایی در استفاده از ngFor در انگولار هستش، بخصوص وقتی تعداد عناصر حلقه زیاد و امکان تغییر اونها توسط کاربر وجود داشته باشه. تابعی که به این ویژگی نسبت داده میشه (ما اسمش رو trackByIndex گذاشتیم) همیشه دو پارامتر ورودی می‌گیره، اندیس عنصر مربوطه در آرایه و خود عنصر، و باید یه مقدار منحصر به فرد برای هر عنصر برگردونه تا انگولار بتونه با استفاده از اون فقط عنصری که مقدارش تغییر کرده رو بروز کنه و نیاز به بازسازی کل DOM نداشته باشه. ما در مثال بالا اندیس عنصر در آرایه رو بر می‌گردونیم.

۰ نظر
فربد صالحی

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

فرض کنیم جدولی به نام Products تو پایگاه داده داریم و ستون جدیدی به نام GroupId بهش اضافه کردیم:

 

 

این جدول ردیف‌های داده‌ی زیادی داره که باعث میشه اضافه کردن مقادیر این فیلد جدید به ردیف‌ها به صورت دستی کار سختی باشه. اما جدول دیگه‌ای به نام Categories داریم که GroupId درش مقدار داره و فیلد ProductCode اون با فیلد Code تو جدول قبلی مقادیرش مشترکه:

 

 

در این صورت تو Sql Server با دستور زیر می‌تونیم فیلد GroupId رو تو جدول Products مقداردهی کنیم:

 

 

UPDATE
    Products
SET
    Products.GroupId = cat.GroupId
FROM
    Products as pro
INNER JOIN
    Categories as cat
ON 
    pro.code = cat.ProductCode

 

در واقع با join جدول‌ها براساس فیلدهای Code و ProductCode (با دستور ON pro.code = cat.ProductCode)، یه جدول (مجازی) به دست میاد و بعد از اون انگار یه آپدیت معمولی روی یه فیلد (Products.GroupId) با استفاده از یه فیلد دیگه (cat.GroupId) داره انجام میشه. 

 

۰ نظر
فربد صالحی

بیسوادی نوین

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

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

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

این مساله در سال‌های اخیر با دسترسی راحت‌تر به اینترنت حادتر هم شده. یعنی جدا از اینکه عمیق نمیشی، حتی به صورت سطحی هم روش‌ها رو حفظ نمی‌کنی، چون میدونی که نیازی به این کار نیست و هر لحظه که لازم شد می‌تونی تو اینترنت به چیزی که نیاز داری دسترسی داشته باشی.

در این شرایط، برای شخص من به عنوان یه برنامه‌نویس گاهی این سوال پیش میاد که من واقعا چیزی هم بلدم یا فقط با استفاده از اینترنت دارم کارم رو راه میندازم؟ اصلا چه معیارهایی برای سنجش دانش یه شخص در زمینه حرفه‌ایش باید در نظر گرفت؟ همین که کاری رو بهش بسپاری و بهش زمان بدی و کیفیت نهایی کار رو بسنجی کافیه یا باید دانش تئوریکش رو هم سنجید؟

 

 

۰ نظر
فربد صالحی