اشتراک‌ها
گوگل کد به زودی از دسترس خارج خواهد شد

When we started the Google Code project hosting service in 2006, the world of project hosting was limited. We were worried about reliability and stagnation, so we took action by giving the open source community another option to choose from. Since then, we’ve seen a wide variety of better project hosting services such as GitHub and Bitbucket bloom. Many projects moved away from Google Code to those other systems. To meet developers where they are, we ourselves migrated nearly a thousand of our own open source projects from Google Code to GitHub. 

گوگل کد به زودی از دسترس خارج خواهد شد
نظرات مطالب
ارتقاء به ASP.NET Core 2.0 - معرفی بسته‌ی Microsoft.AspNetCore.All
به روز رسانی در مورد ابزار dotnet-outdated 
اگر پیشتر از ابزار dotnet-outdated برای به روز رسانی سراسری بسته‌های نیوگت پروژه‌های خود استفاده می‌کردید، این ابزار دیگر نگهداری نمی‌شود و به dotnet-outdated-tool تغییر نام یافته‌است. در این حالت اجرای دستورات زیر، در یک فایل bat و یا cmd در کنار فایل sln شما، کار به روز رسانی یک‌دست تمام وابستگی‌ها را انجام می‌دهد؛ البته اگر نیاز به به روز رسانی به net5 را داشته باشید، باید ابتدا یک سطر <TargetFramework>net5.0</TargetFramework> را نیز در تمام فایل‌های csproj خود اصلاح کنید و بعد اجرای دستوارت زیر:
dotnet restore
setx DOTNET_HOST_PATH "%ProgramFiles%\dotnet\dotnet.exe" /M
dotnet tool uninstall --global dotnet-outdated
dotnet tool update --global dotnet-outdated-tool
dotnet outdated -u
dotnet restore
pause
مطالب
دیباگ سرویس‌های ویندوز
یکی از سخت‌ترین چالش‌های تهیه سرویس‌های ویندوز، دیباگ آن‌ها است. برای تست و دیباگ کد‌ها در ویندوز سرویس‌ها، راهکارها و ابزارهای متفاوتی ارائه شده‌اند که در این مقاله قصد دارم یکی از آن‌ها را معرفی کنم.

برای تست کدها در ویندوز سرویس، اولین راه پیشنهادی همیشه این بوده که سرویس را موقتا به Console Application تبدیل کنیم و با تهیه یک متد در سرویس و فراخوانی آن در متد Main برنامه کنسولی، بتوانیم به دیباگ برنامه بپردازیم. مثال: 
تغییرات مورد نیاز در سرویس : 
 public Service1(string[] args)
        {
            this.args = args;
        }

        internal void TestStartupAndStop(string[] args)
        {
            this.OnStart(args);
            Console.ReadLine();
            this.OnStop();
        }
        protected override void OnStart(string[] args)
        {
            Console.WriteLine("Service Started");
            //Do Somthing
        }
تغییرات مورد نیاز در متد Main : 
static void Main(string[] args)
        {
            if (Environment.UserInteractive)
            {
                Service1 service1 = new Service1(args);
                service1.TestStartupAndStop(args);
            }
            else
            {
                // Put the body of your old Main method here.
                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[]
                {
                new Service1()
                };
                ServiceBase.Run(ServicesToRun);
            }
        }
برای کسب اطلاعات بیشتر در مورد این روش برای دیباگ، میتوانید به این لینک مراجعه نمایید.
این راه حل کلی، عموما جوابگوی نیازها هست و تست و دیباگ را میتوانیم از همین طریق انجام دهیم. اما مشکلات زیادی هم دارد. به طور مثال یکی از مشکلات هنگام استفاده از این راه حل، تفاوت دسترسی‌ها و کاربر اجرا کننده برنامه، در حالت کنسول و سرویس ویندوز است. به همین دلیل بسیار با این مشکل مواجه خواهید شد که هنگام دیباگ مشکلی وجود نداشته ولی بعد از نصب سرویس، برنامه بدرستی کار نکند. به طور مثال وقتی شما سرویس را به صورت Network Service ویا Local System نصب کنید، دسترسی برنامه به کلیدهای رجیستری , فایل سیستم و Environment تغییر میکند. در نتیجه اگر مانند روشی که در بالا ذکر شد دیباگ را انجام دهید، نمیتوانید مشکلات را شناسایی و رفع کنید. همینطور روش قبلی صرفا برای دیباگ نیازمند تغییر در سورس کد اصلی برنامه است که خود میتواند مشکلات یا محدودیت‌هایی را هنگام کار به صورت تیمی ایجاد کند.

برای دیباگ سرویس نصب شده چه باید کرد ؟
در این مطلب نمیخواهم وارد جزئیات نحوه نصب ویندوز سرویس‌ها شوم؛ چراکه خودش مبحث گسترده‌ایست. برای کسب اطلاعات بیشتر در مورد نحوه‌ی نصب سرویس‌ها میتوانید به این لینک مراجعه نمایید.
برای اینکه بعد از نصب سرویس بتوانیم به دیباگ آن بپردازیم، مراحل زیر را طی کنید.
1- اول در ابتدای متد OnStart سرویس، تکه کد زیر را وارد نمایید و یک BreakPoint کنارش قرار دهید:
System.Diagnostics.Debugger.Launch(); // Start Debugger
البته پیشنهاد میکنم که این بخش را به صورت زیر وارد کنید تا فقط درصورتیکه سرویس شما در حالت Debug ساخته شده باشد، اجرا شود.
#if DEBUG
      System.Diagnostics.Debugger.Launch(); // Start Debugger
