فرض کنید کتابخانهای را مخصوص ASP.NET Core 2x تولید کردهاید و این کتابخانه، وابستگی را به بستهی Microsoft.AspNetCore.Mvc.Core دارد و اکنون قصد دارید نگارش 3x آنرا تهیه کنید. اگر به نیوگت مراجعه کنید، آخرین نگارشی که از آن موجود است، 2.2.5 است و دیگر هیچ خبری، حتی از نگارشهای preview مربوط به 3x، در اینجا وجود ندارد. علت اینجا است که تیم ASP.NET Core تصمیم گرفتهاست، دیگر بستههای نیوگت زیر مجموعهی Microsoft.AspNetCore.App را به صورت جداگانه تولید و منتشر نکند (و دیگر آخرین نگارشهای آنها را در سایت نیوگت نخواهید یافت).
همچنین نحوهی تعریف متاپکیج Microsoft.AspNetCore.App اینبار از طریق PackageReferenceها صورت نمیگیرد و بر اساس معرفی FrameworkReferenceها انجام شدهاست:
<ItemGroup> <FrameworkReference Include="Microsoft.AspNetCore.App" /> </ItemGroup>
یک نکته: تمام بستههای جدید تولید شده، بر اساس netcoreapp3.0 تهیه شدهاند؛ منهای بستههای Microsoft.Extensions و همچنین Entity Framework Core که هنوز بر پایهی NET Standard. تهیه میشوند. بنابراین فایل پروژهی یک class library که بخواهد از بستههای مبتنی بر netcoreapp3.0 استفاده کند و همچنین بستههای Microsoft.AspNetCore.App را نیز لحاظ کند، چنین شکلی را پیدا میکند (و TargetFramework آن دیگر برای مثال netstandard2.0 نمیتواند باشد):
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netcoreapp3.0</TargetFramework> </PropertyGroup> <ItemGroup> <FrameworkReference Include="Microsoft.AspNetCore.App" /> </ItemGroup> </Project>
ASP.NET MVC #19
سناریویی را در نظر بگیرید که یک برنامه وب نوشته شده، قرار است به چندین مستاجر (مشتری یا tenant) خدماتی را ارائه کند. در این حالت اطلاعات هر مشتری به صورت کاملا جدا شده از دیگر مشتریان در سیستم قرار دارد و فقط به همان قسمتها دسترسی دارد.
مثلا یک برنامه مدیریت رستوران را در نظر بگیرید که برای هر مشتری، در دامین
مخصوص به خود قرار دارد و همه آنها به یک سیستم متمرکز متصل شده و اطلاعات
خود را از آنجا دریافت میکنند.
در معماری Multi-Tenancy، چندین کاربر میتوانند از یک نمونه (Single
Instance) از اپلیکیشن نرمافزاری استفاده کنند. یعنی این نمونه روی سرور
اجرا میشود و به چندین کاربر سرویس میدهد. هر کاربر را یک Tenant
مینامیم. میتوان به Tenantها امکان تغییر و شخصیسازی بخشی از اپلیکیشن
را داد؛ مثلا امکان تغییر رنگ رابط کاربری و یا قوانین کسبوکار، اما آنها نمیتوانند
کدهای اپلیکیشن را شخصیسازی کنند.
خوشبختانه اوضاع با وجود OWIN بهتر شده و ما در این مطلب قصد استفاده از یک تولکیت را به نام SaasKit، برای پیاده سازی این معماری در ASP.NET Core داریم. هدف از این toolkit، سادهتر کردن هر چه بیشتر ساخت برنامههای SaaS (Software as a Service) هست. با استفاده از OWIN ما قادریم که بدون در نظر گرفتن فریم ورک مورد استفاده، رفتار مورد نظر خودمان را مستقیما در یک چرخه درخواست HTTP پیاده سازی کنیم و البته به لطف طراحی خاص ASP.NET Core 1.0 و استفاده از میان افزارهایی مشابه OWIN در برنامه، کار ما با SaasKit باز هم راحتتر خواهد بود.
شروع کار
یک پروژه ASP.NET Core جدید را ایجاد کنید و سپس ارجاعی را به فضای نام SaasKit.Multitenancy (موجود در Nuget) بدهید.PM> Install-Package SaasKit.Multitenancy
شناسایی مستاجر (tenant)
اولین جنبه در معماری multi-tenant، شناسایی مستاجر بر اساس اطلاعات درخواست جاری میباشد که میتواند از hostname ، کاربر جاری یا یک HTTP header باشد.ابتدا به تعریف کلاس مستاجر میپردازیم:
public class AppTenant { public string Name { get; set; } public string[] Hostnames { get; set; } }
public class AppTenantResolver : ITenantResolver<AppTenant> { IEnumerable<AppTenant> tenants = new List<AppTenant>(new[] { new AppTenant { Name = "Tenant 1", Hostnames = new[] { "localhost:6000", "localhost:6001" } }, new AppTenant { Name = "Tenant 2", Hostnames = new[] { "localhost:6002" } } }); public async Task<TenantContext<AppTenant>> ResolveAsync(HttpContext context) { TenantContext<AppTenant> tenantContext = null; var tenant = tenants.FirstOrDefault(t => t.Hostnames.Any(h => h.Equals(context.Request.Host.Value.ToLower()))); if (tenant != null) { tenantContext = new TenantContext<AppTenant>(tenant); } return tenantContext; } }
سیم کشی کردن
بعد از پیاده سازی این اینترفیس نوبت به سیم کشیهای SaasKit میرسد. من در اینجا سعی کردم که مثل الگوی برنامههای ASP.NET Core عمل کنم. ابتدا نیاز داریم که وابستگیهای SaasKit را ثبت کنیم. فایل startups.cs را باز کنید و کدهای زیر را در متد ConfigureServices اضافه نمایید:public void ConfigureServices(IServiceCollection services) { services.AddMultitenancy<AppTenant, AppTenantResolver>(); }
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { // after .UseStaticFiles() app.UseMultitenancy<AppTenant>(); // before .UseMvc() }
دریافت مستاجر جاری
حالا هر جا که نیاز به وهلهای از شیء مستاجر جاری داشتید، میتوانید به روش زیر عمل کنید:public class HomeController : Controller { private AppTenant tenant; public HomeController(AppTenant tenant) { this.tenant = tenant; } }
@inject AppTenant Tenant;
<a asp-controller="Home" asp-action="Index">@Tenant.Name</a>
اجرای نمونه مثال
فایل project.json را باز کنید و مقدار web را به شکل زیر مقدار دهی کنید: (در اینجا برای سایت خود 3 آدرس را نگاشت کردیم)"commands": { "web": "Microsoft.AspNet.Server.Kestrel --server.urls=http://localhost:6000;http://localhost:6001;http://localhost:6002", },
dotnet run
و اگر آدرس http://localhost:6002 را وارد کنیم، مستاجر 2 را مشاهده میکنیم:
قابل پیکربندی کردن مستاجر ها
از آنجائیکه نوشتن مشخصات مستاجرها در کد زیاد جالب نیست، برای همین
تصمیم داریم که این مشخصات را با استفاده از قابلیتهای ASP.NET Core از
فایل appsettings.json دریافت کنیم. تنظیمات مستاجرها را مطابق اطلاعات زیر به این فایل اضافه کنید:
"Multitenancy": { "Tenants": [ { "Name": "Tenant 1", "Hostnames": [ "localhost:6000", "localhost:6001" ] }, { "Name": "Tenant 2", "Hostnames": [ "localhost:6002" ] } ] }
public class MultitenancyOptions { public Collection<AppTenant> Tenants { get; set; } }
services.Configure<MultitenancyOptions>(Configuration.GetSection("Multitenancy"));
public class AppTenantResolver : ITenantResolver<AppTenant> { private readonly IEnumerable<AppTenant> tenants; public AppTenantResolver(IOptions<MultitenancyOptions> options) { this.tenants = options.Value.Tenants; } public async Task<TenantContext<AppTenant>> ResolveAsync(HttpContext context) { TenantContext<AppTenant> tenantContext = null; var tenant = tenants.FirstOrDefault(t => t.Hostnames.Any(h => h.Equals(context.Request.Host.Value.ToLower()))); if (tenant != null) { tenantContext = new TenantContext<AppTenant>(tenant); } return Task.FromResult(tenantContext); } }
در آخر
اولین قدم در پیاده سازی یک معماری multi-tenant، تصمیم گیری درباره این
موضوع است که شما چطور مستاجر خود را شناسایی کنید. به محض این شناسایی شما میتوانید عملیاتهای بعدی خود را مثل تفکیک بخشی از برنامه، فیلتر کردن دادهای، نمایش یک view خاص برای هر مستاجر و یا بازنویسی قسمتهای مختلف
برنامه بر اساس هر مستاجر، انجام دهید.
_ سورس مثال بالا در گیت هاب قابل دریافت میباشد.
_ منبع: اینجا
ASP.NET MVC #23
اجرای برنامههای ASP.NET MVC توسط نگارشهای متفاوت IIS
تا اینجا برای اجرای برنامههای ASP.NET MVC از وب سرور توکار VS.NET استفاده شد که صرفا جهت آزمایش برنامهها طراحی شده است. تا این تاریخ سه رده از وب سرورهای مایکروسافت ارائه شدهاند که برای نصب ASP.NET MVC میتوانند مورد استفاده قرار گیرند و هر کدام هم نکتههای خاص خودشان را دارند که در ادامه به بررسی آنها خواهیم پرداخت.
اجرای برنامههای ASP.NET MVC بر روی IIS 5.x ویندوز XP
پس از ایجاد یک دایرکتوری مجازی بر روی پوشه یک برنامه ASP.NET MVC و سعی در اجرای برنامه، بلافاصله پیغام خطای HTTP 403 forbidden مشاهده میشود.
اولین کاری که برای رفع این مساله باید صورت گیرد، کلیک راست بر روی نام دایرکتوری مجازی در کنسول IIS، انتخاب گزینه خواص و سپس مراجعه به برگه «ASP.NET» آن است. در اینجا شماره نگارش دات نت فریم ورک مورد استفاده را به 4 تغییر دهید (برای نمونه ASP.NET MVC 3.0 مبتنی بر دات نت فریم ورک 4 است).
بعد از این تغییر، بازهم موفق به اجرای برنامههای ASP.NET MVC بر روی IIS 5.x نخواهیم شد؛ چون در آن زمان مفاهیم مسیریابی و Routing که اصل و پایه ASP.NET MVC هستند وجود خارجی نداشتند. این نگارش از IIS به صورت پیش فرض تنها قادر به پردازش درخواستهای رسیدهای که به یک فایل فیزیکی بر روی سرور اشاره میکند، میباشد (یعنی مشکلی با اجرای برنامههای ASP.NET Web forms ندارد).
برای رفع این مشکل، مجددا بر روی نام دایرکتوری مجازی برنامه در کنسول IIS کلیک راست کرده و گزینه خواص را انتخاب کنید. در صفحه ظاهر شده، در برگه «Virtual directory» آن، بر روی دکمه «Configuration» کلیک نمائید. در صفحه باز شده مجددا بر روی دکمه «Add» کلیک کنید.
در صفحه باز شده، مسیر Executable را C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll وارد کرده و Extension را به .* (دات هرچی) تنظیم کنید. همین مقدار تنظیم، برای اجرای برنامههای ASP.NET MVC بر روی IIS 5.x ویندوز XP کفایت میکند.
کاری که در اینجا انجام شده است، نگاشت تمام درخواستهای رسیده صرفنظر از پسوند فایلها، به موتور ASP.NET میباشد. به صورت پیش فرض در IIS 5.x درخواستها تنها بر اساس پسوند فایلها پردازش میشوند. مثلا اگر فایل درخواستی aspx است، درخواست رسیده به aspnet_isapi.dll یاد شده هدایت خواهد شد. اگر پسوند فایل php است به isapi مخصوص آن (در صورت نصب) هدایت میگردد و به همین ترتیب برای سایر سیستمهای دیگر. زمانیکه Extension به «دات هرچی» و Executable به aspnet_isapi.dll دات نت 4 تنظیم میشود، دایرکتوری مجازی تنظیم شده تنها جهت سرویس دهی به یک برنامه ASP.NET عمل خواهد کرد و تمام درخواستهای رسیده به آن، به موتور اجرایی ASP.NET هدایت میشوند.
بدیهی است تنظیمات فوق تنها به یک دایرکتوری مجازی اعمال شدند. اگر نیاز باشد تا بر روی تمام سایتها تاثیر گذار شود، اینبار در کنسول IIS 5.x بر روی «Default web site» کلیک راست کرده و گزینه خواص را انتخاب کنید. در صفحه باز شده به برگه «Home directory» مراجعه کرده و مراحل ذکر شده را تکرار کنید.
مشکل! این روش بهینه نیست.
روش فوق خوبه، کار میکنه، اما بهینه نیست؛ از این جهت که «نگاشت تمام درخواستها به موتور ASP.NET» یعنی پروسه پردازش درخواست یک فایل تصویری، js یا css هم باید از فیلتر موتور ASP.NET عبور کند که ضروری نیست.
برای رفع این مشکل، توصیه شده است که سیستم مسیریابی ASP.NET MVC را در IIS 5.x «پسوند دار» کنید. به این نحو که با مراجعه به فایل Global.asax.cs، تعاریف مسیریابی را به نحو زیر ویرایش کنید:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add(
new Route("{controller}.aspx/{action}/{id}", new MvcRouteHandler())
{
Defaults = new RouteValueDictionary(new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
})
});
اینبار برای مثال مسیر http://localhost/MyMvcApp/home.aspx/index به علت داشتن پسوند aspx وارد موتور پردازشی ASP.NET خواهد شد. البته در این حالت URL های تمیز ASP.NET MVC را از دست خواهیم داد و مدام باید دقت داشت که مسیرهای کنترلرها حتما باید به aspx ختم شوند. ضمنا با این تنظیم، دیگر نیازی به تغییر تعاریف نگاشتها در کنسول مدیریتی IIS، نخواهد بود.
اجرای برنامههای ASP.NET MVC بر روی IIS 6.x ویندوز سرور 2003
تمام نکات عنوان شده جهت IIS 5.x در IIS 6.x نیز صادق هستند. به علاوه برای اجرای برنامههای ASP.NET بر روی IIS 6.x باید به دو نکته مهم دیگر نیز دقت داشت:
الف) ASP.NET 4 به صورت پیش فرض در IIS 6.x غیرفعال است که باید با مراجعه به قسمت Web Services Extensions در کنسول مدیریتی IIS، آنرا از حالت prohibited خارج کرد.
ب) در هر Application pool تنها از یک نگارش دات نت فریم ورک میتوان استفاده کرد. برای مثال اگر هم اکنون AppPool1 مشغول سرویس دهی به یک سایت ASP.NET 3.5 است، از آن نمیتوانید جهت اجرای برنامههای ASP.NET MVC 3 به بعد استفاده کنید. زیرا برای مثال ASP.NET MVC 3 مبتنی بر دات نت فریم ورک 4 است. به همین جهت حتما نیاز است تا یک Application pool مجزا را برای برنامههای دات نت 4 در IIS 6 اضافه نمائید و سپس در تنظیمات سایت، از این Application pool جدید استفاده نمائید.
البته روش صحیح و اصولی کار با IIS از نگارش 6 به بعد هم مطابق شرحی است که عنوان شد. برای دستیابی به بهترین کارآیی و امنیت بیشتر، بهتر است به ازای هر سایت، از یک Application pool مجزا استفاده نمائید.
اطلاعات تکمیلی:
نکات نصب برنامههای ASP.NET 4.0 بر روی IIS 6
مروری بر تاریخچه محدودیت حافظه مصرفی برنامههای ASP.NET در IIS
اجرای برنامههای ASP.NET MVC بر روی IIS 7.x ویندوز 7 و ویندوز سرور 2008
اگر برنامه ASP.NET MVC در IIS 7.x در حالت یکپارچه (integrated mode) اجرا شود، بدون نیاز به هیچگونه تغییری در تنظیمات سرور یا برنامه، بدون مشکل قابل اجرا خواهد بود. بدیهی است در اینجا نیز بهتر است به ازای هر برنامه، یک Application pool مجزا را ایجاد کرد.
اما در حالت classic (که برای برنامههای جدید توصیه نمیشود) نیاز است همان مراحل IIS 5,x تکرار شود. البته اینبار مسیر زیر را باید طی کرد تا به صفحه افزودن نگاشتها رسید:
Right-click on a web site -> Properties -> Home Directory tab -> click on the Configuration button -> Mappings tab
نکتهای مهم در تمام نگارشهای IIS
ترتیب نصب دات نت فریم ورک 4 و IIS مهم است. اگر ابتدا IIS نصب شود و سپس دات نت فریم ورک 4، به صورت خودکار، کار نگاشت اطلاعات ASP.NET به IIS صورت خواهد گرفت.
اگر ابتدا دات نت فریم ورک 4 نصب شود و سپس IIS، برای مثال دیگر از برگه ASP.NET در IIS 6.x خبری نخواهد بود. برای رفع این مشکل دستور زیر را در خط فرمان اجرا کنید:
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe /i
به این ترتیب، اطلاعات مرتبط با موتور ASP.NET مجددا به تنظیمات IIS اضافه خواهند شد.
{ "$.time": [ "The JSON value could not be converted to System.DateTime. Path: $.time | LineNumber: 1 | BytePositionInLine: 23." ] }
2019-06-14T02:30:04.0576719Z
'Full date' "yyyy'-'MM'-'dd" "'Full date''T''Hour'':''Minute'" "yyyy'-'MM'-'dd'T'HH':'mm" "'Full date''T''Partial time'" "yyyy'-'MM'-'dd'T'HH':'mm':'ss" (The Sortable ("s") Format Specifier) "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFF" "'Full date''T''Time hour'':''Minute''Time offset'" "yyyy'-'MM'-'dd'T'HH':'mmZ" "yyyy'-'MM'-'dd'T'HH':'mm('+'/'-')HH':'mm" 'Date time' "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ" "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFFZ" "yyyy'-'MM'-'dd'T'HH':'mm':'ss('+'/'-')HH':'mm" "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFF('+'/'-')HH':'mm"
تغییرات پردازش تاریخ در ASP.NET Core 5x
https://localhost:44393/time?time=2019-06-14T02:30:04.0576719Z
Model-bound value is: 2019-06-13T19:30:04.0576719-07:00, Kind is: Local
[BindProperty, DisplayFormat(DataFormatString = "{0:yyyy-MM-ddTHH:mm}", ApplyFormatInEditMode = true)] public DateTime DateTime { get; set; } DateTime: <input asp-for="DateTime" asp-format="{0:yyyy-MM-ddTHH:mm}" />