مطالب
بررسی بهبودهای ProblemDetails در ASP.NET Core 7x
در زمان ارائه‌ی ASP.NET Core 2.1، ویژگی جدیدی به نام [ApiController] ارائه شد که با استفاده از آن، یکسری اعمال توکار جهت سهولت کار با Web API توسط خود فریم‌ورک انجام می‌شوند؛ برای مثال عدم نیاز به بررسی وضعیت ModelState و بررسی خودکار آن با علامتگذاری یک کنترلر به صورت ApiController. یکی دیگر از این ویژگی‌های توکار، تبدیل خروجی تمام status codeهای بزرگتر و یا مساوی 400 یا همان Bad Request، به شیء جدید و استاندارد ProblemDetails است:
{
    "type": "https://example.com/probs/out-of-credit",
    "title": "You do not have enough credit.",
    "detail": "Your current balance is 30, but that costs 50.",
    "instance": "/account/12345/msgs/abc",
    "status": 403,
}
 بازگشت یک چنین خروجی یک‌دست و استانداردی، استفاده‌ی از آن‌را توسط کلاینت‌ها، ساده و قابل پیش‌بینی می‌کند. البته باید درنظر داشت که اگر در این‌حالت، برنامه یک استثنای معمولی را سبب شود، ProblemDetails ای بازگشت داده نمی‌شود. اگر برنامه در حالت توسعه اجرا شود، با استفاده از میان‌افزار app.UseDeveloperExceptionPage، یک صفحه‌ی نمایش جزئیات خطا ظاهر می‌شود و اگر برنامه در حالت تولید و ارائه‌ی نهایی اجرا شود، یک صفحه‌ی خالی (بدون داشتن response body) با status code مساوی 500 بازگشت داده می‌شود. این کمبود ویژه و امکانات سفارشی سازی بیشتر آن، به صورت توکار به ASP.NET Core 7x اضافه شده‌اند و دیگر نیازی به استفاده از کتابخانه‌های ثالث دیگری برای انجام آن نیست.


ProblemDetails بر اساس RFC7807 طراحی شده‌است

RFC7807، قالب استانداردی را برای ارائه‌ی خطاهای HTTP APIها تعریف می‌کند تا نیازی به وجود تعاریف متعددی در این زمینه نباشد و خروجی آن قابل پیش‌بینی و قابل بررسی توسط تمام کلاینت‌های یک API باشد. کلاس ProblemDetails در ASP.NET Core نیز بر همین اساس طراحی شده‌است.
این RFC دو فرمت خروجی را بر اساس مقدار مشخص شده‌ی در هدر Content-Type بازگشت داده شده، مجاز می‌داند:
  • JSON: “application/problem+json” media type
  • XML: “application/problem+xml” media type

که با توجه به این هدر ارسالی، اگر از یک کلاینت از نوع HttpClient استفاده کنیم، می‌توان بر اساس مقدار ویژه‌ی «application/problem+json» تشخیص داد که خروجی API دریافتی، به همراه خطا است و نحوه‌ی پردازش آن به صورت زیر خواهد بود:
var mediaType = response.Content.Headers.ContentType?.MediaType;
if (mediaType != null && mediaType.Equals("application/problem+json", StringComparison.InvariantCultureIgnoreCase))
{
   var problemDetails = await response.Content.ReadFromJsonAsync<ProblemDetails>(null, ct) ?? new ProblemDetails();
   // ...
}
در اینجا بدنه‌ی اصلی شیء ProblemDetails بازگشت داده شده، می‌تواند به همراه اعضای زیر باشد:
- type: یک رشته‌است که به آدرس مستندات HTML ای مرتبط با خطای بازگشت داده شده، اشاره می‌کند.
- title: رشته‌ای است که خلاصه‌ی خطای رخ‌داده را بیان می‌کند.
- detail: رشته‌ای است که توضیحات بیشتری را در مورد خطای رخ‌داده، بیان می‌کند.
- instance: رشته‌ای است که به آدرس محل بروز خطا اشاره می‌کند.
- status: عددی است که بیانگر HTTP status code بازگشتی از سمت سرور است.


البته اگر ویژگی ApiController بر روی کنترلرهای خود استفاده نمی‌کنید، می‌توانید این خروجی را به صورت زیر هم با استفاده از return Problem، تولید کنید:
[HttpPost("/sales/products/{sku}/availableForSale")]
public async Task<IActionResult> AvailableForSale([FromRoute] string sku)
{
   return Problem(
            "Product is already Available For Sale.",
            "/sales/products/1/availableForSale",
            400,
            "Cannot set product as available.",
            "http://example.com/problems/already-available");
}


امکان افزودن اعضای سفارشی به شیء ProblemDetails

امکان بسط این خروجی، با افزودن اعضای سفارشی نیز پیش‌بینی شده‌است. یک نمونه‌ی متداول و پرکاربرد آن، بازگشت خطاهای مرتبط با اعتبارسنجی اطلاعات رسیده‌است:
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
Content-Language: en
{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "errors": {
        "User": [
            "The user name is not verified."
        ]
    }
}
در اینجا عضو جدید errors را بنابر نیاز این مساله‌ی خاص، مشاهده می‌کنید که در صورت استفاده از ویژگی ApiController بر روی کنترلرهای Web API، به صورت خودکار توسط ASP.NET Core تولید می‌شود و نیازی به تنظیم خاصی و یا کدنویسی اضافه‌تری ندارد. کلاس مخصوص آن نیز ValidationProblemDetails‌ است.


جهت افزودن اعضای سفارشی دیگری به شیء ProblemDetails می‌توان به صورت زیر عمل کرد:
namespace WebApplication.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class DemoController : ControllerBase
    {
        [HttpPost]
        public ActionResult Post()
        {
            var problemDetails = new ProblemDetails
            {
                Detail = "The request parameters failed to validate.",
                Instance = null,
                Status = 400,
                Title = "Validation Error",
                Type = "https://example.net/validation-error",
            };

            problemDetails.Extensions.Add("invalidParams", new List<ValidationProblemDetailsParam>()
            {
                new("name", "Cannot be blank."),
                new("age", "Must be great or equals to 18.")
            });

            return new ObjectResult(problemDetails)
            {
                StatusCode = 400
            };
        }
    }

    public class ValidationProblemDetailsParam
    {
        public ValidationProblemDetailsParam(string name, string reason)
        {
            Name = name;
            Reason = reason;
        }

        public string Name { get; set; }
        public string Reason { get; set; }
    }
}
شیء ProblemDetails، به همراه خاصیت Extensions است که می‌توان به آن یک <Dictionary<string, object را انتساب داد و نمونه‌ای از آن‌را در مثال فوق مشاهده می‌کنید. این مثال سبب می‌شود تا عضو جدیدی با کلید دلخواه invalidParams، به همراه لیستی از name و reasonها به خروجی نهایی اضافه شود. مقدار این کلید، از نوع object است؛ یعنی هر شیء دلخواهی را در اینجا می‌توان تعریف و استفاده کرد.