#endif
2- سرویس را با استفاده از installutil نصب کرده و استارت نمایید.
هنگام استارت سرویس، پنجره زیر نمایان میشود.


درصورتی که Yes را انتخاب نمایید، میتوانید در یک Solution جدید به دیباگ برنامه بپردازید و دیباگر به صورت خودکار کدهای مورد نیاز را برای شروع کار، برای شما بارگذاری خواهد کرد. اما در پروژه‌های بزرگ‌تر به دلیل وابستگی‌های سرویس به کتابخانه‌های متعدد، امکان دیباگ کامل را هنوز در اختیار نداریم و دیباگ ما در اینجا، محدود به خود سرویس است. برای اینکه بتوانیم دیباگ را کامل در Solution اصلی برنامه انجام دهید، قبل پاسخ دادن به پنجره بالا یک مرحله دیگر از کار باقی مانده است.
3- در Solution اصلی برنامه خود، کلید‌های Ctrl+Alt+P را فشار دهید؛ یا از طریق منوی Debug > Attach to process، پنجره Attack to Process را باز نمایید.

همانطور که در تصویر مشاهده میکنید، ID پروسه را در هر دو عکس برایتان مشخص کرد‌ه‌ام (هایلایت زرد) که به راحتی میتوانید پروسه سرویستان را از این طریق پیدا کنید.
کافیست روی کلید Attach کلیک نمایید تا در Solution اصلی برنامه بتوانید به صورت کامل به دیباگ سرویس نصب شده بپردازید. پس از اتچ کردن پروسه به Solution خود، پنجره اول (Visual Studio Just-In Time debugger) را ببندید یا گزینه No را انتخاب نمایید.
نکته: برای استفاده از این روش باید Visual Studio به صورت Administrator اجرا شده باشد.
مطالب
ایجاد نصاب یک قالب پروژه جدید چند پروژه‌ای در ویژوال استودیو
در ویژوال استودیو ذیل منوی File، گزینه‌ای وجود دارد به نام  Export template که کار آن تهیه یک قالب، بر اساس ساختار پروژه جاری است. این قابلیت جهت تهیه قالب‌های سفارشی، برای کاهش زمان تهیه پروژه‌ها بسیار مفید است. به این ترتیب می‌توان بسیاری از نکات مدنظر را، در یک قالب ویژه لحاظ کرد و به دفعات بدون نیاز به copy/paste مداوم فایل‌ها و تنظیمات اولیه، بسیار سریع یک پروژه جدید دلخواه را ایجاد نمود.
اما ... این قالب تهیه شده، صرفا بر اساس یکی از چندین پروژه Solution جاری تهیه می‌شود و همچنین نصب و توزیع آن نیز دستی است. در ادامه قصد داریم با نحوه تهیه یک قالب جدید پروژه متشکل از چندین پروژه، به همراه تهیه فایل VSI نصاب آن، آشنا شویم.


تهیه یک ساختار نمونه

یک پروژه جدید کنسول را به نام MyConsoleApplication ایجاد کنید. سپس به Solution جاری، یک Class library جدید را به نام مثلا MyConsoleApplication.Tests اضافه نمائید. تا اینجا به شکل زیر خواهیم رسید:


اکنون قصد داریم از این پروژه خاص، یک قالب تهیه کنیم؛ تا هربار نخواهیم یک چنین مراحلی را تکرار کنیم.


تهیه قالب به ازای هر پروژه در Solution

در همین حال که Solution باز است، به منوی File و گزینه Export template مراجعه کنید.


در اینجا تنها امکان انتخاب یک پروژه وجود دارد. به همین جهت این مرحله را باید به ازای هر تعداد پروژه موجود در Solution یکبار تکرار کرد.


اکنون در پوشه My Documents\Visual Studio 2010\My Exported Templates دو فایل zip به نام‌های MyConsoleApplication.zip و MyConsoleApplication.Tests.zip وجود دارند. هر دو فایل را توسط برنامه‌های مخصوص گشودن فایل‌های Zip گشوده و تبدیل به دو پوشه باز شده MyConsoleApplication و MyConsoleApplication.Tests کنید.



افزودن فایل MyTemplate.vstemplate چند پروژه‌ای

در همین پوشه جاری که اکنون حاوی دو پوشه باز شده است، یک فایل متنی جدید را با محتوای ذیل به نام MyTemplate.vstemplate ایجاد کنید:
<VSTemplate Version="3.0.0" Type="ProjectGroup"
xmlns="http://schemas.microsoft.com/developer/vstemplate/2005">
  <TemplateData>
    <Name>MyConsoleApplication</Name>
    <Description>MyConsoleApplication Desc</Description>
    <ProjectType>CSharp</ProjectType>
  </TemplateData>
  <TemplateContent>
    <ProjectCollection>
      <ProjectTemplateLink ProjectName="MyConsoleApplication">
      MyConsoleApplication\MyTemplate.vstemplate</ProjectTemplateLink>
      <ProjectTemplateLink ProjectName="MyConsoleApplication.Tests">
      MyConsoleApplication.Tests\MyTemplate.vstemplate</ProjectTemplateLink>
    </ProjectCollection>
  </TemplateContent>
