اگر thumbها به درستی نمایش داده نمیشود و فقط قسمتی از عکس رو مشاهده میکنید ، با استفاده از قطعه کد زیر این مشکل رفع خواهد شد :
1- تعریف کلاس به صورت زیر
2- تعریف کلاس ImageResizer همانند زیر :
3- تعریف کلاس ThumbnailCreator همانند نمونه زیر :
4- تعریف اکشن Thumbnail همانند زیر :
و در پایان اکشن GetThumbnail را همانند زیر تغییر خواهیم داد :
1- تعریف کلاس به صورت زیر
public class ImageSize { public int Height { get; set; } public int Width { get; set; } }
public class ImageResizer { public ImageSize Resize(ImageSize originalSize, ImageSize targetSize) { var aspectRatio = (float)originalSize.Width / (float)originalSize.Height; var width = targetSize.Width; var height = targetSize.Height; if (originalSize.Width > targetSize.Width || originalSize.Height > targetSize.Height) { if (aspectRatio > 1) { height = (int)(targetSize.Height / aspectRatio); } else { width = (int)(targetSize.Width * aspectRatio); } } else { width = originalSize.Width; height = originalSize.Height; } return new ImageSize { Width = Math.Max(width, 1), Height = Math.Max(height, 1) }; } }
3- تعریف کلاس ThumbnailCreator همانند نمونه زیر :
public class ThumbnailCreator { private static readonly IDictionary<string, ImageFormat> ImageFormats = new Dictionary<string, ImageFormat>{ {"image/png", ImageFormat.Png}, {"image/gif", ImageFormat.Gif}, {"image/jpeg", ImageFormat.Jpeg} }; private readonly ImageResizer resizer; public ThumbnailCreator() { this.resizer = new ImageResizer(); } public byte[] Create(Stream source, ImageSize desiredSize, string contentType) { using (var image = Image.FromStream(source)) { var originalSize = new ImageSize { Height = image.Height, Width = image.Width }; var size = resizer.Resize(originalSize, desiredSize); using (var thumbnail = new Bitmap(size.Width, size.Height)) { ScaleImage(image, thumbnail); using (var memoryStream = new MemoryStream()) { thumbnail.Save(memoryStream, ImageFormats[contentType]); return memoryStream.ToArray(); } } } } private void ScaleImage(Image source, Image destination) { using (var graphics = Graphics.FromImage(destination)) { graphics.CompositingMode = CompositingMode.SourceCopy; graphics.CompositingQuality = CompositingQuality.HighQuality; graphics.SmoothingMode = SmoothingMode.AntiAlias; graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.DrawImage(source, 0, 0, destination.Width, destination.Height); } } }
4- تعریف اکشن Thumbnail همانند زیر :
private FileContentResult CreateThumbnail(string physicalPath) { using (var fileStream = System.IO.File.OpenRead(physicalPath)) { var desiredSize = new ImageSize { Width = ThumbnailWidth, Height = ThumbnailHeight }; string contentType = MimeMapping.GetMimeMapping(physicalPath); return File(thumbnailCreator.Create(fileStream, desiredSize, contentType), contentType); } }
و در پایان اکشن GetThumbnail را همانند زیر تغییر خواهیم داد :
public virtual ActionResult GetThumbnail(string path) { path = GetSafeFileAndDirPath(path); // return File(path, contentType); return CreateThumbnail(path); }
یک نکتهی تکمیلی: اجرای دستوراتی پیش از اجرای درخواستها در postman
همانطور که در مطلب جاری نیز عنوان شد، آزمونهای Postman، پس از پایان اجرای یک درخواست و بر روی محتوای response دریافتی از سرور، اجرا میشوند. اگر نیاز به اجرای دستوراتی پیش از اجرای یک درخواست وجود داشته باشد، میتوان از گزینهی Pre-request script استفاده کرد:
مهمترین کاربرد آن، تنظیم یکسری متغیر، پیش از اجرای درخواست اصلی است. برای مثال فرض کنید که میخواهید id ارسالی به سمت سرور را random کنید. برای این منظور یک متغیر محیطی جدید را در قسمت Pre-request script درخواست جاری، تنظیم میکنیم (در مورد متغیرهای محیطی در قسمت بعد، مفصل بحث میشود):
سپس برای استفادهی از آن، اینبار url را به صورت زیر تنظیم میکنیم:
که در اینجا متغیر {{newId}} هربار پیش از اجرای درخواست، به یک مقدار اتفاقی تنظیم شده و سپس از آن برای ساخت URL نهایی استفاده میشود.
همانطور که در مطلب جاری نیز عنوان شد، آزمونهای Postman، پس از پایان اجرای یک درخواست و بر روی محتوای response دریافتی از سرور، اجرا میشوند. اگر نیاز به اجرای دستوراتی پیش از اجرای یک درخواست وجود داشته باشد، میتوان از گزینهی Pre-request script استفاده کرد:
مهمترین کاربرد آن، تنظیم یکسری متغیر، پیش از اجرای درخواست اصلی است. برای مثال فرض کنید که میخواهید id ارسالی به سمت سرور را random کنید. برای این منظور یک متغیر محیطی جدید را در قسمت Pre-request script درخواست جاری، تنظیم میکنیم (در مورد متغیرهای محیطی در قسمت بعد، مفصل بحث میشود):
pm.environment.set("newId", "name " + parseInt(Math.random() * 10000));
https://localhost:5001/users/{{newId}}
مشکلی مشاهده نشد. ابتدا فایل جدید نمونه Areas\Identity\Controllers\DynamicPermissionsAreaSampleController.cs اضافه شد؛ با این محتوای فرضی:
namespace ASPNETCoreIdentitySample.Areas.Identity.Controllers { [Authorize(Policy = ConstantPolicies.DynamicPermission)] [Area(AreaConstants.IdentityArea)] [BreadCrumb(UseDefaultRouteUrl = true, Order = 0)] [DisplayName("کنترلر نمونه با سطح دسترسی پویا در یک ناحیه خاص")] public class DynamicPermissionsAreaSampleController : Controller { [DisplayName("ایندکس")] [BreadCrumb(Order = 1)] public IActionResult Index() { return View(); } } }
نظرات اشتراکها
روش کاهش چشمگیر میزان مصرف اینترنت ویندوز 8
نکتهای در مورد ویندوز 10
ویندوز 10 دارای قابلیتی است به نام «Windows Update Delivery Optimization». این مورد سیستم شما را تبدیل به یک «به اشتراک گذارندهی به روز رسانیها» در شبکه و یا اینترنت میکند (چیزی شبیه به تورنت).
اگر از نسخهی سازمانی استفاده میکنید، این قابلیت فقط در شبکهی داخلی فعال است که سبب صرفه جویی قابل ملاحظهای برای دریافت به روز رسانیها در یک شرکت یا مجموعه خواهد شد. در مورد سایر نسخهها، خیر و این به اشتراک گذاری در سطح اینترنت است.
برای خاموش کردن آن مسیر ذیل را طی کنید:
ویندوز 10 دارای قابلیتی است به نام «Windows Update Delivery Optimization». این مورد سیستم شما را تبدیل به یک «به اشتراک گذارندهی به روز رسانیها» در شبکه و یا اینترنت میکند (چیزی شبیه به تورنت).
اگر از نسخهی سازمانی استفاده میکنید، این قابلیت فقط در شبکهی داخلی فعال است که سبب صرفه جویی قابل ملاحظهای برای دریافت به روز رسانیها در یک شرکت یا مجموعه خواهد شد. در مورد سایر نسخهها، خیر و این به اشتراک گذاری در سطح اینترنت است.
برای خاموش کردن آن مسیر ذیل را طی کنید:
Start Start > Settings > Update & security > Windows Update > Advanced options
Choose how updates are delivered, and then use the toggle to turn Delivery Optimization off
اطلاعات بیشترChoose how updates are delivered, and then use the toggle to turn Delivery Optimization off
یکی از مسائلی که همیشه برای وب سایت، از نظر موتورهای جستجو بسیار مهم است، مقوله SEO Friendly بودن آدرسهاست. خوشبختانه در ASP.NET MVC این مقوله به خوبی پوشش داده شده است. برای مثال این قابلیت در ASP.NET Webforms توسط بسته نرم افزاری به نام ASP.NET FriendlyUrls توسط تیم توسعه دهنده ASP.NET ارائه شده است. در این زمینه قبلا آقای کاویانی در همین سایت در صفحه مروری بر قابلیت جدید ASP.NET FriendlyUrls
به این مقوله پرداختهاند و با مثال، نحوهی استفاده از این بستهی نرم افزاری را آموزش دادهاند.
یکی از مسائلی که در ASP.NET Webforms با آن مواجهبودیم انتقال دائمی آدرسهای صفحات یا همان Permanent Redirect بود.
در واقع زمانیکه شما در حالت عادی از بستهی نرم افزاری مذکور استفاده میکنید، این افزونه باعث SEO Friendly شدن آدرسهای صفحات شما میگردد. با در نظر گرفتن اینکه شما مطلب مروری بر قابلیت جدید ASP.NET FriendlyUrls را مطالعه کرده اید، به فرض مثال کد زیر:
و رجیستر کردن آن در RouteTable سایت به صورت زیر در فایل Global
باعث میشود که با زدن آدرس https://www.dntips.ir/sitemap.xml آدرس https://www.dntips.ir/sitemap.aspx لود شود؛ اما در نوار آدرس، همان آدرس sitemap.xml باقی خواهد ماند.
در این روش در صورتیکه کاربر آدرس https://www.dntips.ir/sitemap.aspx را در مرورگر خود تایپ کند باز هم سایت کار میکند (آدرس مورد نظر در دسترس است) و فاجعه زمانی شروع میشود که موتور جستجو، با محتوی تکراری در سایت در دو آدرس مختلف مواجه شود (پست آقای نصیری را در بهبود SEO در ASP.NET MVC بررسی کنید) .
یا زمانیکه شما صفحات قدیمی دارید و میخواهید آنها را به آدرسهای جدید منتقل کنید. برای رفع این مشکل طوریکه هر زمان آدرس قدیمی هم وارد سیستم مسیریابی سایت شود به صورت اتوماتیک به آدرس جدید به صورت دائم منتقل شود، میتوانید کد بالا را به صورت زیر اصلاح نمایید:
با این تنظیمات، سیستم مسیریابی ملزم به انتقال دائمی آدرس، به آدرس جدید خواهد شد. یعنی شما حتی اگر آدرس https://www.dntips.ir/sitemap.aspx را وارد نمایید مسیریاب سایت برای آدرس مورد نظر، وضعیت 301 Moved Permanently را مقدار دهی کرده و فقط آدرس https://www.dntips.ir/sitemap.xml به عنوان آدرس اصلی در نظر گرفته خواهد شد.
یکی از مسائلی که در ASP.NET Webforms با آن مواجه
در واقع زمانیکه شما در حالت عادی از بستهی نرم افزاری مذکور استفاده میکنید، این افزونه باعث SEO Friendly شدن آدرسهای صفحات شما میگردد. با در نظر گرفتن اینکه شما مطلب مروری بر قابلیت جدید ASP.NET FriendlyUrls را مطالعه کرده اید، به فرض مثال کد زیر:
public static class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.EnableFriendlyUrls(); routes.MapPageRoute("Sitemap", "sitemap.xml", "~/sitemap.aspx"); } }
protected void Application_Start(object sender, EventArgs e) { RouteConfig.RegisterRoutes(RouteTable.Routes); }
در این روش در صورتیکه کاربر آدرس https://www.dntips.ir/sitemap.aspx را در مرورگر خود تایپ کند باز هم سایت کار میکند (آدرس مورد نظر در دسترس است) و فاجعه زمانی شروع میشود که موتور جستجو، با محتوی تکراری در سایت در دو آدرس مختلف مواجه شود (پست آقای نصیری را در بهبود SEO در ASP.NET MVC بررسی کنید) .
یا زمانیکه شما صفحات قدیمی دارید و میخواهید آنها را به آدرسهای جدید منتقل کنید. برای رفع این مشکل طوریکه هر زمان آدرس قدیمی هم وارد سیستم مسیریابی سایت شود به صورت اتوماتیک به آدرس جدید به صورت دائم منتقل شود، میتوانید کد بالا را به صورت زیر اصلاح نمایید:
public static void RegisterRoutes(RouteCollection routes) { routes.EnableFriendlyUrls(new FriendlyUrlSettings() { AutoRedirectMode = RedirectMode.Permanent, ResolverCachingMode = ResolverCachingMode.Dynamic }); routes.MapPageRoute("Sitemap", "sitemap.xml", "~/sitemap.aspx"); }
اگر ViewModel را همان فایل code behind عاری از ارجاعاتی به اشیاء بصری بدانیم، یک تفاوت مهم را علاوه بر مورد ذکر شده نسبت به Code behind متداول خواهد داشت: وهله سازی آن باید دستی انجام شود و خودکار نیست.
اگر به ابتدای کلاسهای code behind دقت کنید همیشه واژهی partial قابل رویت است، به این معنا که این کلاس در حقیقت جزئی از همان کلاس متناظر با XAML ایی است که مشاهده میکنید؛ یا به عبارتی با آن یکی است. فقط جهت زیبایی یا مدیریت بهتر، در دو کلاس قرار گرفتهاند اما واژه کلیدی partial اینها را نهایتا به صورت یکسان و یکپارچهای به کامپایلر معرفی خواهد کرد. بنابراین وهله سازی code behind هم خودکار خواهد بود و به محض نمایش رابط کاربری، فایل code behind آن هم وهله سازی میشود؛ چون اساسا و در پشت صحنه، از دیدگاه کامپایلر تفاوتی بین این دو وجود ندارد.
اکنون سؤال اینجا است که آیا میتوان با ViewModel ها هم همین وهله سازی خودکار را به محض نمایش یک View متناظر، پیاده سازی کرد؟
البته صحیح آن این است که عنوان شود ViewModel متناظر با یک View و نه برعکس. چون روابط در الگوی MVVM از View به ViewModel به Model است و نه حالت عکس؛ مدل نمیداند که ViewModel ایی وجود دارد. ViewModel هم از وجود View ها در برنامه بیخبر است و این «بیخبریها» اساس الگوهایی مانند MVC ، MVVM ، MVP و غیره هستند. به همین جهت شاعر در وصف ViewModel فرمودهاند که:
ای در درون برنامهام و View از تو بی خبر_________وز تو برنامهام پر است و برنامه از تو بی خبر :)
پاسخ:
بله. برای این منظور الگوی دیگری به نام ViewModel Locator طراحی شده است؛ روشهای زیادی برای پیاده سازی این الگو وجود دارند که سادهترین آنها مورد زیر است:
فرض کنید ViewModel ساده زیر را قصد داریم به کمک الگوی ViewModel Locator به View ایی تزریق کنیم:
namespace WpfViewModelLocator.ViewModels
{
public class MainWindowViewModel
{
public string SomeText { set; get; }
public MainWindowViewModel()
{
SomeText = "Data ...";
}
}
}
برای این منظور ابتدا کلاس ViewModelLocatorBase زیر را تدارک خواهیم دید:
using WpfViewModelLocator.ViewModels;
namespace WpfViewModelLocator.ViewModelLocator
{
public class ViewModelLocatorBase
{
public MainWindowViewModel MainWindowVm
{
get { return new MainWindowViewModel(); }
}
}
}
در اینجا یک وهله از کلاس MainWindowViewModel توسط خاصیتی به نام MainWindowVm در دسترس قرار خواهد گرفت. برای اینکه بتوان این کلاس را در تمام Viewهای برنامه قابل دسترسی کنیم، آنرا در App.Xaml تعریف خواهیم کرد:
<Application x:Class="WpfViewModelLocator.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vml="clr-namespace:WpfViewModelLocator.ViewModelLocator"
StartupUri="MainWindow.xaml">
<Application.Resources>
<vml:ViewModelLocatorBase x:Key="ViewModelLocatorBase" />
</Application.Resources>
</Application>
اکنون فقط کافی است در View خود DataContext را به نحو زیر مقدار دهی کنیم تا در زمان اجرا به صورت خودکار بتوان به خاصیت MainWindowVm یاد شده دسترسی یافت:
<Window x:Class="WpfViewModelLocator.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid DataContext="{Binding Path=MainWindowVm, Source={StaticResource ViewModelLocatorBase}}">
<TextBlock Text="{Binding SomeText}" VerticalAlignment="Top" Margin="5" />
</Grid>
</Window>
در مورد ViewModel ها و Viewهای دیگر هم به همین ترتیب خواهد بود. یک وهله از آنها به کلاس ViewModelLocatorBase اضافه میشود. سپس Binding Path مرتبط به DataContext به نام خاصیتی که در کلاس ViewModelLocatorBase مشخص خواهیم کرد، Bind خواهد شد.
روش دوم:
اگر در اینجا بخواهیم Path را حذف کنیم و فقط دسترسی عمومی به ViewModelLocatorBase را ذکر کنیم، باید یک Converter نوشت (چون به این ترتیب میتوان به اطلاعات Binding در متد Convert دسترسی یافت). سپس یک قرار داد را هم تعریف خواهیم کرد؛ به این صورت که ما در Converter به نام View دسترسی پیدا میکنیم (از طریق ریفلکشن). سپس نام viewModel ایی را که باید به دنبال آن گشت مثلا ViewName به علاوه کلمه ViewModel در نظر خواهیم گرفت. در حقیقت یک نوع Convection over configuration است:
using System;
using System.Globalization;
using System.Linq;
using System.Windows.Data;
namespace WpfViewModelLocator.ViewModelLocator
{
public class ViewModelLocatorBaseConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
//مقدار در اینجا همان مشخصات ویوو است
if (value == null) return null;
string viewTypeName = value.GetType().Name;
//قرار داد ما است
//ViewModel Name = ViewName + "ViewModel"
string viewModelName = string.Concat(viewTypeName, "ViewModel");
//یافتن اسمبلی که حاوی ویوو مدل ما است
var asms = AppDomain.CurrentDomain.GetAssemblies();
var viewModelAsmName = "WpfViewModelLocator"; //نام پروژه مرتبط
var viewModelAsm = asms.Where(x => x.FullName.Contains(viewModelAsmName)).First();
//یافتن این کلاس ویوو مدل مرتبط
var viewModelType = viewModelAsm.GetTypes().Where(x => x.FullName.Contains(viewModelName)).FirstOrDefault();
if (viewModelType == null)
throw new InvalidOperationException(string.Format("Could not find view model '{0}'", viewModelName));
//وهله سازی خودکار آن
return Activator.CreateInstance(viewModelType);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
کار این تبدیلگر بسیار ساده و واضح است. Value دریافتی، وهلهای از view است. پس به این ترتیب میتوان نام آنرا یافت. سپس قرارداد ویژه خودمان را اعمال میکنیم به این ترتیب که ViewModel Name = ViewName + "ViewModel" و سپس به دنبال اسمبلی که حاوی این نام است خواهیم گشت. آنرا یافته، کلاس مرتبط را در آن پیدا میکنیم و در آخر، به صورت خودکار آنرا وهله سازی خواهیم کرد.
اینبار تعریف عمومی این Conveter در فایل App.Xaml به صورت زیر خواهد بود:
<Application x:Class="WpfViewModelLocator.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vml="clr-namespace:WpfViewModelLocator.ViewModelLocator"
StartupUri="MainWindow.xaml">
<Application.Resources>
<vml:ViewModelLocatorBaseConverter x:Key="ViewModelLocatorBaseConverter" />
</Application.Resources>
</Application>
و استفادهی آن در تمام View های برنامه به شکل زیر میباشد (بدون نیاز به ذکر هیچ نام خاصی و بدون نیاز به کلاس ViewModelLocatorBase یاد شده در ابتدای مطلب):
<Window x:Class="WpfViewModelLocator.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self},
Converter={StaticResource ViewModelLocatorBaseConverter}}"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBlock Text="{Binding SomeText}" VerticalAlignment="Top" Margin="5" />
</Grid>
</Window>
مدتی قبل سؤال مهمی در سایت مطرح شد، به شرح زیر:
«من از کنترلهای تلریک استفاده میکنم که یک سری اسکریپت را بصورت
http://localhost:1244/WebResource.axd?d=aklE6L8AEfPEgIS3T-oXc6mevPfbpi6VRp_ZTP2nBVrnt5ULOFYD3GNWRrDHwANC3VDQlL8dLAa5g35dzgHyuzAgAguIpYrf-_NXIJwNNu0YRSnH3-MgKMfnwKBKF_Lk2E5oeIcLL78uDlQ0se_GxQ2&t=635231470568640000
به فرم تزریق میکند و بعضی وقتها داخلش xp و یا یک سری دستورات اسکیوال تولید میشوند. در این حالت این مسیرها توسط ISA Server در شبکه داخلی حمله تشخیص داده شده و بلاک خواهند شد و عملا برنامه از کار میافتد. آیا راهی برای خلاصی از دست آنها هست؟»
پاسخ: بلی. از دات نت 3 و نیم به بعد، امکان جایگزینی کامل اسکریپتهای خودکار مدفون شده در اسمبلیها با فایلهای استاتیک پیش بینی شدهاست که در ادامه نحوهی استخراج و کار با آنها را بررسی خواهیم کرد.
الف) یافتن اسکریپتهای مدفون در اسمبلیها
در ابتدا اسمبلی حاوی کنترلهای وب فرم مدنظر خود را باید توسط برنامههای Reflector یا ILSpy و امثال آنها گشوده و نام دقیق منبع و همچنین محتوای آن فایل اسکریپت را استخراج کنید. برای مثال:
در این تصویر، اسمبلی استاندارد System.Web.Extensions مورد بررسی قرار گرفته است. برای نمونه اگر بخواهید اسکریپتهای متناظر با ScriptManager و UpdatePanel را با معادلهای استاتیک آنها جایگزین کنید، باید دو فایل MicrosoftAjaxWebForms.js و MicrosoftAjax.js را از این اسمبلی استخراج نمائید. (برنامههای یاد شده امکان ذخیره سازی منابع را نیز میدهند)
ب) وادار کردن ASP.NET به استفاده از نسخهی استاتیک منابع
فرض کنید دو اسکریپت یاد شده را در فایلهای staticJS1.js و staticJS2.js در ریشهی سایت خود ذخیره کردهاید. اکنون یک ScriptManager را به صفحه اضافه کرده و مطابق کدهای فوق، اسکریپتهای مدفون شده در اسمبلی System.Web.Extensions را به این فایلهای استاتیک هدایت کنید. همانطور که عنوان شد نام این مداخل باید دقیقا با نام موجود در اسمبلی یکی باشد؛ در غیر اینصورت با خطای ذیل مواجه خواهید شد:
اکنون اگر برنامه را اجرا کنید (با فرض قرار داشتن یک ScriptManager و UpdatePanel در صفحه)، اینبار دیگر در سورس صفحه، شاهد آدرسهای طولانی WebResource.axd و ScriptResource.axd نخواهید بود. به صورت خودکار از دو فایل استاتیک تنظیم شده، استفاده میشود:
بدیهی است در صورت نیاز، باید تعاریف سایر اسکریپتهای مدفون در اسمبلی یاد شده یا اسمبلی System.Web را نیز به صفحه از طریق ScriptManager اضافه کرد. در مورد کنترلهای ثالث نیز وضع به همین صورت است و استاندارد آن تفاوتی نمیکند.
یک نکتهی تکمیلی
در مطلب «ASP.NET 4.5 ScriptManager Improvements in WebForms » مشاهده خواهید کرد که از ASP.NET 4.5 به بعد، طی دو بستهی نیوگت که هر از چندگاهی به روز میشوند، کلیه اسکریپتهای System.Web و System.Web.Extensions خارج از این اسمبلیها نیز قابل دریافت بوده و با استفاده از سیستم bunding & minification میتوان آنها را فشرده و یکی کرد.
«من از کنترلهای تلریک استفاده میکنم که یک سری اسکریپت را بصورت
http://localhost:1244/WebResource.axd?d=aklE6L8AEfPEgIS3T-oXc6mevPfbpi6VRp_ZTP2nBVrnt5ULOFYD3GNWRrDHwANC3VDQlL8dLAa5g35dzgHyuzAgAguIpYrf-_NXIJwNNu0YRSnH3-MgKMfnwKBKF_Lk2E5oeIcLL78uDlQ0se_GxQ2&t=635231470568640000
به فرم تزریق میکند و بعضی وقتها داخلش xp و یا یک سری دستورات اسکیوال تولید میشوند. در این حالت این مسیرها توسط ISA Server در شبکه داخلی حمله تشخیص داده شده و بلاک خواهند شد و عملا برنامه از کار میافتد. آیا راهی برای خلاصی از دست آنها هست؟»
پاسخ: بلی. از دات نت 3 و نیم به بعد، امکان جایگزینی کامل اسکریپتهای خودکار مدفون شده در اسمبلیها با فایلهای استاتیک پیش بینی شدهاست که در ادامه نحوهی استخراج و کار با آنها را بررسی خواهیم کرد.
الف) یافتن اسکریپتهای مدفون در اسمبلیها
در ابتدا اسمبلی حاوی کنترلهای وب فرم مدنظر خود را باید توسط برنامههای Reflector یا ILSpy و امثال آنها گشوده و نام دقیق منبع و همچنین محتوای آن فایل اسکریپت را استخراج کنید. برای مثال:
در این تصویر، اسمبلی استاندارد System.Web.Extensions مورد بررسی قرار گرفته است. برای نمونه اگر بخواهید اسکریپتهای متناظر با ScriptManager و UpdatePanel را با معادلهای استاتیک آنها جایگزین کنید، باید دو فایل MicrosoftAjaxWebForms.js و MicrosoftAjax.js را از این اسمبلی استخراج نمائید. (برنامههای یاد شده امکان ذخیره سازی منابع را نیز میدهند)
ب) وادار کردن ASP.NET به استفاده از نسخهی استاتیک منابع
<asp:ScriptManager ID="Scriptmanager1" runat="server"> <Scripts> <asp:ScriptReference Name="MicrosoftAjaxWebForms.js" Assembly="System.Web.Extensions" Path="~/staticJS1.js" /> <asp:ScriptReference Name="MicrosoftAjax.js" Assembly="System.Web.Extensions" Path="~/staticJS2.js" /> </Scripts> </asp:ScriptManager>
The assembly 'System.Web.Extensions' does not contain a Web resource that has the name 'xyz.js'. Make sure that the resource name is spelled correctly. Make sure that the application references the correct version of an ASP.NET AJAX Framework assembly.
<script src="staticJS1.js" type="text/javascript"></script> <script src="staticJS2.js" type="text/javascript"></script>
یک نکتهی تکمیلی
در مطلب «ASP.NET 4.5 ScriptManager Improvements in WebForms » مشاهده خواهید کرد که از ASP.NET 4.5 به بعد، طی دو بستهی نیوگت که هر از چندگاهی به روز میشوند، کلیه اسکریپتهای System.Web و System.Web.Extensions خارج از این اسمبلیها نیز قابل دریافت بوده و با استفاده از سیستم bunding & minification میتوان آنها را فشرده و یکی کرد.
نظرات مطالب
ASP.NET MVC #18
ببخشید؛
با این تفاسیر متد IsUserInRole باید در داخل کلاس EfRole در لایه سرویس، پوشه EFServices به شکل زیر پیاده سازی بشه؟ یعنی میشه به طور مستقیم از شی context استفاده کرد؟
public class EfRole : EfGenericService<Role>, IRole { public EfRole(IUnitOfWork uow) : base(uow) { } public bool IsUserInRole(string username, string roleName) { using (var context = new PublishingContext()) { var user = context.Users.Where(x => x.Username.Equals(username, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault(); var roles = from ur in user.Rolls from r in context.Rolls where ur.Id == r.Id select r.Rol; if (user != null) return roles.Any(x => x.Equals(roleName, StringComparison.CurrentCultureIgnoreCase)); else return false; } } }
نظرات مطالب
SignalR - قسمت سوم
خیلی خوب و کاربردی , واقعا خسته نباشید.
سئوال اینجاست , زمانی که پیامی رو ارسال میکنیم دستور زیر رو اجرا میکنیم که این پیام واسه بقیه اعضا هم ارسال بشه
زمانی که مثلا توی یک اتاق 10 نفر در یک زمان پیامی رو ارسال میکنند , واسه همه اعضا 10 تا Response جدا میاد ؟ اگه اینطور هست چطور میشه این مورد رو بهینه کرد ؟
سئوال اینجاست , زمانی که پیامی رو ارسال میکنیم دستور زیر رو اجرا میکنیم که این پیام واسه بقیه اعضا هم ارسال بشه
public void SendMessage(string message, string id) { Clients.reciveMessage(message, Users[id]); }