معرفی سرویس جدید ProblemDetails در دات نت 7

در دات نت 7 می‌توان سرویس‌های جدید ProblemDetails را به نحو زیر به برنامه اضافه کرد:
services.AddProblemDetails();
پس از آن به 3 روش مختلف می‌توان از امکانات این سرویس‌ها استفاده کرد:
الف) با اضافه کردن میان‌افزار مدیریت خطاها
app.UseExceptionHandler();
پس از آن، هر استثنای مدیریت نشده‌ای نیز به صورت یک ProblemDetails ظاهر می‌شود و دیگر همانند قبل، سبب نمایش یک صفحه‌ی خالی نخواهد شد.

ب) با افزودن میان‌افزار StatusCodePages
app.UseStatusCodePages();
در این حالت مواردی که استثناء شمرده نمی‌شوند مانند 404، در صورت بروز رسیدن به یک مسیریابی یافت نشده و یا 405، در صورت درخواست یک HTTP method غیرمعتبر نیز توسط یک ProblemDetails استاندارد مدیریت می‌شوند.

ج) با افزودن میان‌افزار صفحه‌ی استثناءهای توسعه دهنده‌ها
app.UseDeveloperExceptionPage();
به این ترتیب در خروجی ProblemDetails، اطلاعات بیشتری از استثناء رخ‌داده، مانند استک‌تریس آن ظاهر خواهد شد.


امکان بازگشت ساده‌تر یک ProblemDetails سفارشی در دات نت 7

برای سفارشی سازی خروجی ProblemDetails، علاوه بر راه‌حلی که پیشتر در این مطلب مطرح شد، می‌توان در دات نت 7 از روش تکمیلی ذیل نیز استفاده کرد:
builder.Services.AddProblemDetails(options =>
    options.CustomizeProblemDetails = ctx =>
            ctx.ProblemDetails.Extensions.Add("MachineName", Environment.MachineName));
به این ترتیب در صورت لزوم می‌توان یک عضو سفارشی سراسری را به تمام اشیاء ProblemDetails برنامه به صورت خودکار اضافه کرد و یا اگر می‌خواهیم این مورد را کمی اختصاصی‌تر کنیم، می‌توان به صورت زیر عمل کرد:

الف) تعریف یک ErrorFeature سفارشی
public class MyErrorFeature
{
    public ErrorType Error  { get; set; }
}
​
public enum ErrorType
{
    ArgumentException
}
در ASP.NET Core می‌توان به شیء HttpContext.Features قابل تنظیم در هر اکشن متدی، اشیاء دلخواهی را مانند شیء سفارشی فوق، اضافه کرد و سپس در قسمت options.CustomizeProblemDetails تنظیماتی که ذکر شد، به دریافت و تنظیم آن، واکنش نشان داد.

ب) تنظیم مقدار ErrorFeature سفارشی در اکشن متدها
    [HttpGet("{value}")]
    public IActionResult MyErrorTest(int value)
    {
        if (value <= 0)
        {
            var errorType = new MyErrorFeature
            {
                Error = ErrorType.ArgumentException
            };
            HttpContext.Features.Set(errorType);
            return BadRequest();
        }
​
        return Ok(value);
    }
پس از تعریف شیءایی که قرار است به HttpContext.Features اضافه شود، اکنون روش تنظیم و مقدار دهی آن‌را در یک اکشن متد، در مثال فوق مشاهده می‌کنید.

ج) واکنش نشان دادن به دریافت ErrorFeature سفارشی
services.AddProblemDetails(options =>
    options.CustomizeProblemDetails = ctx =>
    {
        var MyErrorFeature = ctx.HttpContext.Features.Get<MyErrorFeature>();
​
        if (MyErrorFeature is not null)
        {
            (string Title, string Detail, string Type) details = MyErrorFeature.Error switch
            {
                ErrorType.ArgumentException =>
                (
                    nameof(ArgumentException),
                    "This is an argument-exception.",
                    "https://www.rfc-editor.org/rfc/rfc7231#section-6.5.1"
                ),
                _ =>
                (
                    nameof(Exception),
                    "default-exception",
                    "https://www.rfc-editor.org/rfc/rfc7231#section-6.6.1"
                )
            };
​
            ctx.ProblemDetails.Title = details.Title;
            ctx.ProblemDetails.Detail = details.Detail;
            ctx.ProblemDetails.Type = details.Type;
        }
    }
);
پس از تنظیم HttpContext.Features در اکشن متدی، می‌توان در options.CustomizeProblemDetails فوق، توسط متد ctx.HttpContext.Features.Get به آن شیء خاص تنظیم شده، در صورت وجود دسترسی یافت و سپس جزئیات بیشتری را از آن استخراج و مقادیر ctx.ProblemDetails جاری را که قرار است به کاربر بازگشت داده شوند، بازنویسی کرد و یا تغییر داد.
 

امکان تبدیل ساده‌تر اطلاعات استثناءهای سفارشی به یک ProblemDetails سفارشی در دات نت 7

بجای استفاده از تنظیمات services.AddProblemDetails جهت بازنویسی مقدار شیء ProblemDetails بازگشتی، می‌توان جزئیات میان‌افزار app.UseExceptionHandler را نیز سفارشی سازی کرد و به بروز استثناءهای خاصی واکنش نشان داد. برای مثال فرض کنید یک استثنای سفارشی را به صورت زیر طراحی کرده‌اید:
public class MyCustomException : Exception
{
    public MyCustomException(
        string message,
        HttpStatusCode statusCode = HttpStatusCode.BadRequest
    ) : base(message)
    {
        StatusCode = statusCode;
    }
​
    public HttpStatusCode StatusCode { get; }
}
و سپس در اکشن متدی، سبب بروز آن شده‌اید:
    [HttpGet("{value}")]
    public IActionResult MyErrorTest(int value)
    {
        if (value <= 0)
        {
            throw new MyCustomException("The value should be positive!");
        }
​
        return Ok(value);
    }