</VSTemplate>
در اینجا به ازای هر پروژه، یک ProjectTemplateLink ایجاد خواهد شد که به فایل MyTemplate.vstemplate موجود در قالب آن اشاره می‌کند.
در ادامه این دو پوشه باز شده و فایل MyTemplate.vstemplate فوق را انتخاب کرده:


و همگی را تبدیل به یک فایل zip جدید کنید؛ مثلا به نام MyConsoleApplicationTemplates.zip.


تهیه فایل نصاب از قالب پروژه جدید

تا اینجا موفق شدیم، چندین قالب پروژه تهیه شده را به هم متصل کرده و تبدیل به یک فایل zip نهایی کنیم. مرحله بعد ایجاد فایلی است متنی به نام MyConsoleApplicationTemplates.vscontent با محتویات زیر:
<VSContent xmlns="http://schemas.microsoft.com/developer/vscontent/2005">
  <Content>
    <FileName>MyConsoleApplicationTemplates.zip</FileName>
    <DisplayName>MyConsoleApplication</DisplayName>
    <Description>A C# project that ...</Description>
    <FileContentType>VSTemplate</FileContentType>
    <ContentVersion>1.0</ContentVersion>
    <Attributes>
      <Attribute name="ProjectType" value="Visual C#" />
      <Attribute name="ProjectSubType" value="Web" />
      <Attribute name="TemplateType" value="Project" />
    </Attributes>
  </Content>
</VSContent>
در اینجا توسط قسمت Attributes مشخص می‌کنیم که قالب پروژه جدید باید در صفحه new project، در کدام مدخل قرار گیرد. بنابراین مطابق تنظیمات فوق، قالب جدید ذیل پروژه‌های وب سی‌شارپ قرار خواهد گرفت. مقدار FileName آن دقیقا معادل نام فایل zip ایی است که در مرحله قبل ایجاد کردیم.

مرحله بعد انتخاب دو فایل MyConsoleApplicationTemplates.vscontent و MyConsoleApplicationTemplates.zip و تبدیل ایندو به یک فایل zip جدید است. پس از ایجاد فایل جدید، پسوند آن‌را به VSI تغییر دهید؛ برای مثال نام آن‌را به MyConsoleApplicationTemplates.vsi تغییر دهید. اکنون این فایل نهایی با دوبار کلیک بر روی آن قابلیت اجرا و نصب خودکار را پیدا می‌کند.


پس از نصب، بلافاصله ذیل قسمت پروژه‌های وب قابل دسترسی و استفاده خواهد بود:



بنابراین به صورت خلاصه:
1) به ازای هر پروژه، یک فایل قالب zip معادل آن باید تهیه شود.
2) تمام این فایل‌های zip را گشوده و تبدیل به پوشه‌های متناظری کنید.
3) یک فایل MyTemplate.vstemplate را در پوشه ریشه مرحله 2 جهت تعریف ProjectTemplateLink‌ها اضافه کنید.
4) فایل جدید MyTemplate.vstemplate مرحله 3 و تمام پوشه‌های قالب‌های باز شده مرحله 2 را zip کنید.
5) سپس یک فایل vscontent نصاب را تهیه و آن‌را با فایل zip مرحله 4 مجددا zip کرده و پسوند آن‌را به VSI تغییر دهید.
اکنون می‌توان این فایل VSI را توزیع کرد.
اشتراک‌ها
پیاده سازی سرویس‌های gRPC در دات نت 7

gRPC Service with .NET 7
In this video we build a gRPC service with 5 methods: Create, Read (single), List (multiple), Update and Delete. We then employ JSON transcoding (a new feature added in .NET 7) to allow our gRPC service to act as a REST based API. This allows web-based endpoints to consume our service, while at the same time continuing to allow native gRPC clients to consume as well.


⏲️ Time Codes ⏲️

- 0:33 - Welcome
- 2:00 - gRPC Overview
- 5:08 - Scaffold the prject
- 9:58 - Test "greeter" service with Postman
- 12:32 - Add package dependencies
- 14:48 - Create the Model
- 16:38 - Create DB Context & migrations
- 22:37 - Define the protobuf file
- 32:39 - Build the first service method
- 40:55 - Test method with postman
- 42:52 - Read method
- 48:42 - List method
- 52:39 - Update method
- 59:14 - Delete method
- 1:03:24 - Add the annotation files
- 1:06:07 - Annotate the first gRPC method
- 1:09:26 - Annotate remaining methods
- 1:12:42 - Test with Postman
- 1:16;00 - Patreon supporter credits 

