فرض کنید یک برنامه ASP.Net نوشتهاید که کار آن نمایش یک سری فید از سایتهای مختلف است یا دریافت وضعیت آب و هوا از یک وب سرویس و نمایش آن در سایت میباشد و امثال آن (استفاده از WebRequest ، WebClient ، XmlDom/Reader و ...).
اگر همین عملیات را یکبار با ASP.Net 1.1 و بار دیگر با ASP.Net 2.0 به بالا انجام دهید، متوجه تفاوت سرعت دریافت قابل تاملی خواهید شد (ASP.Net 2.0 به بعد حدودا تا 10 ثانیه کندتر عمل میکند). علت چیست؟
در دات نت 1.1 ، تنظیمات پروکسی پیش فرض در کتابخانههای مربوطه وجود نداشت و به نال تنظیم شده بود. در دات نت 2 به بعد این مورد به پروکسی پیش فرض سیستم، تنظیم شده است. پروکسی پیش فرض سیستم همان تنظیماتی است که در internet explorer صورت میگیرد.
کاربر پیش فرض ASP.Net (مثلا NETWORK SERVICE) دسترسی خواندن این اطلاعات را از رجیستری ویندوز ندارد. علت این وقفه هم همین مورد است! (حتی اگر برنامه ویندوزی شما هم دسترسی خواندن اطلاعات کلیدهای HKLM رجیستری ویندوز را نداشته باشد، باز هم این مساله رخ خواهد داد)
دات نت فریم ورک سعی میکند تا این تنظیمات را از رجیستری یا مکانهای میسر دیگر بخواند و در آخر پس از شکست کلیه حالات مختلف، کلاینت را به صورت مستقیم متصل خواهد کرد.
خوشبختانه این عملکرد پیش فرض قابل تغییر است. تنها کافی است چند سطر زیر را به فایل config برنامه خود اضافه کنید:
<system.net>
<defaultProxy>
<proxy bypassonlocal="true" usesystemdefault="false" />
</defaultProxy>
</system.net>
راه دیگر انجام آن، نسبت دادن نال به خاصیت Proxy شیء HttpWebRequest است که همین اثر را خواهد داشت.
کار با کوکیها در ASP.NET Core
مرورگر کروم، از نگارش 80 آن به بعد به همراه یک تغییر غیرسازگار با نگارشهای قبلی آن است: از این پس تمام کوکیهای آن در صورتیکه تنظیمات SameSite را نداشته باشند، به صورت SameSite=Lax تفسیر میشوند؛ که تغییر امنیتی فوق العادهای است و سبب خواهد شد تا بسیاری از حملات مانند CSRF به طور کامل غیرعملی شوند. اما ... این مورد سبب از کار افتادن برنامههایی خواهد شد که از سرویسهایی مانند IdentityServer استفاده میکنند. در یک چنین حالتی نیاز خواهید داشت برای رفع این مشکل، SameSite=None را تنظیم کنید.
اما SameSite چیست؟ تنظیم و خاصیت SameSite از سال 2016 به کوکیها اضافه شد تا بتوان توسط آن در برابر حملات CSRF مقاومت کرد؛ مقادیر اولیهی آن نیز Lax و Strict بودند. Lax به این معنا است که کوکیها در حین مرور یک سایت، باید به صورت خودکار به سمت سرور همان سایت ارسال شوند؛ اما در حالت مرور سایت و هدایت از طریق سایتهای دیگر به سایت ما، فقط درخواستهای GET رسیده میتوانند کوکیها را نیز ارسال کنند. حالت Strict آن فقط کوکیهای تنظیم شدهی درون یک سایت را معتبر شمرده و ارسال میکند. عدم تنظیم SameSite نیز مشکل خاصی را ایجاد نمیکرد. برای مثال اعمال اعتبارسنجی مبتنی بر OpenIdConnect مانند login/logout، نیاز دارند در طی یک درخواست POST، اطلاعاتی را به سایت خارجی درخواست کننده ارسال کنند. برای اینکه این عملیات به درستی صورت گیرد، میبایستی تنظیمات SameSite انجام نمیشد تا جابجایی کوکیها بین دو دومین مختلف در حالت POST، بدون مشکل صورت میگرفت.
وضعیت دات نت در این مورد: با به روز رسانیهای جدید دات نت 4.7.2 و همچنین NET Core 2.1.، مقدار جدید None توسط برنامه (در CookieOptions) قابل تنظیم خواهد بود که سبب تولید SameSite=None میشود. به علاوه در NET Core 3.1.، مقدار SameSite.Unspecified را نیز میتوان تنظیم کرد که سبب خواهد شد تا خاصیت SameSite اصلا تنظیم نشود (اصلا به درخواست اضافه نشود؛ تا مرورگرهایی که نمیتوانند مقدار None را تفسیر کنند، به اشتباه آنرا به Strict تنظیم نکنند).
به صورت خلاصه: اگر برنامهی شما از OpenIdConnect و یا IdentityServer استفاده نمیکند، هیچ تنظیمی را تغییر ندهید! تنظیم پیشفرض SameSite=Lax گوگل سبب میشود تا عملا حملات CSRF دیگر بر روی سایت شما قابل اجرا نباشد. اما اگر از IdentityServer استفاده میکنید، این تنظیم پیشفرض، سبب از کار افتادن امکان ارسال کوکیهای حالت POST، به سایتهای خارجی میشود و برنامه و سیستم شما از کار خواهد افتاد.
کلیدهای مربوط به Request
ضروری؟ | نام کلید | مقدار |
بله | "owin.RequestBody" | یک Stream همراه با request body. اگر body برای request وجود نداشته باشد، Stream.Null به عنوان placeholder قابل استفاده است. |
بله | "owin.RequestHeaders" | یک دیکشنری به صورت IDictionary<string, string[]> از هدرهای درخواست. |
بله | "owin.RequestMethod" | رشتهایی حاوی نوع فعل متد HTTP مربوط به درخواست (مانند GET and POST ) |
بله | "owin.RequestPath" | path درخواست شده به صورت string |
بله | "owin.RequestPathBase" | قسمتی از path درخواست به صورت string |
بله | "owin.RequestProtocol" | نام و نسخهی پروتکل (مانند HTTP/1.0 or HTTP/1.1 ) |
بله | "owin.RequestQueryString" | رشتهای حاوی query string ؛ بدون علامت ? (مانند foo=bar&baz=quux ) |
بله | "owin.RequestScheme" | رشتهایی حاوی URL scheme استفاده شده در درخواست (مانند HTTP or HTTPS ) |
ضروری؟ | نام کلید | مقدار |
بله | "owin.ResponseBody" | یک Stream جهت نوشتن response body در خروجی |
بله | "owin.ResponseHeaders" | یک دیکشنری به صورت IDictionary<string, string[]> از هدرهای response |
خیر | "owin.ResponseStatusCode" | یک عدد صحیح حاوی کد وضعیت HTTP response ؛ حالت پیشفرض 200 است. |
خیر | "owin.ResponseReasonPhrase" | یک رشته حاوی reason phrase مربوط به status code ؛ اگر خالی باشد در نتیجه سرور بهتر است آن را مقداردهی کند. |
خیر | "owin.ResponseProtocol" | یک رشته حاوی نام و نسخهی پروتکل (مانند HTTP/1.0 or HTTP/1.1 )؛ اگر خالی باشد؛ “owin.RequestProtocol” به عنوان مقدار پیشفرض در نظر گرفته خواهد شد. |
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net461" /> <package id="Microsoft.Owin.Host.SystemWeb" version="3.0.1" targetFramework="net461" /> <package id="Owin" version="1.0" targetFramework="net461" />
using Owin; namespace SimpleOwinWebApp { public class Startup { public void Configuration(IAppBuilder app) { } } }
using Owin; namespace SimpleOwinWebApp { public class Startup { public void Configuration(IAppBuilder app) { app.Use(async (ctx, next) => { await ctx.Response.WriteAsync("Hello"); }); } }
Func<IOwinContext, Func<Task>, Task> handler
app.Use(async (ctx, next) => { var response = ctx.Environment["owin.ResponseBody"] as Stream; using (var writer = new StreamWriter(response)) { await writer.WriteAsync("Hello"); } });
using System; using Microsoft.Owin.Hosting; namespace SimpleOwinConsoleApp { class Program { static void Main(string[] args) { using (WebApp.Start<Startup>("http://localhost:12345")) { Console.WriteLine("Listening to port 12345"); Console.WriteLine("Press Enter to end..."); Console.ReadLine(); } } } }
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace SimpleOwinCoreApp { public class Startup { public void ConfigureServices(IServiceCollection services) { } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.Run(async (context) => { await context.Response.WriteAsync("Hello World!"); }); } } }
using System.Threading.Tasks; using Microsoft.AspNetCore.Http; namespace SimpleOwinCoreApp.Middlewares { public class SimpleMiddleware { private readonly RequestDelegate _next; public SimpleMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext ctx) { // قبل از فراخوانی میانافزار بعدی await ctx.Response.WriteAsync("Hello DNT!"); await _next(ctx); // بعد از فراخوانی میانافزار بعدی } } }
app.UseMiddleware<SimpleMiddleware>();
"Microsoft.AspNetCore.Owin": "1.0.0"
app.UseOwin(pipeline => { pipeline(next => new MyKatanaBasedMiddleware(next).Invoke) });
using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; namespace SimpleOwinAspNetCore.Middleware { public class IpBlockerMiddleware { private readonly RequestDelegate _next; private readonly IpBlockerOptions _options; public IpBlockerMiddleware(RequestDelegate next, IpBlockerOptions options) { _next = next; _options = options; } public async Task Invoke(HttpContext context) { var ipAddress = context.Request.Host.Host; if (IsBlockedIpAddress(ipAddress)) { context.Response.StatusCode = 403; await context.Response.WriteAsync("Forbidden : The server understood the request, but It is refusing to fulfill it."); return; } await _next.Invoke(context); } private bool IsBlockedIpAddress(string ipAddress) { return _options.Ips.Any(ip => ip == ipAddress); } } }
using System.Collections.Generic; namespace SimpleOwinAspNetCore.Middleware { public class IpBlockerOptions { public IpBlockerOptions() { Ips = new[] { "192.168.1.1" }; } public IList<string> Ips { get; set; } } }
using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; namespace SimpleOwinAspNetCore.Middleware { public static class IpBlockerExtensions { public static IApplicationBuilder UseIpBlocker(this IApplicationBuilder builder, IConfigurationRoot configuration, IpBlockerOptions options = null) { return builder.UseMiddleware<IpBlockerMiddleware>(options ?? new IpBlockerOptions { Ips = configuration.GetSection("block_list").GetChildren().Select(p => p.Value).ToArray() }); } } }
{ "block_list": [ "192.168.1.1", "localhost", "127.0.0.1", "172.16.132.151" ] }
public IConfigurationRoot Configuration { set; get; } public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("blockedIps.json"); Configuration = builder.Build(); }
app.UseIpBlocker(Configuration);
CREATE DATABASE DNTSampleDB GO USE DNTSampleDB GO CREATE TABLE Cars( ID int PRIMARY KEY, Name VARCHAR(255) NOT NULL, NumberPlate VARCHAR(255) UNIQUE,--شماره پلاک Model INT);
INSERT INTO Cars VALUES (1,'Mazda','ABC 123',199), (2,'Mazda','ABC 345',207), (3,'Mazda','ABC 758',305), (4,'Mazda','ABC 741',306), (5,'Mazda','ABC 356',124)
INSERT INTO Cars VALUES (2,'Mazda','ABC 123111',199)
INSERT INTO Cars VALUES (6,'Mazda','ABC 741',200)
USE DNTSampleDB GO sp_help cars
SQL Server 2014 برای عموم منتشر شد
sql2014.zip
باید عرض کنم بله وقتی کاربری یافت نشد چه کاری میتوانیم انجام دهیم؟
Errors should be handled via exceptions, but successes shouldn't
The typical meaning of the word "exception" is an event that is unexpected rather than part of normal operation; otherwise people would just say "event"
Methods should have a way to indicate various kinds of results. Whether those results are treated as exceptional should depend on the caller.
var image = document.createElement("img"); image.setAttribute("src", "logo.png"); React.createElement("img", { src : "logo.png" });
Virtual DOM
تفاوت در ساخت تگهای HTML به صورت مجازی بین JavaScript و React این است که React وضعیت تگهایی را که میسازد دنبال میکند. برای مثال فرض کنید نام سه محصول را در یک تگ < ul > نشان دادهایم. React وضعیت اصلی این تگ را که به مرورگر فرستاده، در حافظه دارد و همچنین در اثر تغییر منبع دادهای که برای < ul > مشخص کردهایم (که میتواند ورود اطلاعات به صورت Ajax باشد (مثلا اضافه شدن یک محصول جدید)) وضعیت جدیدی را برای تگ < ul > در حافظه ایجاد میکند. با وجود دو وضعیت برای یک تگ در حافظه، React میتواند تفاوت بین آنها را تشخیص داده و تگ را به روز کند. به این حالت عملکرد React ، اصطلاحا Virtual DOM میگویند.
React رابط کاربری را به صورت یک مدل میبیند و این مدل را با توجه به وضعیت اصلی آن در حافظه دوباره میسازد. برای React مهم نیست که ماهیت تغییر چیست. فقط وضعیتها را مثل دو عکس میبیند و میفهمد که آیا چیزی عوض شدهاست یا نه. دیالوگ React با مرورگر اینطور است: ای تگ < ul > این لیست را نشان بده (لیستی با سه محصول)، و بعد میگوید: ای تگ < ul > این لیست را نشان بده (لیستی با چهار محصول)!
کامپوننتهای React
<a href = “http://google.com”> <img src=”google.png”/> </a> // Components <clickableimage/> <linkimage/>
در کد بالا، بخش اول واضح است. عکسی که قابلیت کلیک شدن را دارد. حال فرض کنید یکی از کامپوننتهای <clickableimage/> یا <linkimage/>، همان تصویر قابل کلیک را ایجاد کنند. با نام گذاری واضح کامپوننتها، خوانایی برنامه بهتر میشود. یعنی میدانیم هر کامپوننت چه کاری را برای ما انجام میدهد. با این تصور که اگر تگهای زیاد و طولانی را در بخش رابط کاربری داریم، ارزش استفاده از کامپوننتهای React مشخص میشود.
قابلیت استفاده مجدد
در React کامپوننتها برای اساس توابع ساخته میشوند. یعنی وقتی یک کامپوننت را صدا بزنیم، در واقع یک تابع را اجرا میکنیم. در نتیجه کامپوننتها رفتار توابع را دارند؛ ورودی میگیرند و خروجی که یک DOM مجازی است را تحویل میدهند. اگر تابعی که مسئول ساخت کامپوننت است وابستگی به توابع یا متغیرهای بیرونی نداشته باشد، میتواند در جای دیگری از برنامه یا برنامهای دیگر مجددا استفاده شود. کد زیر نشان میدهد که چطور کامپوننتهای React ساخته میشوند.var ClickableImage = function(props) { return ( <a href={props.href}> <img src={props.src} /> </a> ); }; ReactDOM.render( <ClickableImage href="http://google.com" src="logo.png" />, document.getElementById("targetDivId"));