اکنون می‌توان در میان‌افزار مدیریت استثناءهای برنامه، نسبت به مدیریت این استثناء خاص، واکشن نشان داد و ProblemDetails متناظری را تولید و بازگشت داد:
app.UseExceptionHandler(exceptionHandlerApp =>
{
    exceptionHandlerApp.Run(async context =>
    {
        context.Response.ContentType = "application/problem+json";
​
        if (context.RequestServices.GetService<IProblemDetailsService>() is { } problemDetailsService)
        {
            var exceptionHandlerFeature = context.Features.Get<IExceptionHandlerFeature>();
            var exceptionType = exceptionHandlerFeature?.Error;
​
            if (exceptionType is not null)
            {
                (string Title, string Detail, string Type, int StatusCode) details = exceptionType switch
                {
                    MyCustomException MyCustomException =>
                    (
                        exceptionType.GetType().Name,
                        exceptionType.Message,
                        "https://www.rfc-editor.org/rfc/rfc7231#section-6.5.1",
                        context.Response.StatusCode = (int)MyCustomException.StatusCode
                    ),
                    _ =>
                    (
                        exceptionType.GetType().Name,
                        exceptionType.Message,
                        "https://www.rfc-editor.org/rfc/rfc7231#section-6.6.1",
                        context.Response.StatusCode = StatusCodes.Status500InternalServerError
                    )
                };
​
                await problemDetailsService.WriteAsync(new ProblemDetailsContext
                {
                    HttpContext = context,
                    ProblemDetails =
                    {
                        Title = details.Title,
                        Detail = details.Detail,
                        Type = details.Type,
                        Status = details.StatusCode
                    }
                });
            }
        }
    });
});
​
در اینجا نحوه‌ی کار با سرویس توکار IProblemDetailsService و سپس دسترسی به IExceptionHandlerFeature و استثنای صادر شده را مشاهده می‌کنید. پس از آن بر اساس نوع و اطلاعات این استثناء، می‌توان یک ProblemDetails مخصوص را تولید و در خروجی ثبت کرد.
اشتراک‌ها
روشی برای تبدیل برنامه‌های React به PWA

You’ve built a React web app and would love to bring it to iOS and Android. That means you have to learn React Native first, right? As it turns out, there’s an easier way to deploy to mobile. With Capacitor, a new native runtime for web apps, you can deploy any React-based web app as a PWA, iOS, or Android app - all from the same codebase. 

روشی برای تبدیل برنامه‌های React به PWA
مطالب
مسیریابی در Angular - قسمت اول - معرفی
مسیریابی در +Angular 2 به همراه قابلیت‌های فراوان و ویژه‌ای است که تعدادی از آن‌ها را تابحال در این سایت بررسی کرده‌ایم. مورد مقدماتی اول، نیاز به بازنویسی کامل دارد، مورد دوم جهت آشنایی با ساختار Angular CLI مفید است و مورد سوم یکی از مباحث تکمیلی آن است. به همین جهت در طی یک سری، ویژگی‌های متعدد سیستم مسیریابی +Angular 2 را از ابتدا بررسی خواهیم کرد.


مسیریابی در +Angular 2

عموما از مسیریابی جهت حرکت بین Viewهای مختلف برنامه استفاده می‌شود، اما کارهای بیشتری را نیز می‌توان با آن انجام داد؛ مانند ارسال اطلاعات، به مسیریابی‌ها، پیش بارگذاری اطلاعات، جهت نمایش در Viewها، گروه بندی و محافظت از مسیریابی‌ها، پویانمایی و انیمیشن و همچنین بهبود کارآیی، با بارگذاری async مسیرهای مختلف.
کار سیستم مسیریاب +Angular 2 زمانی شروع می‌شود که تغییری را در آدرس درخواستی از برنامه مشاهده می‌کند؛ یا از طریق درخواست آدرسی توسط مرورگر و یا هدایت به قسمتی خاص، از طریق کدنویسی. سپس مسیریاب به آرایه‌ی تنظیم شده‌ی مسیرهای سیستم مراجعه می‌کند تا بتواند تطابقی را بین آدرس درخواستی و یکی از کلیدهای تنظیم شده‌ی در آن پیدا کند. در این حالت اگر تطابقی یافت نشود، کارمسیریابی خاتمه خواهد یافت. در غیراینصورت کار ادامه یافته و سپس مسیریاب، محافظ‌های مسیر درخواستی را بررسی می‌کند تا مشخص شود که آیا کاربر مجاز به هدایت به این قسمت خاص از برنامه هست یا خیر؟ در صورت مثبت بودن پاسخ، مرحله‌ی بعد، پیش بارگذاری اطلاعات درخواستی جهت نمایش View مرتبط است. در ادامه کامپوننت متناظر با مسیریابی فعالسازی می‌شود. سپس قالب این کامپوننت را در قسمتی که توسط router-outlet مشخص می‌گردد، جایگذاری کرده و نمایش می‌دهد.


تعریف مسیر پایه یا Base path

اولین مرحله‌ی کار با سیستم مسیریابی +Angular 2، تعریف یک base path است. مسیرپایه، به زیرپوشه‌ای اشاره می‌کند که برنامه‌ی ما در آن قرار گرفته‌است:
 www.mysite.com/myapp
در این مثال، مسیرپایه برنامه، /myapp/ است.
مسیریاب از این مسیرپایه جهت ساخت آدرس‌های مسیریابی استفاده می‌کند. مقدار آن نیز به صورت ذیل در فایل index.html برنامه، درست پس از تگ head تعیین می‌گردد:
<!DOCTYPE html>
<html>
<head>
    <base href="/">
در اینجا با ذکر / ، به عنوان مسیرپایه، به ریشه‌ی سایت اشاره شده‌است. اگر برنامه‌ی شما قرار است در یک زیرپوشه قرار بگیرد، در این حالت نحوه‌ی تعیین این زیر پوشه به صورت ذیل خواهد بود:
<base href="/myapp/">


تعیین مسیرپایه جهت ارائه‌ی نهایی

استفاده از مسیر پایه / برای حالت توسعه و همچنین زمانیکه برنامه‌ی نهایی شما در ریشه‌ی سایت توزیع می‌شود، بسیار مناسب است. اما اگر برای حالت توسعه از مقدار / و برای حالت توزیع از مقدار /myapp/ بخواهید استفاده کنید، مدام نیاز خواهید داشت تا فایل index.html نهایی سایت را ویرایش کنید. برای این منظور Angular CLI دارای پرچمی است به نام base-href:
 > ng build --base-href /myapp/
به این ترتیب در زمان ساخت و توزیع نهایی برنامه توسط Angular CLI، کار تنظیم خودکار مسیرپایه نیز صورت خواهد گرفت.
حالت پیش فرض تولید برنامه‌های Angular توسط Angular CLI، تنظیم مسیرپایه در فایل src\index.html به صورت خودکار به / می‌باشد.


تعریف مسیریاب Angular

مسیریاب Angular در ماژولی به نام RouterModule قرار گرفته‌است و باید در ابتدای کار import شود. این ماژول شامل سرویسی است جهت هدایت کاربران به صفحات دیگر و مدیریت URLها، تنظیماتی برای تعریف جزئیات مسیریابی‌ها و تعدادی دایرکتیو که برای فعالسازی و نمایش مسیرها از آن‌ها استفاده می‌شود. برای مثال دایرکتیو RouterLink آن یک المان قابل کلیک HTML را به مسیر و کامپوننتی خاص در برنامه متصل می‌کند. RouterLinkActive، شیوه‌نامه‌ها را به لینک فعال انتساب می‌دهد و RouterOutlet محل نمایش قالب کامپوننت فعال شده را مشخص می‌کند.