پیاده سازی سرویس‌های gRPC در دات نت 7
اشتراک‌ها
مستندات ASP.NET 5
    Getting Started
        Installing ASP.NET 5 On Windows
        Installing ASP.NET 5 On Mac OS X
        Installing ASP.NET 5 On Linux
        Choosing the Right .NET For You on the Server
    Tutorials
        Your First ASP.NET 5 Application Using Visual Studio
        Your First ASP.NET 5 Application on a Mac
        Publish to an Azure Web App using Visual Studio
    Conceptual Overview
        Introduction to ASP.NET 5
        Introducing .NET Core
        DNX Overview
        Understanding ASP.NET 5 Web Apps
    Fundamentals
        Working with Static Files
        Routing
        Configuration
        Dependency Injection
        Diagnostics
        Working with Multiple Environments
        OWIN
    .NET Execution Environment (DNX)
        DNX Overview
        Creating a Cross-Platform Console App with DNX
        Working with DNX Projects
        Using Commands
    Publishing and Deployment
        Publish to a Docker Image
    Client-Side Development
        Grunt and Gulp: Task Runners
        Manage Client-Side Packages with Bower
        Building Beautiful, Responsive Sites with Bootstrap
        Knockout.js MVVM Framework
        Styling Applications with Less, Sass, and Font Awesome
    Security
        Enabling authenication using external providers
        Account Confirmation and Password Recovery with ASP.NET Identity
        Two-factor authenication with SMS using ASP.NET Identity
        Data Protection
    Extensibility
        Writing Middleware
مستندات ASP.NET 5
مطالب
مراحل ارتقاء پروژه‌های Angular از نسخه‌ی 6.0 به 7.0
مراحل ارتقاء پروژه‌های Angular از نگارش 6 به 7 آن به شرح زیر هستند:
1- به روز رسانی Angular CLI
ابتدا نیاز است نگارش قبلی را حذف و سپس نگارش جدید را نصب کنید:
npm uninstall -g @angular/cli
npm cache verify
# if npm version is < 5 then use `npm cache clean` 
npm install -g @angular/cli@latest
البته Angular 7 پشتیبانی از  Node 10 را اضافه کرده است (بیشتر؛ دانلود Node). بنابراین پیش از اجرای دستورات فوق بهتر است NodeJS خود را نیز به روز کنید:
npm i -g npm

2- به روز رسانی RxJS (اگر در نگارش 6 آن‌را تکمیل نکرده‌اید)
1-تعویض کردن HttpModule با HttpClientModule و سرویس Http با سرویس HttpClient
2-حذف کردن ویژگی‌های منسوخ شده از RxJS 6 با اجرای دستور زیر:
npm install -g rxjs-tslint
rxjs-5-to-6-migrate -p src/tsconfig.app.json
3-حذف rxjs-compat بعد از بروزرسانی RxJS 6
 
3- به روز رسانی TypeScript
Angular 7 از تایپ اسکریپت 3.1 استفاده می‌کند (بیشتر). به همین جهت نیاز است وابستگی‌های سراسری سیستم خود را مانند TypeScript، پس از نصب CLI جدید، به نحو زیر به روز کنید:
npm update -g

4- به روز رسانی وابستگی‌های اصلی پروژه
برای به‌‌روز رسانی به نسخه 7 (core framework و CLI)، دستورات زیر را اجرا کنید:
ng update @angular/cli
ng update @angular/core
ng update rxjs
و اگر از Angular Material نیز استفاده می‌کنید، نیاز به اجرای دستور زیر را نیز خواهید داشت:
ng update @angular/material
اگر در اینجا خطای  peer dependency را مشاهده کردید، از سوئیچ force-- در انتهای دستورات، استفاده کنید.
و یا به صورت خلاصه دستور زیر تمام مراحل فوق را به صورت یکجا انجام می‌دهد:
 ng update --all --force
 
5- به روز رسانی Service worker 

اگر شما از Service worker  مربوط به Angular استفاده می‌کنید، بجای versionedFiles از files استفاده کنید. رفتار همان است و تغییر نکرده‌است.
 
6- به روز رسانی وابستگی‌های ثالث پروژه
برای به روز رسانی سایر وابستگی‌های پروژه‌، می‌توان از بسته‌ی npm-check-updates استفاده کرد:
npm install npm-check-updates -g
ncu -u
npm install
دستور دوم تمام شماره نگارش‌های بسته‌های موجود در فایل package.json را به صورت خودکار به آخرین نگارش آن‌ها روز رسانی می‌کند و دستور سوم این بسته‌های جدید را دریافت و نصب خواهد کرد.  
مطالب
فعال سازی قسمت آپلود تصویر و فایل Kendo UI Editor
یکی دیگر از ویجت‌های Kendo UI یک HTML Editor کامل است به همراه امکانات ارسال فایل، تصویر و ... پشتیبانی از راست به چپ. در ادامه قصد داریم نحوه‌ی مدیریت نمایش لیست فایل‌ها، افزودن و حذف آن‌ها را از طریق این ادیتور بررسی کنیم.


تنظیمات ابتدایی Kendo UI Editor

در ذیل کدهای سمت کاربر فعال سازی مقدماتی Kendo UI را مشاهده می‌کنید. در قسمت tools آن، لیست امکانات و نوار ابزار مهیای آن درج شده‌اند.
دو مورد insertImage و insertFile آن نیاز به تنظیمات سمت کاربر و سرور بیشتری دارند.
<!--نحوه‌ی راست به چپ سازی -->
<div class="k-rtl">
    <textarea id="editor" rows="10" cols="30" style="height: 440px"></textarea>
</div>
 
@section JavaScript
{
    <script type="text/javascript">
        $(function () {
            $("#editor").kendoEditor({
                tools: [
                    "bold", "italic", "underline", "strikethrough", "justifyLeft",
                    "justifyCenter", "justifyRight", "justifyFull", "insertUnorderedList",
                    "insertOrderedList", "indent", "outdent", "createLink", "unlink",
                    "insertImage", "insertFile",
                    "subscript", "superscript", "createTable", "addRowAbove", "addRowBelow",
                    "addColumnLeft", "addColumnRight", "deleteRow", "deleteColumn", "viewHtml",
                    "formatting", "cleanFormatting", "fontName", "fontSize", "foreColor",
                    "backColor", "print"
                ],
                imageBrowser: {
                    messages: {
                        dropFilesHere: "فایل‌های خود را به اینجا کشیده و رها کنید"
                    },
                    transport: {
                        read: {
                            url: "@Url.Action("GetFilesList", "KendoEditorImages")",
                            dataType: "json",
                            contentType: 'application/json; charset=utf-8',
                            type: 'GET',
                            cache: false
                        },
                        destroy: {
                            url: "@Url.Action("DestroyFile", "KendoEditorImages")",
                            type: "POST"
                        },
                        create: {
                            url: "@Url.Action("CreateFolder", "KendoEditorImages")",
                            type: "POST"
                        },
                        thumbnailUrl: "@Url.Action("GetThumbnail", "KendoEditorImages")",
                        uploadUrl: "@Url.Action("UploadFile", "KendoEditorImages")",
                        imageUrl: "@Url.Action("GetFile", "KendoEditorImages")?path={0}"
                    }
                },
                fileBrowser: {
                    messages: {
                        dropFilesHere: "فایل‌های خود را به اینجا کشیده و رها کنید"
                    },
                    transport: {
                        read: {
                            url: "@Url.Action("GetFilesList", "KendoEditorFiles")",
                            dataType: "json",
                            contentType: 'application/json; charset=utf-8',
                            type: 'GET',
                            cache: false
                        },
                        destroy: {
                            url: "@Url.Action("DestroyFile", "KendoEditorFiles")",
                            type: "POST"
                        },
                        create: {
                            url: "@Url.Action("CreateFolder", "KendoEditorFiles")",
                            type: "POST"
                        },
                        uploadUrl: "@Url.Action("UploadFile", "KendoEditorFiles")",
                        fileUrl: "@Url.Action("GetFile", "KendoEditorFiles")?path={0}"
                    }
                }
            });
        });
    </script>
}
در اینجا نحوه‌ی تنظیم مسیرهای مختلف ارسال فایل و تصویر Kendo UI Editor را ملاحظه می‌کنید.
منهای قسمت thumbnailUrl، عملکرد قسمت‌های مختلف افزودن فایل و تصویر این ادیتور یکسان هستند. به همین جهت می‌توان برای مثال کنترلی مانند KendoEditorFilesController را ایجاد و سپس در کنترلر KendoEditorImagesController از آن ارث بری کرد و متد دریافت و نمایش بند انگشتی تصاویر را افزود. به این ترتیب دیگر نیازی به تکرار کدهای مشترک بین این دو قسمت نخواهد بود.


نمایش لیست پوشه‌ها و تصویر در ابتدای باز شدن صفحه‌ی درج تصویر

با کلیک بر روی دکمه‌ی نمایش لیست تصاویر، صفحه دیالوگی مانند شکل زیر ظاهر خواهد شد:


تنظیمات خواندن این فایل‌ها، از قسمت read مربوط به imageBrowser دریافت می‌شود که cache آن نیز به false تنظیم شده‌است تا در این بین مرورگر اطلاعات را کش نکند. این مورد در حین حذف فایل‌ها و پوشه‌ها مهم است. زیرا اگر cache:false تنظیم نشده باشد، حذف یک فایل یا پوشه در سمت کاربر تاثیری نخواهد داشت.
imageBrowser: {
    transport: {
        read: {
            url: "@Url.Action("GetFilesList", "KendoEditorImages")",
            dataType: "json",
            contentType: 'application/json; charset=utf-8',
            type: 'GET',
            cache: false
        }
    }
},
در ادامه نیاز است اکشن متد GetFilesList را به نحو ذیل در سمت سرور تهیه کرد:
namespace KendoUI13.Controllers
{
    public class KendoEditorFilesController : Controller
    {
        //مسیر پوشه فایل‌ها
        protected string FilesFolder = "~/files";
 
        protected string KendoFileType = "f";
        protected string KendoDirType = "d";
 