یک مثال: در ادامه، یک پروژه‌ی جدید مبتنی بر Angular CLI را به نام angular-routing-lab به همراه تنظیمات ابتدایی مسیریابی آن ایجاد می‌کنیم:
 > ng new angular-routing-lab --routing
به علاوه قصد استفاده‌ی از بوت استرپ را نیز داریم. به همین جهت ابتدا به ریشه‌ی پروژه وارد شده و سپس دستور ذیل را صادر کنید تا بوت استرپ نیز نصب شود و پرچم save آن سبب به روز رسانی فایل package.json نیز گردد:
 > npm install bootstrap --save
پس از آن نیاز است به فایل angular-cli.json. مراجعه کرده و شیوه‌نامه‌ی بوت استرپ را تعریف کنیم:
  "apps": [
    {
      "styles": [
    "../node_modules/bootstrap/dist/css/bootstrap.min.css",
        "styles.css"
      ],
به این ترتیب به صورت خودکار این شیوه نامه به همراه توزیع برنامه حضور خواهد داشت و نیازی به تعریف مستقیم آن در فایل index.html نیست.

در ادامه اگر به فایل src\app\app-routing.module.ts مراجعه کنید، یک چنین محتوایی را خواهید یافت:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
  {
    path: '',
    children: []
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
در اینجا برای شروع به کار با سیستم مسیریابی، RouterModule معرفی شده‌است. ابتدا import آن‌را مشاهده می‌کنید و سپس این ماژول به قسمت imports مربوط به NgModule اضافه شده‌است.
می‌توان این قسمت را خلاصه کرد و فایل app-routing.module.ts را نیز حذف کرد و سپس import لازم و تعریف ماژول آن‌را به ماژول آغازین برنامه یا همان src\app\app.module.ts نیز منتقل کرد. اما پس از مدتی تنظیمات مسیریابی آن، فایل ماژول اصلی برنامه را بیش از اندازه شلوغ خواهند کرد. بنابراین Angular-CLI تصمیم به ایجاد یک ماژول مستقل را برای تعریف تنظیمات مسیریابی برنامه گرفته‌است. سپس تعریف آن را به فایل src\app\app.module.ts به صورت خودکار اضافه می‌کند:
import { AppRoutingModule } from './app-routing.module';

@NgModule({
  imports: [
    AppRoutingModule
  ],

اگر به قسمت import مربوط به NgModule فایل src\app\app-routing.module.ts دقت کنید، این ماژول به همراه متد forRoot معرفی شده‌است.
@NgModule({
  imports: [RouterModule.forRoot(routes)],
ماژول مسیریابی به همراه دو متد ذیل است:
الف) forRoot
- کار آن تعریف دایرکتیوهای مسیریابی، مدیریت تنظیمات مسیریابی و ثبت سرویس مسیریابی است.
- نکته‌ی مهم اینجا است که متد forRoot تنها یکبار باید در طول عمر یک برنامه تعریف شود.
- این متد آرایه‌ای از تنظیمات مسیریابی‌های تعریف شده را دریافت می‌کند.
ب) forChild
- کار آن تعریف دایرکتیوهای مسیریابی و مدیریت تنظیمات مسیریابی است؛ اما سرویس مسیریابی را مجددا ثبت نمی‌کند.
- از این متد در جهت تعریف مسیریابی‌های ماژول‌های ویژگی‌های مختلف برنامه و نظم بخشیدن به آن‌ها استفاده می‌شود.

بنابراین زمانیکه از forRoot استفاده می‌شود، سرویس مسیریابی تنها یکبار ثبت خواهد شد و تنها یک وهله از آن موجود خواهد بود. در ادامه هر کدام از ماژول‌های دیگر برنامه می‌توانند forChild خاص خودشان را داشته باشند.

اکنون تمام کامپوننت‌های قید شده‌ی در قسمت declaration، امکان دسترسی به دایرکتیوهای مسیریابی را پیدا می‌کنند. همچنین از آنجائیکه AppRoutingModule به همراه متد forRoot است، سرویس مسیریابی نیز در کل برنامه قابل دسترسی است.


تنظیمات اولیه مسیریابی برنامه

آرایه‌ی const routes: Routes فایل src\app\app-routing.module.ts در ابتدای کار خالی است. در اینجا کار تعریف URL segments و سپس اتصال آن‌ها به کامپوننت‌های متناظری جهت فعالسازی و نمایش قالب آن‌ها صورت می‌گیرد. این نمایش نیز در محل router-outlet تعریف شده‌ی در فایل src\app\app.component.html انجام می‌شود:
<h1>
  {{title}}
</h1>
<router-outlet></router-outlet>
به ازای هر router-outlet، تنها یک مسیر را در هر زمان می‌توان نمایش داد. بنابراین با فعال شدن هر کامپوننت، قالب آن، قالب کامپوننت پیشین را در router-outlet جایگزین خواهد کرد. امکان تعریف router-outletهای دیگری نیز وجود دارند که به آن‌ها secondary routes گفته می‌شود.

در ادامه برای تکمیل مثال جاری، دو کامپوننت جدید خوش‌آمد گویی و همچنین یافتن نشدن مسیرها را به برنامه اضافه می‌کنیم:
>ng g c welcome
>ng g c PageNotFound
که سبب ایجاد کامپوننت‌های src\app\welcome\welcome.component.ts و src\app\page-not-found\page-not-found.component.ts خواهند شد، به همراه به روز رسانی خودکار فایل src\app\app.module.ts جهت تکمیل قسمت declarations آن:
@NgModule({
  declarations: [
    AppComponent,
    WelcomeComponent,
    PageNotFoundComponent
  ],

سپس فایل src\app\app-routing.module.ts را به نحو ذیل تکمیل نمائید:
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
import { WelcomeComponent } from './welcome/welcome.component';
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
  { path: 'welcome', component: WelcomeComponent },
  { path: '', redirectTo: 'welcome', pathMatch: 'full' },
  { path: '**', component: PageNotFoundComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
در اینجا در ابتدا دو کامپوننت جدید تعریف شده، import شده‌اند.

یک نکته: افزونه‌ی auto import، کار تعریف کامپوننت‌ها را در VSCode بسیار ساده می‌کند و امکان تشکیل خودکار قسمت import را با ارائه‌ی یک intellisense به همراه دارد.

سپس کار تکمیل آرایه‌ی Routes انجام شده‌است. همانطور که مشاهده می‌کنید، این آرایه متشکل است از اشیایی که به همراه خاصیت path و سایر پارامترهای مورد نیاز هستند.
کار خاصیت path، تعیین URL segment متناظری است که این مسیریابی را فعال می‌کند. برای مثال اولین شیء تعریف شده با آدرس‌هایی مانند www.mysite.com/welcome متناظر است.
 { path: 'welcome', component: WelcomeComponent },
در اغلب حالات به همراه path، خاصیت کامپوننت متناظر با آن مسیر نیز ذکر می‌شود؛ مانندWelcomeComponent ایی که در اینجا مقدار دهی شده‌است. هنگامیکه این مسیریابی فعال می‌شود، قالب این کامپوننت‌است که در router-outlet نمایش داده خواهد شد (فایل src\app\welcome\welcome.component.html).

چند نکته:
- در حین تعریف مقدار خاصیت path، هیچ / آغاز کننده‌ای تعریف نشده‌است.
- مقدار خاصیت path، حساس به کوچکی و بزرگی حروف است.
- WelcomeComponent تعریف شده، یک رشته نیست و ارجاعی را به کامپوننت مرتبط دارد. به همین جهت نیاز به import statement ابتدایی را دارد و وجود آن توسط کامپایلر بررسی می‌شود.


تعیین مسیریابی پیش فرض سایت

اما زمانیکه برنامه برای بار اول بارگذاری می‌شود، چطور؟ در این حالت هیچ URL segment ایی وجود ندارد. بنابراین برای تنظیم مسیرپیش فرض سایت، خاصیت path، به یک رشته‌ی خالی همانند دومین شیء تنظیمات مسیریابی، تنظیم می‌شود:
   { path: '', redirectTo: 'welcome', pathMatch: 'full' },
در اینجا زمانیکه کاربر، ریشه‌ی سایت را درخواست می‌کند، به کامپوننت welcome هدایت خواهد شد. در این تنظیم خاصیت pathMatch نحوه‌ی تطابق با خاصیت path را مشخص می‌کند. مقدار full نیز به این معنا است که اگر تمام URL segment دریافتی خالی بود، از این تنظیم استفاده کن.


مدیریت مسیریابی آدرس‌های ناموجود در سایت

تنظیم سومی را نیز در اینجا مشاهده می‌کنید:
 { path: '**', component: PageNotFoundComponent },
** به این معنا است که اگر مسیریاب موفق به تطابق URL segment دریافتی، با هیچکدام از pathهای تعریف شده نبود، از این تنظیم استفاده کند (catch all). برای مثال اگر کاربر آدرس www.mysite.com/someAddr را درخواست کند، چون URL segment آن در آرایه‌ی تنظیمات، تعریف نشده‌است، می‌خواهیم یک صفحه‌ی یافت نشد را نمایش دهیم.

یک نکته: ترتیب مسیریابی‌ها در آرایه‌ی تعریف آن‌ها اهمیت دارد. در اینجا از استراتژی «اولین تطابق یافته‌، برنده خواهد بود» استفاده می‌شود. بنابراین تنظیم ** باید در انتهای لیست ذکر شود؛ در غیراینصورت هیچکدام از مسیریابی‌های تعریف شده‌ی پس از آن پردازش نخواهند شد.


مدیریت تغییرات آدرس‌های برنامه

در طول عمر برنامه ممکن است نیاز به تغییر آدرس‌های برنامه باشد. برای مثال بجای مسیر welcome مسیر home نمایش داده شود:
const routes: Routes = [
  { path: 'home', component: WelcomeComponent },
  { path: 'welcome', redirectTo: 'home', pathMatch: 'full' },
  { path: '', redirectTo: 'welcome', pathMatch: 'full' },
  { path: '**', component: PageNotFoundComponent }
];
در این تنظیمات جدید برنامه، مسیر home به WelcomeComponent اشاره می‌کند. اما جهت هدایت آدرس‌های قدیمی welcome، به این آدرس جدید home می‌توان از redirectTo، مانند دومین شیء مسیریابی اضافه شده‌ی به آرایه‌ی تنظیمات استفاده کرد. به این ترتیب هم تغییرات آرایه‌ی Routes به حداقل می‌رسد و هم آدرس‌های ذخیره شده‌ی توسط کاربران هنوز هم معتبر خواهند بود.

نکته: redirectToها قابلیت تعریف زنجیره‌ای را ندارند. به این معنا که اگر ریشه‌ی سایت درخواست شود، ابتدا به مسیر welcome هدایت خواهیم شد. مسیر welcome هم یک redirectTo دیگر به مسیر home را دارد. اما در اینجا کار به این redirectTo دوم نخواهد رسید و این پردازش، زنجیره‌ای نیست. بنابراین مسیریابی پیش‌فرض را نیز باید ویرایش کرد و به home تغییر داد:
const routes: Routes = [
  { path: 'home', component: WelcomeComponent },
  { path: 'welcome', redirectTo: 'home', pathMatch: 'full' },
  { path: '', redirectTo: 'home', pathMatch: 'full' },
  { path: '**', component: PageNotFoundComponent }
];
به این ترتیب دیگر درخواست زنجیره‌وار redirectToها رخ نخواهد داد.

نکته: redirectToها می‌توانند local و یا absolute باشند. تعریف محلی آن‌ها مانند ذکر home و welcome در اینجا است و تنها سبب تغییر یک URL segment می‌شود. اما اگر در ابتدای مقادیر redirectToها یک / قرار دهیم، به معنای تعریف یک مسیر مطلق است و کل URL را جایگزین می‌کند.


تعیین محل نمایش قالب‌های کامپوننت‌ها

زمانیکه یک کامپوننت فعالسازی می‌شود، قالب آن در router-outlet نمایش داده خواهد شد. برای این منظور فایل src\app\app.component.html را گشوده و به نحو ذیل تغییر دهید:
<nav class="navbar navbar-default">
  <div class="container-fluid">
    <a class="navbar-brand">{{title}}</a>
    <ul class="nav navbar-nav">
      <li>
        <a [routerLink]="['/home']">Home</a>
      </li>
    </ul>
  </div>
</nav>
<div class="container">
  <router-outlet></router-outlet>
</div>
در اینجا دایرکتیو router-outlet محلی است که قالب کامپوننت فعالسازی شده، نمایش داده می‌شود. برای مثال اگر کاربر بر روی لینک Home کلیک کند، کامپوننت متناظر با آن از طریق تنظیمات مسیریابی یافت شده و فعالسازی می‌شود و سپس قالب آن در محل router-outlet رندر خواهد شد.

یک نکته: چون کامپوننت welcome از طریق مسیریابی نمایش داده می‌شود و دیگر به صورت مستقیم با درج تگ selector آن در صفحه فعالسازی نخواهد شد، می‌توان به تعریف کامپوننت آن مراجعه کرده و selector آن‌را حذف کرد.
 @Component({
//selector: 'app-welcome',
templateUrl: './welcome.component.html',
styleUrls: ['./welcome.component.css']
})

تا اینجا اگر دستور ng serve -o را صادر کنیم (کار build درون حافظه‌ای جهت محیط توسعه و نمایش خودکار برنامه در مرورگر)، چنین خروجی در مرورگر نمایان خواهد شد:


اگر به آدرس تنظیم شده‌ی در مرورگر دقت کنید، http://localhost:4200/home آدرسی است که در ابتدای نمایش سایت نمایان خواهد شد. علت آن نیز به تنظیم مسیریابی پیش فرض سایت برمی‌گردد.
و اگر یک مسیر غیرموجود را درخواست دهیم، قالب کامپوننت PageNotFound ظاهر می‌شود:



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

کاربران را می‌توان به روش‌های مختلفی به قسمت‌های گوناگون برنامه هدایت کرد؛ برای مثال با کلیک بر روی المان‌های قابل کلیک HTML و سپس اتصال آن‌ها به کامپوننت‌های برنامه. استفاده‌ی کاربر از bookmark مرورگر و یا ورود مستقیم و دستی آدرس قسمتی از برنامه و یا کلیک بر روی دکمه‌های forward و back مرورگر. تنها مورد اول است که نیاز به تنظیم دارد و سایر قسمت‌ها به صورت خودکار مدیریت خواهند شد. نمونه‌ی آن‌را نیز با تعریف لینک Home پیشتر مشاهده کردید:
  <a [routerLink]="['/home']">Home</a>
- در اینجا با استفاده از یک دایرکتیو ویژه به نام routerLink، المان‌های قابل کلیک HTML را به خاصیت‌های path تعریف شده‌ی در تنظیمات مسیریابی برنامه متصل می‌کنیم. routerLink یک attribute directive است؛ بنابراین می‌توان آن‌را به یک المان قابل کلیک anchor tag، مانند مثال فوق نسبت داد.
- زمانیکه کاربر بر روی این لینک کلیک می‌کند، اولین path متناظر با routerLink یافت شده و فعالسازی خواهد شد.
- علت تعریف مقدار routerLink به صورت [] این است که آرایه‌ی پارامترهای لینک را مشخص می‌کند. بنابراین چون آرایه‌است، نیاز به [] دارد. اولین پارامتر این آرایه مفهوم root URL segment را دارد. در اینجا حتما نیاز است URL segment را با یک / شروع کرد. به علاوه باید دقت داشت که خاصیت path تنظیمات مسیریابی، حساس به حروف کوچک و بزرگ است. بنابراین این مورد را باید در اینجا نیز مدنظر داشت.
- پارامترهای دیگر routerLink می‌توانند مفهوم پارامترهای این segment و یا حتی segments دیگری باشند.

یک نکته: چون در مثال فوق، آرایه‌ی تعریف شده تنها دارای یک عضو است، آن‌را می‌توان به صورت ذیل نیز خلاصه نویسی کرد (one-time binding):
 <a routerLink="/home">Home</a>


تفاوت بین آدرس‌های HTML 5 و Hash-based

زمانیکه مسیریاب Angular کار پردازش آدرس‌های رسیده را انجام می‌دهد، اینکار در سمت کلاینت صورت می‌گیرد و تنها URL segment مدنظر را تغییر داده و این درخواست را به سمت سرور ارسال نمی‌کند. به همین جهت سبب reload صفحه نمی‌شود. دو روش در اینجا جهت مدیریت سمت کلاینت آدرس‌ها قابل استفاده است:
الف) HTML 5 Style
- آدرسی مانند http://localhost:4200/home، یک آدرس به شیوه‌ی HTML 5 است. در اینجا مسیریاب Angular با استفاده از HTML 5 history pushState سبب به روز رسانی History مرورگر شده و آدرس‌ها را بدون ارسال درخواستی به سمت سرور، در همان سمت کلاینت تغییر می‌دهد.
- این روش حالت پیش فرض Angular است و نحوه‌ی نمایش آن بسیار طبیعی به نظر می‌رسد.
- در اینجا URL rewriting سمت سرور نیز جهت هدایت آدرس‌ها، به برنامه‌ی Angular ضروری است. برای مثال زمانیکه کاربری آدرس http://localhost:4200/home را مستقیما در مرورگر وارد می‌کند، این درخواست ابتدا به سمت سرور ارسال خواهد شد و چون چنین صفحه‌ای در سمت سرور وجود ندارد، پیغام خطای 404 را دریافت می‌کند. اینجا است که URL rewriting سمت سرور به فایل index.html برنامه، جهت مدیریت یک چنین حالت‌هایی ضروری است.
برای نمونه اگر از وب سرور IIS استفاده می‌کنید، تنظیم ذیل را به فایل web.config در قسمت system.webServer اضافه کنید (کار کرد آن هم وابسته‌است به نصب و فعالسازی ماژول URL Rewrite بر روی IIS):
<rewrite>
    <rules>
        <rule name="Angular 2+ pushState routing" stopProcessing="true">
            <match url=".*" />
                <conditions logicalGrouping="MatchAll">
                    <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                    <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                    <add input="{REQUEST_FILENAME}" pattern=".*\.[\d\w]+$" negate="true" />
                    <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
                </conditions>
                <action type="Rewrite" url="/index.html" />
        </rule>
    </rules>
</rewrite>

ب) Hash-based
- آدرسی مانند http://localhost:4200/#/home یک آدرس به شیوه‌ی Hash-based بوده و مخصوص مرورگرهایی است بسیار قدیمی که از HTML 5 پشتیبانی نمی‌کنند. اینبار قطعات قرار گرفته‌ی پس از علامت # دارای نام URL fragments بوده و قابلیت پردازش در سمت کلاینت را دارا می‌باشند.
- اگر علاقمند به استفاده‌ی از این روش هستید، نیاز است خاصیت useHash را به true تنظیم کنید:
@NgModule({
   imports: [RouterModule.forRoot(routes, { useHash: true })],
-  این روش نیازی به URL rewriting سمت سرور ندارد. از آنجائیکه سرور هرچیزی را که پس # باشد ندید خواهد گرفت و سعی در یافتن آن‌، در سمت سرور نخواهد کرد.
اشتراک‌ها
Visual Studio 2019 RC منتشر شد
Visual Studio 2019 RC منتشر شد
اشتراک‌ها
انتشار TypeScript 4.6

Here’s a quick list of what’s new in TypeScript 4.6!

Allowing Code in Constructors Before super()
Control Flow Analysis for Destructured Discriminated Unions
Improved Recursion Depth Checks
Indexed Access Inference Improvements
Control Flow Analysis for Dependent Parameters
--target es2022
Removed Unnecessary Arguments in react-jsx
JSDoc Name Suggestions
More Syntax and Binding Errors in JavaScript
TypeScript Trace Analyzer
Breaking Changes
 

انتشار TypeScript 4.6
اشتراک‌ها
Migrations در Entity Framework 7

This is the last installment in a series of videos made with the Entity Framework team. In this episode Brice Lambson describes migrations and how they are used in Entity Framework 7.

Migrations در Entity Framework 7
مطالب
انتشار پیش نمایش ASP.NET Identity 2.0.0-alpha1
مایکروسافت در تاریخ 20 دسامبر 2013 پیش نمایش نسخه جدید ASP.NET Identity را معرفی کرد. تمرکز اصلی در این انتشار، رفع مشکلات نسخه 1.0 بود. امکانات جدیدی هم مانند Account Confirmation و Password Reset اضافه شده اند.

دانلود این انتشار
ASP.NET Identity را می‌توانید در قالب یک پکیج NuGet دریافت کنید. در پنجره Manage NuGet Packages می‌توانید پکیج‌های Preview را لیست کرده و گزینه مورد نظر را
نصب کنید. برای نصب پکیج‌های pre-release توسط Package Manager Console از فرامین زیر استفاده کنید.
  • Install-Package Microsoft.AspNet.Identity.EntityFramework -Version 2.0.0-alpha1 -Pre
  • Install-Package Microsoft.AspNet.Identity.Core -Version 2.0.0-alpha1 -Pre
  • Install-Package Microsoft.AspNet.Identity.OWIN -Version 2.0.0-alpha1 -Pre
دقت کنید که حتما از گزینه "Include Prerelease" استفاده می‌کنید. برای اطلاعات بیشتر درباره نصب پکیج‌های Pre-release لطفا به این لینک و یا این لینک مراجعه کنید.

در ادامه لیست امکانات جدید و مشکلات رفع شده را می‌خوانید.

Account Confirmation
سیستم ASP.NET Identity حالا از Account Confirmation پشتیبانی می‌کند. این یک سناریوی بسیار رایج است. در اکثر وب سایت‌های امروزی پس از ثبت نام، حتما باید ایمیل خود را تایید کنید. پیش از تایید ثبت نام قادر به انجام هیچ کاری در وب سایت نخواهید بود، یعنی نمی‌توانید Login کنید. این روش مفید است، چرا که از ایجاد حساب‌های کاربری نامعتبر (bogus) جلوگیری می‌کند. همچنین این روش برای برقراری ارتباط با کاربران هم بسیار کارآمد است. از آدرس‌های ایمیل کاربران می‌توانید در وب سایت‌های فروم، شبکه‌های اجتماعی، تجارت آنلاین و بانکداری برای اطلاع رسانی و دیگر موارد استفاده کنید.

نکته: برای ارسال ایمیل باید تنظیمات SMTP را پیکربندی کنید. مثلا می‌توانید از سرویس‌های ایمیل محبوبی مانند  SendGrid  استفاده کنید، که با Windows Azure براحتی یکپارچه می‌شود و از طرف توسعه دهنده اپلیکیشن هم نیاز به پیکربندی ندارد.

در مثال زیر نیاز دارید تا یک سرویس ایمیل برای ارسال ایمیل‌ها پیکربندی کنید. همچنین کاربران پیش از تایید ایمیل شان قادر به بازنشانی کلمه عبور نیستند.

Password Reset
این هم یک سناریوی رایج و استاندارد است. کاربران در صورتی که کلمه عبورشان را فراموش کنند، می‌توانند از این قابلیت برای بازنشانی آن استفاده کنند. کلمه عبور جدیدی بصورت خودکار تولید شده و برای آنها ارسال می‌شود. کاربران با استفاده از این رمز عبور جدید می‌توانند وارد سایت شوند و سپس آن را تغییر دهند.

Security Token Provider
هنگامی که کاربران کلمه عبورشان را تغییر می‌دهند، یا اطلاعات امنیتی خود را بروز رسانی می‌کنند (مثلا حذف کردن لاگین‌های خارجی مثل فیسبوک، گوگل و غیره) باید شناسه امنیتی (security token) کاربر را بازتولید کنیم و مقدار قبلی را Invalidate یا بی اعتبار سازیم. این کار بمنظور حصول اطمینان از بی اعتبار بودن تمام شناسه‌های قبلی است که توسط کلمه عبور پیشین تولید شده بودند. این قابلیت، یک لایه امنیتی بیشتر برای اپلیکیشن شما فراهم می‌کند. چرا که وقتی کاربری کلمه عبورش را تغییر بدهد از همه جا logged-out می‌شود. یعنی از تمام مرورگرهایی که برای استفاده از اپلیکیشن استفاده کرده خارج خواهد شد.

برای پیکربندی تنظیمات این قابلیت می‌توانید از فایل Startup.Auth.cs استفاده کنید. می‌توانید مشخص کنید که میان افزار OWIN cookie هر چند وقت یکبار باید شناسه امنیتی کاربران را بررسی کند. به لیست زیر دقت کنید.
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(newCookieAuthenticationOptions {
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = newPathString("/Account/Login"),
    Provider = newCookieAuthenticationProvider {
        OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
            validateInterval: TimeSpan.FromSeconds(5),
            regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
    }
});

امکان سفارشی کردن کلید‌های اصلی Users و Roles

در نسخه 1.0 نوع فیلدهای کلید اصلی در جداول Users و Roles از نوع رشته (string) بود. این بدین معنا است که وقتی از Entity Framework و Sql Server برای ذخیره داده‌های ASP.NET Identity استفاده می‌کنیم داده‌های این فیلد‌ها بعنوان nvarchar ذخیره می‌شوند. درباره این پیاده سازی پیش فرض در فروم هایی مانند سایت StackOverflow بسیار بحث شده است. و در آخر با در نظر گرفتن تمام بازخورد ها، تصمیم گرفته شد یک نقطه توسعه پذیری (extensibility) اضافه شود که توسط آن بتوان نوع فیلدهای اصلی را مشخص کرد. مثلا شاید بخواهید کلیدهای اصلی جداول Users و Roles از نوع int باشند. این نقطه توسعه پذیری مخصوصا هنگام مهاجرت داده‌های قبلی بسیار مفید است، مثلا ممکن است دیتابیس قبلی فیلدهای UserId را با فرمت GUID ذخیره کرده باشد.

اگر نوع فیلدهای کلید اصلی را تغییر دهید، باید کلاس‌های مورد نیاز برای Claims و Logins را هم اضافه کنید تا کلید اصلی معتبری دریافت کنند. قطعه کد زیر نمونه ای از نحوه استفاده این قابلیت برای تعریف کلیدهای int را نشان می‌دهد.
de Snippet
publicclassApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
}
publicclassCustomRole : IdentityRole<int, CustomUserRole>
{
    public CustomRole() { }
    public CustomRole(string name) { Name = name; }
}
publicclassCustomUserRole : IdentityUserRole<int> { }
publicclassCustomUserClaim : IdentityUserClaim<int> { }
publicclassCustomUserLogin : IdentityUserLogin<int> { }
 
publicclassApplicationDbContext : IdentityDbContext<ApplicationUser, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
}

پشتیبانی از IQueryable روی Users و Roles

کلاس‌های UserStore و RoleStore حالا از IQueryable پشتیبانی می‌کنند، بنابراین می‌توانید براحتی لیست کاربران و نقش‌ها را کوئری کنید.
بعنوان مثال قطعه کد زیر دریافت لیست کاربران را نشان می‌دهد. از همین روش برای دریافت لیست نقش‌ها از RoleManager می‌توانید استفاده کنید.
//
// GET: /Users/
public async Task<ActionResult> Index()
{
    return View(await UserManager.Users.ToListAsync());
}

پشتیبانی از عملیات Delete از طریق UserManager

در نسخه 1.0 اگر قصد حذف یک کاربر را داشتید، نمی‌توانستید این کار را از طریق UserManager انجام دهید. اما حالا می‌توانید مانند قطعه کد زیر عمل کنید.
var user = await UserManager.FindByIdAsync(id);

if (user == null)
{
    return HttpNotFound();
}

var result = await UserManager.DeleteAsync(user);

میان افزار UserManagerFactory

شما می‌توانید با استفاده از یک پیاده سازی Factory،  وهله ای از UserManager را از OWIN context دریافت کنید. این الگو مشابه چیزی است که برای گرفتن AuthenticationManager در OWIN context استفاده می‌کنیم.  این الگو همچنین روش توصیه شده برای گرفتن یک نمونه از UserManager به ازای هر درخواست در اپلیکیشن است.
قطعه کد زیر نحوه پیکربندی این میان افزار در فایل StartupAuth.cs را نشان می‌دهد.
// Configure the UserManager
app.UseUserManagerFactory(newUserManagerOptions<ApplicationUserManager>()
{
    DataProtectionProvider = app.GetDataProtectionProvider(),
    Provider = newUserManagerProvider<ApplicationUserManager>()
    {
        OnCreate = ApplicationUserManager.Create
    }
});
و برای گرفتن یک وهله از UserManager:
HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();

میان افزار DbContextFactory

سیستم ASP.NET Identity از Entity Framework برای ذخیره داده هایش در Sql Server استفاده می‌کند. بدین منظور، ASP.NET Identity کلاس ApplicationDbContext را رفرنس می‌کند. میان افزار DbContextFactory به ازای هر درخواست در اپلیکیشن یک وهله از ApplicationDbContext را به شما تحویل می‌دهد.
می توانید پیکربندی لازم را در StartupAuth.cs انجام دهید.
app.UseDbContextFactory(ApplicationDbContext.Create);

Samples

امکانات جدید را می‌توانید در پروژه https://aspnet.codeplex.com پیدا کنید. لطفا به پوشه Identity در سورس کد مراجعه کنید. برای اطلاعاتی درباره نحوه اجرای پروژه هم فایل readme را بخوانید.

برای مستندات ASP.NET Identity 1.0 هم به  http://www.asp.net/identity  سر بزنید. هنوز مستنداتی برای نسخه 2.0 منتشر نشده، اما بزودی با انتشار نسخه نهایی مستندات و مثال‌های جدیدی به سایت اضافه خواهند شد.

Known Issues
در کنار قابلیت‌های جدیدی مانند Account Confirmation و Password Reset، دو خاصیت جدید به کلاس IdentityUser اضافه شده اند: 'Email' و 'IsConfirmed'. این تغییرات الگوی دیتابیسی که توسط ASP.NET Identity 1.0 ساخته شده است را تغییر می‌دهد. بروز رسانی پکیج‌ها از نسخه 1.0 به 2.0 باعث می‌شود که اپلیکیشن شما دیگر قادر به دسترسی به دیتابیس عضویت نباشد، چرا که مدل دیتابیس تغییر کرده. برای بروز رسانی الگوی دیتابیس می‌توانید از Code First Migrations استفاده کنید.

نکته: نسخه جدید به EntityFramework 6.1.0-alpha1 وابستگی دارد، که در همین تاریخ (20 دسامبر 2013) پیش نمایش شد.  http://blogs.msdn.com/b/adonet/archive/2013/12/20/ef-6-1-alpha-1-available.aspx

EntityFramework 6.1.0-alpha1 بروز رسانی هایی دارد که سناریوی مهاجرت در ASP.NET Identity را تسهیل می‌کند، به همین دلیل از نسخه جدید EF استفاده شده. تیم ASP.NET هنوز باگ‌های زیادی را باید رفع کند و قابلیت‌های جدیدی را هم باید پیاده سازی کند. بنابراین پیش از نسخه نهایی RTM شاهد پیش نمایش‌های دیگری هم خواهیم بود که در ماه‌های آتی منتشر می‌شوند.

برای اطلاعات بیشتر درباره آینده ASP.NET Identity به لینک زیر سری بزنید.
اشتراک‌ها
لغو درخواستهای http در ASP.NET Core با CancellationToken

longer importantIt is quite a possible situation to have a user navigating to the client application’s page that sends an HTTP request to the server. While our app processing the request, a user can navigate away from that page. In such a case, we want to cancel the HTTP request since the response is no  to that user. 

لغو درخواستهای http در ASP.NET Core با CancellationToken
اشتراک‌ها
پیاده سازی یک پروژه مدیریت رزومه‌ها با React18 و ASP.NET Core7 WebAPI

Resume Management project with React18, ASP.NET Core7 WebAPI, TypeScript and Entity Framework Core
In this video, we will create a Fullstack Resume Management project using React18, ASP.NET Core WebAPI  (.NET 7),TypeScript and Entity Framework Core. The focus of this project is to show you how you can build a Fullstack project from 0 to 100 and build a simple CRUD application with ASP.NET Core WebAPI .
 

پیاده سازی یک پروژه مدیریت رزومه‌ها با React18 و ASP.NET Core7 WebAPI
اشتراک‌ها
پیاده سازی یک پروژه با React18 و ASP.NET Core7 WebAPI

Pet Store Fullstack project with React18, ASP.NET Core7 WebAPI, TypeScript and Entity Framework Core
In this video, we will create a Fullstack Pet Store project using React18, ASP.NET Core WebAPI  (.NET 7),TypeScript and Entity Framework Core. The focus of this project is to show you how you can build a Fullstack project from 0 to 100 and build a simple CRUD application with ASP.NET Core WebAPI . 

پیاده سازی یک پروژه با React18 و ASP.NET Core7 WebAPI