        [HttpGet]
        public ActionResult GetFilesList(string path)
        {
            path = GetSafeDirPath(path);
            var imagesList = new DirectoryInfo(path)
                                .GetFiles()
                                .Select(fileInfo => new KendoFile
                                {
                                    Name = fileInfo.Name,
                                    Size = fileInfo.Length,
                                    Type = KendoFileType
                                }).ToList();
 
            var foldersList = new DirectoryInfo(path)
                                .GetDirectories()
                                .Select(directoryInfo => new KendoFile
                                {
                                    Name = directoryInfo.Name,
                                    Type = KendoDirType
                                }).ToList();
 
            return new ContentResult
            {
                Content = JsonConvert.SerializeObject(imagesList.Union(foldersList), new JsonSerializerSettings
                {
                    ContractResolver = new CamelCasePropertyNamesContractResolver()
                }),
                ContentType = "application/json",
                ContentEncoding = Encoding.UTF8
            };
        }
 
 
        protected string GetSafeDirPath(string path)
        {
            // path = مسیر زیر پوشه‌ی وارد شده
            if (string.IsNullOrWhiteSpace(path))
            {
                return Server.MapPath(FilesFolder);
            }
 
            //تمیز سازی امنیتی
            path = Path.GetDirectoryName(path);
            path = Path.Combine(Server.MapPath(FilesFolder), path);
            return path;
        } 
    }
}
در اینجا کدهای کلاس پایه KendoEditorFilesController را مشاهده می‌کنید. به این جهت فیلد FilesFolder آن protected تعریف شده‌است تا در کلاسی که از آن ارث بری می‌کند نیز قابل دسترسی باشد. سپس لیست فایل‌ها و پوشه‌های path دریافتی با فرمت لیستی از KendoFile تهیه شده و با فرمت JSON بازگشت داده می‌شوند. ساختار KendoFile را در ذیل مشاهده می‌کنید:
namespace KendoUI13.Models
{
    public class KendoFile
    {
        public string Name { set; get; }
        public string Type { set; get; }
        public long Size { set; get; }
    }
}
- در اینجا Type می‌تواند از نوع فایل با مقدار f و یا از نوع پوشه با مقدار d باشد.
- علت استفاده از CamelCasePropertyNamesContractResolver در حین بازگشت JSON نهایی، تبدیل خواص دات نتی، به نام‌های سازگار با JavaScript است. برای مثال به صورت خودکار Name را تبدیل به name می‌کند.
- پارامتر path در ابتدای کار خالی است. اما کاربر می‌تواند در بین پوشه‌های باز شده‌ی توسط مرورگر تصاویر Kendo UI حرکت کند. به همین جهت مقدار آن باید هربار بررسی شده و بر این اساس لیست فایل‌ها و پوشه‌های جاری بازگشت داده شوند.


مدیریت حذف تصاویر و پوشه‌ها

همانطور که در شکل فوق نیز مشخص است، با انتخاب یک پوشه یا فایل، دکمه‌ای با آیکن ضربدر جهت فراهم آوردن امکان حذف، ظاهر می‌شود. این دکمه متصل است به قسمت destroy تنظیمات ادیتور:
imageBrowser: {
    transport: {
        destroy: {
            url: "@Url.Action("DestroyFile", "KendoEditorImages")",
            type: "POST"
        }
    }
},
این تنظیمات سمت کاربر را باید به نحو ذیل در سمت سرور مدیریت کرد:
namespace KendoUI13.Controllers
{
    public class KendoEditorFilesController : Controller
    {
        //مسیر پوشه فایل‌ها
        protected string FilesFolder = "~/files";
 
        protected string KendoFileType = "f";
        protected string KendoDirType = "d";
 
        [HttpPost]
        public ActionResult DestroyFile(string name, string path)
        {
            //تمیز سازی امنیتی
            name = Path.GetFileName(name);
            path = GetSafeDirPath(path);
 
            var pathToDelete = Path.Combine(path, name);
 
            var attr = System.IO.File.GetAttributes(pathToDelete);
            if ((attr & FileAttributes.Directory) == FileAttributes.Directory)
            {
                Directory.Delete(pathToDelete, recursive: true);
            }
            else
            {
                System.IO.File.Delete(pathToDelete);
            }
 
            return Json(new object[0]);
        } 
    }
}
- استفاده از Path.GetFileName جهت دریافت نام فایل‌ها در اینجا بسیار مهم است. زیرا اگر این تمیز سازی امنیتی صورت نگیرد، ممکن است با کمی تغییر در آن، فایل web.config برنامه، دریافت یا حذف شود.
- پارامتر name دریافتی مساوی است با نام فایل انتخاب شده و path مشخص می‌کند که در کدام پوشه قرار داریم.
- چون در اینجا امکان حذف یک پوشه یا فایل وجود دارد، حتما نیاز است بررسی کنیم، مسیر دریافتی پوشه‌است یا فایل و سپس بر این اساس جهت حذف آن‌ها اقدام صورت گیرد.


مدیریت ایجاد یک پوشه‌ی جدید

تنظیمات قسمت create مرورگر تصاویر، مرتبط است به زمانیکه کاربر با کلیک بر روی دکمه‌ی +، درخواست ایجاد یک پوشه‌ی جدید را کرده‌است:
imageBrowser: {
    transport: {
        create: {
            url: "@Url.Action("CreateFolder", "KendoEditorImages")",
            type: "POST"
        }
    }
},
کدهای اکشن متد متناظر با این عمل را در ذیل مشاهده می‌کنید:
namespace KendoUI13.Controllers
{
    public class KendoEditorFilesController : Controller
    {
        //مسیر پوشه فایل‌ها
        protected string FilesFolder = "~/files";
 
        protected string KendoFileType = "f";
        protected string KendoDirType = "d";
 
        [HttpPost]
        public ActionResult CreateFolder(string name, string path)
        {
            //تمیز سازی امنیتی
            name = Path.GetFileName(name);
            path = GetSafeDirPath(path);
            var dirToCreate = Path.Combine(path, name);
 
            Directory.CreateDirectory(dirToCreate);
 
            return KendoFile(new KendoFile
            {
                Name = name,
                Type = KendoDirType
            });
        }
 
        protected ActionResult KendoFile(KendoFile file)
        {
            return new ContentResult
            {
                Content = JsonConvert.SerializeObject(file,
                    new JsonSerializerSettings
                    {
                        ContractResolver = new CamelCasePropertyNamesContractResolver()
                    }),
                ContentType = "application/json",
                ContentEncoding = Encoding.UTF8
            };
        }
    }
}
- در اینجا نیز name مساوی نام پوشه‌ی درخواستی است و path به مسیر تو در توی پوشه‌ی جاری اشاره می‌کند.
- پس از ایجاد پوشه، باید نام آن‌را با فرمت KendoFile به صورت JSON بازگشت داد. همچنین در اینجا Type را نیز باید به d (پوشه) تنظیم کرد.


مدیریت قسمت ارسال فایل و تصویر

زمانیکه کاربر بر روی دکمه‌ی upload file یا بارگذاری تصاویر در اینجا کلیک می‌کند، اطلاعات فایل آپلودی به مسیر uploadUrl ارسال می‌گردد.
imageBrowser: {
    transport: {
        thumbnailUrl: "@Url.Action("GetThumbnail", "KendoEditorImages")",
        uploadUrl: "@Url.Action("UploadFile", "KendoEditorImages")",
        imageUrl: "@Url.Action("GetFile", "KendoEditorImages")?path={0}"
    }
},
دو تنظیم دیگر thumbnailUrl و imageUrl، برای نمایش بند انگشتی و نمایش کامل تصویر کاربرد دارند.
در ادامه کدهای مدیریت سمت سرور قسمت آپلود این ادیتور را مشاهده می‌کنید:
namespace KendoUI13.Controllers
{
    public class KendoEditorFilesController : Controller
    {
        //مسیر پوشه فایل‌ها
        protected string FilesFolder = "~/files";
 
        protected string KendoFileType = "f";
        protected string KendoDirType = "d";

 
        [HttpPost]
        public ActionResult UploadFile(HttpPostedFileBase file, string path)
        {
            //تمیز سازی امنیتی
            var name = Path.GetFileName(file.FileName);
            path = GetSafeDirPath(path);
            var pathToSave = Path.Combine(path, name);
 
            file.SaveAs(pathToSave);
 
            return KendoFile(new KendoFile
            {
                Name = name,
                Size = file.ContentLength,
                Type = KendoFileType
            });
        } 
    }
}
- در اینجا path مشخص می‌کند که در کدام پوشه‌ی تو در تو قرار داریم و file نیز حاوی محتوای ارسالی به سرور است.
- پس از ذخیره سازی اطلاعات فایل، نیاز است اطلاعات فایل نهایی را با فرمت KendoFile به صورت JSON بازگشت دهیم.


ارث بری از KendoEditorFilesController جهت تکمیل قسمت مدیریت تصاویر

تا اینجا کدهایی را که ملاحظه کردید، برای هر دو قسمت ارسال تصویر و فایل کاربرد دارند. قسمت ارسال تصاویر برای تکمیل نیاز به متد دریافت تصاویر به صورت بند انگشتی نیز دارد که به صورت ذیل قابل تعریف است و چون از کلاس پایه KendoEditorFilesController ارث بری کرده‌است، این کنترلر به صورت خودکار حاوی اکشن متدهای کلاس پایه نیز خواهد بود.
using System.Web.Mvc;
 
namespace KendoUI13.Controllers
{
    public class KendoEditorImagesController : KendoEditorFilesController
    {
        public KendoEditorImagesController()
        {
            // بازنویسی مسیر پوشه‌ی فایل‌ها
            FilesFolder = "~/images";
        }
 
        [HttpGet]
        [OutputCache(Duration = 3600, VaryByParam = "path")]
        public ActionResult GetThumbnail(string path)
        {
            //todo: create thumb/ resize image
 
            path = GetSafeFileAndDirPath(path);
            return File(path, "image/png");
        }
    }
}

کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید.
مطالب
یافتن اکشن متد‌های Post ایی در ASP.NET MVC که فیلتر CSRF ندارند
روش مرسوم مقابله با حملات CSRF در ASP.NET MVC، استفاده از فیلتر امنیتی ValidateAntiForgeryToken بر روی اکشن متدهایی از نوع Post است و سپس فراخوانی Html.AntiForgeryToken در View متناظر.
با بالا رفتن تعداد اکشن متدهای یک پروژه، ممکن است استفاده از ValidateAntiForgeryToken فراموش شود. در ادامه مثالی را ملاحظه می‌کنید که یک پروژه‌ی ASP.NET MVC را جهت یافتن اکشن متدهای Post ایی که فیلتر ValidateAntiForgeryToken ندارند، اسکن می‌کند:

using System;
using System.Linq;
using System.Reflection;
// Add a ref. to \Program Files\Microsoft ASP.NET\ASP.NET MVC 4\Assemblies\System.Web.Mvc.dll
using System.Web.Mvc;
// Add a ref. to System.Web
using System.Web.UI;

namespace FindOutputCaches
{
    class Program
    {
        static void Main(string[] args)
        {
            var path = @"D:\path\bin\site.dll";
            var asmTarget = Assembly.LoadFrom(path);

            checkCsrfTokens(asmTarget);

            Console.WriteLine("Press a key...");
            Console.Read();
        }


        private static void checkCsrfTokens(Assembly asmTarget)
        {
            // یافتن کلیه کنترلرها
            var controllers = asmTarget.GetTypes()
                                       .Where(type => typeof(IController).IsAssignableFrom(type) &&
                                                     !type.Name.StartsWith("T4MVC"))
                                       .ToList();

            foreach (var controller in controllers)
            {
                // یافتن کلیه اکشن متدهای کنترلر جاری
                var actionMethods = controller.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
                                              .Where(method => typeof(ActionResult).IsAssignableFrom(method.ReturnType))
                                              .ToList();

                foreach (var method in actionMethods)
                {                    
                    var httpPostAttributes = method.GetCustomAttributes(typeof(HttpPostAttribute), true);
                    if (httpPostAttributes == null || !httpPostAttributes.Any())
                        continue;

                    var csrfTokens = method.GetCustomAttributes(typeof(ValidateAntiForgeryTokenAttribute), true);
                    if (csrfTokens == null || !csrfTokens.Any())
                    {
                        Console.WriteLine("Detected [HttpPost] without [ValidateAntiForgeryToken] in:\n {0}-->{1}",
                                               controller.FullName, method.Name);
                    }
                }
            }
        }
    }
}
ابتدا مسیر اسمبلی کامپایل شده پروژه ASP.NET MVC که حاوی کنترلرهای برنامه است، باید مشخص گردد.
سپس در این اسمبلی، کلیه نوع‌های تعریف شده، یافت گردیده و آن‌هایی که پیاده سازی کننده IController هستند (یعنی کلاس‌های کنترلر واقعی برنامه)، جدا خواهند شد.
در ادامه در این کنترلرها، متدهایی را بررسی خواهیم کرد که دارای خروجی از نوع ActionResult باشند (فقط اکشن متدها مدنظر هستند). اگر این اکشن متد یافت شده دارای ویژگی HttpPost بود و همچنین ValidateAntiForgeryToken نداشت، یعنی یک مشکل امنیتی که باید برطرف شود.
مطالب
امکان اعتبارسنجی با تاخیر در ASP.NET 4.5
در نگارش‌های قبلی ASP.NET Web forms اگر نیاز به ارسال محتوای HTML ایی وجود داشت، می‌بایستی کل سیستم اعتبارسنجی حداقل یک صفحه را غیرفعال کرد. برای مثال:
 <%@ Page Language="C#" ValidateRequest="false" %>
 این نقیصه‌ی همه یا هیچ، در ASP.NET MVC وجود ندارد و می‌توان به ازای یک خاصیت خاص، اعتبارسنجی پیش فرض را با اعمال ویژگی AllowHtml موقتا غیرفعال کرد؛ اما مابقی فیلدها و خاصیت‌های فرم همچنان تحت نظر سیستم اعتبارسنجی‌های ورودی ASP.NET قرار خواهند داشت و به این ترتیب امکان ورود اطلاعات خطرناک، خصوصا از لحاظ مباحث XSS، حداقل در آن فیلدها وجود نخواهد داشت.
در ASP.NET 4.5 مفهوم جدیدی به نام Deferred validation معرفی شده‌است تا رفتار Web forms را نیز در برابر ورودی‌های ارسال شده همانند ASP.NET MVC کند. به این معنا که اگر جهت دسترسی به مقدار کوئری استرینگ lastName همانند قبل عمل کنید:
 Request["lastName"]
و کاربر ورودی خطرناکی را وارد کرده باشد، همچنان استثنای A potentially dangerous request.form value was detected صادر می‌شود.
اما اگر در ASP.NET 4.5، کوئری استرینگ را به نحو ذیل دریافت کنید:
 Request.Unvalidated.QueryString["lastName"]
به صورت خودکار سیستم اعتبارسنجی غیرفعال شده و امکان دسترسی به محتوای اصلی کوئری استرینگ lastName را خواهید داشت. به این ترتیب دیگر نیازی نخواهید داشت تا اعتبارسنجی کل صفحه را برای دسترسی به یک مقدار خاص غیرفعال نمائید.
برای دسترسی به مقادیر اعتبارسنجی نشده فیلدهای یک فرم ارسالی نیز می‌توان به صورت ذیل عمل کرد:
 Request.Unvalidated.Form[txtData1.UniqueID]
کلاس Request.Unvalidated شامل خاصیت‌های Cookies، Files، Headers و امثال آن نیز می‌باشد.
در اینجا اگر دقت کرده باشید از UniqueID بجای Id استفاده شده‌است؛ از این جهت که مجموعه Form، حاوی Idهای واقعی دریافت شده از کاربر مانند ctl00$MainContent$MessageText می‌باشد.

روش دوم، استفاده از خاصیت جدید ValidateRequestMode یک کنترل سمت سرور است و در اینجا نیز می‌توان صرفا اعتبارسنجی یک کنترل را بجای کل صفحه، غیرفعال کرد:
 <asp:TextBox ID="txtASPNet" ValidateRequestMode="Disabled" runat="server" />
تنظیم خاصیت ذکر شده برای کنترل‌های سمت سرور ضروری است. از این جهت که در حین تشکیل ViewState از محتوای این کنترل‌ها نیز استفاده می‌شود. اینجا است که اگر ValidateRequestMode غیرفعال نشده باشد، باز همان خطای ورودی خطرناک را دریافت خواهید کرد.

البته برای فعال سازی اعتبارسنجی با تاخیر نیاز است اصلاح ذیل را نیز به web.config برنامه اعمال کنید (مقدار پیش فرض آن 4 است):
 <httpRuntime targetFramework="4.5" requestValidationMode="4.5" />