آموزش TypeScript #1
تکمیل فرم ثبت نام کاربران
در ادامه کدهای کامل کامپوننت فرم ثبت نام کاربران را مشاهده میکنید:
@page "/registration" @inject IClientAuthenticationService AuthenticationService @inject NavigationManager NavigationManager <EditForm Model="UserForRegistration" OnValidSubmit="RegisterUser" class="pt-4"> <DataAnnotationsValidator /> <div class="py-4"> <div class=" row form-group "> <div class="col-6 offset-3 "> <div class="card border"> <div class="card-body px-lg-5 pt-4"> <h3 class="col-12 text-success text-center py-2"> <strong>Sign Up</strong> </h3> @if (ShowRegistrationErrors) { <div> @foreach (var error in Errors) { <p class="text-danger text-center">@error</p> } </div> } <hr style="background-color:aliceblue" /> <div class="py-2"> <InputText @bind-Value="UserForRegistration.Name" class="form-control" placeholder="Name..." /> <ValidationMessage For="(()=>UserForRegistration.Name)" /> </div> <div class="py-2"> <InputText @bind-Value="UserForRegistration.Email" class="form-control" placeholder="Email..." /> <ValidationMessage For="(()=>UserForRegistration.Email)" /> </div> <div class="py-2 input-group"> <div class="input-group-prepend"> <span class="input-group-text"> +1</span> </div> <InputText @bind-Value="UserForRegistration.PhoneNo" class="form-control" placeholder="Phone number..." /> <ValidationMessage For="(()=>UserForRegistration.PhoneNo)" /> </div> <div class="form-row py-2"> <div class="col"> <InputText @bind-Value="UserForRegistration.Password" type="password" id="password" placeholder="Password..." class="form-control" /> <ValidationMessage For="(()=>UserForRegistration.Password)" /> </div> <div class="col"> <InputText @bind-Value="UserForRegistration.ConfirmPassword" type="password" id="confirm" class="form-control" placeholder="Confirm Password..." /> <ValidationMessage For="(()=>UserForRegistration.ConfirmPassword)" /> </div> </div> <hr style="background-color:aliceblue" /> <div class="py-2"> @if (IsProcessing) { <button type="submit" class="btn btn-success btn-block disabled"><i class="fas fa-sign-in-alt"></i> Please Wait...</button> } else { <button type="submit" class="btn btn-success btn-block"><i class="fas fa-sign-in-alt"></i> Register</button> } </div> </div> </div> </div> </div> </div> </EditForm> @code{ UserRequestDTO UserForRegistration = new UserRequestDTO(); bool IsProcessing; bool ShowRegistrationErrors; IEnumerable<string> Errors; private async Task RegisterUser() { ShowRegistrationErrors = false; IsProcessing = true; var result = await AuthenticationService.RegisterUserAsync(UserForRegistration); if (result.IsRegistrationSuccessful) { IsProcessing = false; NavigationManager.NavigateTo("/login"); } else { IsProcessing = false; Errors = result.Errors; ShowRegistrationErrors = true; } } }
- مدل این فرم بر اساس UserRequestDTO تشکیل شدهاست که همان شیءای است که اکشن متد ثبت نام سمت Web API انتظار دارد.
- در این کامپوننت به کمک سرویس IClientAuthenticationService که آنرا در قسمت قبل تهیه کردیم، شیء نهایی متصل به فرم، به سمت Web API Endpoint ثبت نام ارسال میشود.
- در اینجا روشی را جهت غیرفعال کردن یک دکمه، پس از کلیک بر روی آن مشاهده میکنید. میتوان پس از کلیک بر روی دکمهی ثبت نام، با true کردن یک فیلد مانند IsProcessing، بلافاصله دکمهی جاری را برای مثال با ویژگی disabled در صفحه درج کرد و یا حتی آنرا از صفحه حذف کرد. این روش، یکی از روشهای جلوگیری از کلیک چندبارهی کاربر، بر روی یک دکمهاست.
- فرم جاری، خطاهای اعتبارسنجی مخصوص Identity سمت سرور را نیز نمایش میدهد که حاصل از ارسال آنها توسط اکشن متد ثبت نام است:
- پس از پایان موفقیت آمیز ثبت نام، کاربر را به سمت فرم لاگین هدایت میکنیم.
تکمیل فرم ورود به سیستم کاربران
در ادامه کدهای کامل کامپوننت فرم ثبت نام کاربران را مشاهده میکنید:
@page "/login" @inject IClientAuthenticationService AuthenticationService @inject NavigationManager NavigationManager <div id="logreg-forms"> <h1 class="h3 mb-3 pt-3 font-weight-normal text-primary" style="text-align:center;">Sign In</h1> <EditForm Model="UserForAuthentication" OnValidSubmit="LoginUser"> <DataAnnotationsValidator /> @if (ShowAuthenticationErrors) { <p class="text-center text-danger">@Errors</p> } <InputText @bind-Value="UserForAuthentication.UserName" id="email" placeholder="Email..." class="form-control mb-2" /> <ValidationMessage For="(()=>UserForAuthentication.UserName)"></ValidationMessage> <InputText @bind-Value="UserForAuthentication.Password" type="password" placeholder="Password..." id="password" class="form-control mb-2" /> <ValidationMessage For="(()=>UserForAuthentication.Password)"></ValidationMessage> @if (IsProcessing) { <button type="submit" class="btn btn-success btn-block disabled"><i class="fas fa-sign-in-alt"></i> Please Wait...</button> } else { <button type="submit" class="btn btn-success btn-block"><i class="fas fa-sign-in-alt"></i> Sign in</button> } <a href="/registration" class="btn btn-primary text-white mt-3"><i class="fas fa-user-plus"></i> Register as a new user</a> </EditForm> </div> @code { AuthenticationDTO UserForAuthentication = new AuthenticationDTO(); bool IsProcessing = false; bool ShowAuthenticationErrors; string Errors; string ReturnUrl; private async Task LoginUser() { ShowAuthenticationErrors = false; IsProcessing = true; var result = await AuthenticationService.LoginAsync(UserForAuthentication); if (result.IsAuthSuccessful) { IsProcessing = false; var absoluteUri = new Uri(NavigationManager.Uri); var queryParam = HttpUtility.ParseQueryString(absoluteUri.Query); ReturnUrl = queryParam["returnUrl"]; if (string.IsNullOrEmpty(ReturnUrl)) { NavigationManager.NavigateTo("/"); } else { NavigationManager.NavigateTo("/" + ReturnUrl); } } else { IsProcessing = false; Errors = result.ErrorMessage; ShowAuthenticationErrors = true; } } }
- مدل این فرم بر اساس AuthenticationDTO تشکیل شدهاست که همان شیءای است که اکشن متد لاگین سمت Web API انتظار دارد.
- در این کامپوننت به کمک سرویس IClientAuthenticationService که آنرا در قسمت قبل تهیه کردیم، شیء نهایی متصل به فرم، به سمت Web API Endpoint ثبت نام ارسال میشود.
- در اینجا نیز همانند فرم ثبت نام، پس از کلیک بر روی دکمهی ورود به سیستم، با true کردن یک فیلد مانند IsProcessing، بلافاصله دکمهی جاری را با ویژگی disabled در صفحه درج کردهایم تا از کلیک چندبارهی کاربر، جلوگیری شود.
- این فرم، خطاهای اعتبارسنجی مخصوص Identity سمت سرور را نیز نمایش میدهد که حاصل از ارسال آنها توسط اکشن متد لاگین است:
همانطور که مشاهده میکنید، مقدار JWT تولید شدهی پس از لاگین و همچنین مشخصات کاربر دریافتی از Web Api، جهت استفادههای بعدی، در Local Storage مرورگر درج شدهاند.
تغییر منوی راهبری سایت، بر اساس وضعیت لاگین شخص
تا اینجا قسمتهای ثبت نام و ورود به سیستم را تکمیل کردیم. در ادامه نیاز داریم تا منوی سایت را هم بر اساس وضعیت اعتبارسنجی شخص، تغییر دهیم. برای مثال اگر شخصی به سیستم وارد شدهاست، باید در منوی سایت، لینک خروج و نام خودش را مشاهده کند و نه مجددا لینکهای ثبتنام و لاگین را. جهت تغییر منوی راهبری سایت، کامپوننت Shared\NavMenu.razor را گشوده و لینکهای قبلی ثبتنام و لاگین را با محتوای زیر جایگزین میکنیم:
<AuthorizeView> <Authorized> <li class="nav-item p-0"> <NavLink class="nav-link" href="#"> <span class="p-2"> Hello, @context.User.Identity.Name! </span> </NavLink> </li> <li class="nav-item p-0"> <NavLink class="nav-link" href="logout"> <span class="p-2"> Logout </span> </NavLink> </li> </Authorized> <NotAuthorized> <li class="nav-item p-0"> <NavLink class="nav-link" href="registration"> <span class="p-2"> Register </span> </NavLink> </li> <li class="nav-item p-0"> <NavLink class="nav-link" href="login"> <span class="p-2"> Login </span> </NavLink> </li> </NotAuthorized> </AuthorizeView>
تکمیل قسمت خروج از سیستم
اکنون که لینک logout را در منوی سایت، پس از ورود به سیستم نمایش میدهیم، میتوان کدهای کامپوننت آنرا (Pages\Authentication\Logout.razor) به صورت زیر تکمیل کرد:
@page "/logout" @inject IClientAuthenticationService AuthenticationService @inject NavigationManager NavigationManager @code { protected async override Task OnInitializedAsync() { await AuthenticationService.LogoutAsync(); NavigationManager.NavigateTo("/"); } }
مشکل! پس از کلیک بر روی logout، هرچند میتوان مشاهده کرد که اطلاعات Local Storage به درستی حذف شدهاند، اما ... پس از هدایت به صفحهی اصلی برنامه، هنوز هم لینک logout و نام کاربری شخص نمایان هستند و به نظر هیچ اتفاقی رخ ندادهاست!
علت اینجا است که AuthenticationStateProvider سفارشی را که تهیه کردیم، فاقد اطلاع رسانی تغییر وضعیت است:
namespace BlazorWasm.Client.Services { public class AuthStateProvider : AuthenticationStateProvider { // ... public void NotifyUserLoggedIn(string token) { var authenticatedUser = new ClaimsPrincipal( new ClaimsIdentity(JwtParser.ParseClaimsFromJwt(token), "jwtAuthType") ); var authState = Task.FromResult(new AuthenticationState(authenticatedUser)); base.NotifyAuthenticationStateChanged(authState); } public void NotifyUserLogout() { var authState = Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()))); base.NotifyAuthenticationStateChanged(authState); } } }
namespace BlazorWasm.Client.Services { public class ClientAuthenticationService : IClientAuthenticationService { private readonly HttpClient _client; private readonly ILocalStorageService _localStorage; private readonly AuthenticationStateProvider _authStateProvider; public ClientAuthenticationService( HttpClient client, ILocalStorageService localStorage, AuthenticationStateProvider authStateProvider) { _client = client; _localStorage = localStorage; _authStateProvider = authStateProvider; } public async Task<AuthenticationResponseDTO> LoginAsync(AuthenticationDTO userFromAuthentication) { // ... if (response.IsSuccessStatusCode) { //... ((AuthStateProvider)_authStateProvider).NotifyUserLoggedIn(result.Token); return new AuthenticationResponseDTO { IsAuthSuccessful = true }; } //... } public async Task LogoutAsync() { //... ((AuthStateProvider)_authStateProvider).NotifyUserLogout(); } } }
- ابتدا AuthenticationStateProvider به سازندهی کلاس تزریق شدهاست.
- سپس در حین لاگین موفق، متد NotifyUserLoggedIn آن فراخوانی شدهاست.
- در آخر پس از خروج از سیستم، متد NotifyUserLogout فراخوانی شدهاست.
پس از این تغییرات اگر بر روی لینک logout کلیک کنیم، این گزینه به درستی عمل کرده و اینبار شاهد نمایش مجدد لینکهای لاگین و ثبت نام خواهیم بود.
محدود کردن دسترسی به صفحات برنامه بر اساس نقشهای کاربران
پس از ورود کاربر به سیستم و تامین AuthenticationState، اکنون میخواهیم تنها این نوع کاربران اعتبارسنجی شده بتوانند جزئیات اتاقها (برای شروع رزرو) و یا صفحهی نمایش نتیجهی پرداخت را مشاهده کنند. البته نمیخواهیم صفحهی نمایش لیست اتاقها را محدود کنیم. برای این منظور ویژگی Authorize را به ابتدای تعاریف کامپوننتهای PaymentResult.razor و RoomDetails.razor، اضافه میکنیم:
@attribute [Authorize(Roles = ConstantRoles.Customer)]
@using Microsoft.AspNetCore.Authorization
تکمیل مشخصات هویتی شخصی که قرار است اتاقی را رزرو کند
پیشتر در فرم RoomDetails.razor، اطلاعات ابتدایی کاربر را مانند نام او، دریافت میکردیم. اکنون با توجه به محدود شدن این کامپوننت به کاربران لاگین کرده، میتوان اطلاعات کاربر وارد شدهی به سیستم را نیز به صورت خودکار بارگذاری و تکمیل کرد:
@page "/hotel-room-details/{Id:int}" // ... @code { // ... protected override async Task OnInitializedAsync() { try { HotelBooking.OrderDetails = new RoomOrderDetailsDTO(); if (Id != null) { // ... if (await LocalStorage.GetItemAsync<UserDTO>(ConstantKeys.LocalUserDetails) != null) { var userInfo = await LocalStorage.GetItemAsync<UserDTO>(ConstantKeys.LocalUserDetails); HotelBooking.OrderDetails.UserId = userInfo.Id; HotelBooking.OrderDetails.Name = userInfo.Name; HotelBooking.OrderDetails.Email = userInfo.Email; HotelBooking.OrderDetails.Phone = userInfo.PhoneNo; } } } catch (Exception e) { await JsRuntime.ToastrError(e.Message); } }
public async Task<RoomOrderDetailsDTO> SaveRoomOrderDetailsAsync(RoomOrderDetailsDTO details) { // details.UserId = "unknown user!";
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: Blazor-5x-Part-32.zip
- موقعی که کلاینت درخواست خود را مبنی بر یکی از منابع سرور ارسال میکند، Http.sys این درخواست را میگیرد.
- http.sys با WAS تماس گرفته و درخواست میکند تا اطلاعات پیکربندی یا تنظیمات IIS را برای نحوهی برخورد با درخواست، برایش بفرستد.
- WAS هم اطلاعات پیکربندی شده را از محل ذخیره دادهها که applicationHost.config هست، میخواند.
- WWW Service که یک آداپتور برای Http.sys هست، اطلاعات را از WAS دریافت میکند. این اطلاعات شامل پیکربندی application pool و سایت میباشد.
- WWW Service اطلاعات را برای Http.sys میفرستد.
- WAS یک پروسه کارگر را در application pool ایجاد میکند تا درخواست رسیده مورد پردازش قرار بگیرد.
- پروسههای کارگر درخواست را پردازش کرده و خروجی یا response مورد نظر را تولید میکنند.
- Http.sys نتیجه را دریافت و برای کلاینت میفرستد.
حال بیایید ببینیم موقعی که درخواست وارد پروسهی کارگر میشود چه اتفاقی میافتد؟
در پروسههای کارگر، یک درخواست از مراحل لیست شده ای به ترتیب عبور میکند. در هسته وب سرور، رویدادهایی را فراخوانی میکند که در هر رویداد چندین ماژول native برای کارهایی چون authentication یا events logs دارد و در صورتیکه درخواستی نیاز به یک ماژول مدیریت شده CLR داشته باشد، از ماژول native managedEngine کمک گرفته و یک app domain را ایجاد میکند تا ماژولهای مدیریت شده، عملیات لازم خودشان را انجام دهند. مثل authentication form و ...
موقعی هم که درخواست، از تمامی این رویدادها عبور کند، response برای http.sys ارسال میشود تا به کلاینت بازگشت داده شود. شکل زیر نحوه ورود یک درخواست به پروسه کارگر را نشان میدهد.
از نسخه 7 به بعد، IIS از یک معماری ماژولار استفاده میکند و این ویژگی، سه فایده دارد:
- Componentization یا کامپوننت سازی
- Extensibility یا توسعه پذیری یا قابل گشترش
- ASP.NET Integration
Componentization
- باعث کاهش attack surface میشود که در نتیجه امنیت سیستم را بالا میبرد. با ویژگی حذف کامپوننتها شما میتوانید ویژگیهای غیرقابل استفاده IIS را حذف کنید تا وروردیهای سیستم کاهش یابد. پس با کاهش ویژگیهایی که از آن هرگز استفاده نخواهید کرد، مدخل ورود هکر را از بین برده تا امنیت سرور بالاتر برود.
- افزایش کارآیی و کاهش مصرف حافظه. با حذف ویژگیهایی که هرگز استفاده نمیکنید، در مصرف حافظه و بهینه استفاده شدن منابع سرور صرفه جویی کنید.
- با وجود ویژگی افزودن و جایگزینی کامپوننتها، ناخودآگاه ذهن ما به سمت کاستوم سازی یا خصوصی سازی کشیده میشود. با این کار شما به راحتی یک custom server ایجاد میکنید که این سرور بر اساس علایق شما کارش را انجام میدهد و به راحتی امکاناتی چون افزودن third partyها را به توسعه دهنده میدهد.
Extensibility
- قدرت بخشی به برنامههای وب. امکانات و قدرتی که میتواند در این حالت به برنامههای در حال اجرا داد به مراتب بیشتر از استفاده از لایههای داخلی خود برنامه هست. برای اینکار شما میتوانید کدهای خود را با ASP.Net نوشته یا از کدهای native چون ++C استفاده کنید.
- تجربهای از توسعه پذیری سادهتر و راحت تر
- استفاده از قدرت و تمامی امکانات را به شما میدهد و میتوانید تمام دستورات را برای همه منابع حتی فایلهای ایستا، CGI ، ASP و دیگر منابع اجرا کنید.
ASP.NET Integration
کتابخانه GMap.Net
سال گذشته بود که به بررسی کتابخانههای موجود برای دات نت که به ساخت نقشههای گوگل (+ ) میپردازند پرداختم. ولی مشکلی که وجود داشت، همه آنها در نهایت یک تصویر jpeg تحویل میدادند. ولی من میخواستم نقشهی من زنده و واکنشگرا باشد تا کاربر بتواند روی آن حرکت کند، زوم کند، مارکرها را جابجا کند و امکانات دیگری که در این نقشه در دسترس است را داشته باشد. برای همین شروع به ساخت یک class library کردم تا کاربر بتواند در محیط سی شارپ، تنظیمات را با اسامی قابل شناخت و یک intellisense قدرتمند بنویسد و در نهایت بر اساس اطلاعات کاربر، این کدها به صورت جاوا اسکریپت تولید شود. میتوانید سورس نهایی کتابخانهی GMap.Net را در گیت هاب، به همراه یک پروژهی نمونه ببینید.
پروژهی وابسته این کتابخانه، MS Ajax Minifier جهت کم حجم کردن کدهای جاوا اسکریپت است. در مورد این کتابخانه در سایت جاری بحث شده است.
برای نصب این کتابخانه میتوانید از طریق دستور زیر در Nuget عمل کنید:
Install-Package GMap.Net
در این کتابخانه مواردی که مورد توجه قرار گرفته است، تنظیمات نقشه و بعد از آن overlayها هستند که شامل مارکرها و اشکال مختلف میباشند. این اشکال شامل رسم مستطیل بر روی نقشه، چند ضلعیها و ... نیز میشوند.
برای شروع نیاز است که یک نمونه از کلاس GoogleMapApi را ایجاد کنید. بعد از آن با استفاده از خصوصیت SetLocation، مختصات مرکز نقشه را تنظیم نمایید. سپس با استفاده از خصوصیات دیگر نیز میتوانید نقشه را تنظیم نمایید. تعدادی از این خصوصیات مثل SetZoomVisibility هستند که با استفاده از آن میتوانید تنظیمات زوم را روی نقشه پیاده سازی کنید. البته فعال کردن این گزینه به تنهایی کافی نیست و باید از طریق خصوصیت ZoomControlOption پیکربندی کنترل زوم را نیز اینجام دهید که این پیکربندی شامل موقعیت قرارگیری کنترلهای زوم و اندازهی دکمهها میباشد و مابقی تنظیمات هم بدین شکل هستند:
به غیر از تنظیمات نقشه، Overlayهای زیر در این کلاس پشتیبانی میشوند:
عنوان | توضیحات |
Marker | یک نشانه گذار که برای مشخص کردن یک محل بر روی نقشه به کار میرود. این علامت گذار شامل خصوصیتهایی چون نقطهی قرارگیری، آیکن، عنوان و انیمیشنی برای نحوهی نمایش آن میباشد. همچنین شامل یک خصوصیت دیگر از نوع InfoWindow است که به شما امکان نمایش یک پنجرهی توضیحات را نیز بر روی مارکر میدهد. این محتوا میتواند به صورت HTML نمایش یابد. |
Circle | در صورتیکه بخواهید ناحیهای دایرهای شکل را بر روی نقشه مشخص کنید، کاربرد دارد. با دادن نقطهی مرکزی و شعاع میتوانید دایره را ترسیم کنید. همچنین شامل خصوصیات ظاهری چون رنگ داخل و حاشیهها و میزان شفافیت نیز میباشد. |
Rectangle | به رسم یک مستطیل میپردازد و تنها لازم است دو مختصات را به آن بدهید و بر اساس این دو نقطه، ناحیهی مستطیلی شکل ترسیم میگردد. در صورتیکه نقاط بیشتری را به آن اضافه کنید، فقط دوتای اولی در نظر گرفته میشوند. این گزینه شامل خصوصیات ظاهری نیز میگردد. |
Polyline | برای ترسیم مسیرها به صورت چند ضلعی به کار میرود و الزامی به بستن مسیرها نیست. دارای خصوصیات ظاهری نیز میباشد. |
polygon | کاملا شبیه Ployline است؛ با این تفاوت که یک چند ضلعی بسته است و میتواند داخل آن با رنگ پر باشد. برای بستن این چند ضلعی لازم نیست تا کاری انجام دهید. خود کلاس، نقطهی اول و آخر را به هم وصل میکند. |
خصوصیات آیتمهای بالا، شامل موارد زیر میشود:
نام خصوصیت | توضیحات |
Id | در سازندهی هر کدام به طور اجباری قرار گرفته است. این id برای زمانی است که بخواهید با استفاده از جاوااسکرپیت با آن ارتباط برقرار کنید. |
Editable | با فعال کردن این خاصیت، به کاربر این اجازه را میدهید که بتواند روی Overlay ویرایش انجام دهد. |
StrokeWeight | ضخامت لبهها را مشخص میکند. |
StrokeColor | رنگ لبه را مشخص میکند. |
StrokeOpacity | میزان شفافیت لبه را بین 0 تا 1 مشخص میکند. |
FillColor | بعضی از المانها مانند چند ضلعیهای بسته و مستطیل که ناحیهی داخلی دارند، شامل این گزینه هستند و رنگ داخل این ناحیه را مشخص میکنند. |
FillOpacity | میزان شفافیت خصوصیت بالا را از 0 تا 1 مشخص میکند. |
Points | با استفاده از این خاصیت میتوانید مختصات را با استفاده از کلاس Location به آن اضافه کنید. برای دایره خصوصیت Point وجود دارد. |
Radius | برای دایره کاربرد دارد. با مقدار نوع Int میتوانید شعاع آن را مقدار دهی کنید. |
public class MiladTower { public GoogleMapApi TestMarker() { var map=new GoogleMapApi(true); var location = new Location(35.7448416, 51.3753212); map.SetLocation(location); map.SetZoom(17); map.SetMapType(MapTypes.ROADMAP); map.SetBackgroundColor(Color.Aqua); map.ZoomControlVisibilty(true); map.ZoomOptions = new zoomControlOptions() { Position = Position.TOP_LEFT, ZoomStyle = ZoomStyle.SMALL }; Marker marker=new Marker("mymarker1"); marker.InfoWindow=new InfoWindow("iw1") { Content = "<b>Milad Tower</b><i>in Tehran</i><br/>Milad Tower is the highest tower in iran,many people and tourists visit it each year, but it's so expensive that i cant afford it as iranian citizen<br/>please see more info at <a href=\"https://en.wikipedia.org/wiki/Milad_Tower\"><img width='16px' height='16px' src='https://en.wikipedia.org/favicon.ico'/>wikipedia</a>" }; marker.MarkerPoint = location; map.Markers.Add(marker); var circle=new CircleMarker("mymarker2"); circle.FillColor = Color.Green; circle.FillOpacity = 0.6f; circle.StrokeColor = Color.Red; circle.StrokeOpacity = 0.8f; circle.Point = location; circle.Radius = 30; circle.Editable = true; circle.StrokeWeight = 3; map.Circles.Add(circle); Rectangle rect=new Rectangle("rect1"); rect.FillColor = Color.Black; rect.FillOpacity = 0.4f; rect.Points.Add(new Location(35.74728723483808, 51.37550354003906)); rect.Points.Add(new Location(35.74668641224311, 51.376715898513794)); map.Rectangles.Add(rect); Polyline polyline=new Polyline("poly1"); polyline.Points.Add(new Location(35.74457043569041, 51.373915672302246)); polyline.Points.Add(new Location(35.74470976097927, 51.37359380722046)); polyline.Points.Add(new Location(35.744378863020074, 51.37337923049927)); polyline.StrokeColor = Color.Blue; polyline.StrokeWeight = 2; map.Polylines.Add(polyline); Polygon polygon=new Polygon("poly2"); polygon.Points.Add(new Location(35.746494844665094, 51.374655961990356)); polygon.Points.Add(new Location(35.74635552250061, 51.37283205986023)); polygon.Points.Add(new Location(35.74598109297522, 51.372681856155396)); polygon.Points.Add(new Location(35.7454934611854, 51.37361526489258)); polygon.FillColor = Color.Black; polygon.FillOpacity = 0.5f; polygon.StrokeColor = Color.Gray; polygon.StrokeWeight = 1; map.Polygons.Add(polygon); return map; } }
@section javascript { @{ var map = new MiladTower().TestMarker(); @map.ShowMapForMvc("mapdiv") } } <br/><br/> <div id="mapdiv" style="width:600px;height:600px;"></div>
در نهایت نقشهی زیر نمایش داده میشود:
کم حجم کردن کدها
در صورتیکه به سورس صفحه نگاهی بیندازید، میبینید که کدهای جاوا اسکریپت، داخل صفحه نوشته شدهاند. اگر بخواهید برای کم حجمتر شدن کد، عملیات minify را انجام دهید، با true شدن خصوصیت minified با استفاده از کتابخانهی وابستهاش (MS Ajax Minifier) اینکار را انجام میدهد.
انتقال کدها به یک فایل خارجی
بسیاری از ما برای نوشتن کدهای جاوا اسکریپت، از یک فایل خارجی استفاده میکنیم. برای داشتن این قابلیت میتوانید به جای ShowMapForMVC متد CallJs را صدا بزنید تا کتابخانه api گوگل را صدا بزند و سپس در یک اکشن متد، متد GiveJustJS را صدا بزنید و طبق مقالهی موجود در سایت جاری محتوای آن را برگردانید و به عنوان یک فایل JS به این اکشن متد لینک بدهید. کدهای زیر به شما نحوهی این کار را نشان میدهند:
ابتدا در یک اکشن متد، کد زیر را وارد میکنیم:
public ActionResult MiladJs() { var output = new MiladTower().TestMarker().GiveJustJs("mapdiv"); Response.ContentType = "text/javascript"; return Content(output); }
بعد از آن در ویووی مربوطه کد زیر را داریم:
@section javascript { @{ var map = new MiladTower().TestMarker(); @map.CallJs() <script type="text/javascript" src="@Url.Action("MiladJs","Home")"></script> } } <br/><br/> <div id="mapdiv" style="width:600px;height:600px;"></div>
نحوهی کارکرد این کتابخانه
برای آشنایی با نحوهی کارکرد آن باید بدانید که اساس کار این کتابخانه string interpolation است. این کتابخانه کلاسی را به صورت Partial دارد که بین چندین فایل تقسیم شده است و هر یک از فایلها، با نام محتوای آن نامگذاری شدهاند. Public methods متدهای عمومی، private methods متدهای خصوصی، Constants یا ثابتها که حاوی تمام دستورات جاوا اسکریپتی هستند و در نهایت خود کلاس اصلی GoogleMapApi که حاوی کدهای اجرایی و تشکیل کد جاوا اسکریپت میباشد. در کنار کلاس اصلی، کلاسهای Overlay هم قرار دارند که شامل اطلاعات اشیاء روی نقشهها هستند؛ مثل مارکرها و چندضلعیها و ... و در نهایت یک سری کلاس و نوع شمارشی Enum شامل خصوصیتهایی که برای تنظیمات و پیکربندی نقشه به کار میروند.
کلاس GoogleMapApi برای ایجاد کدها، دادههایی را که برای هر کلاس در نظر گرفتهاید، با استفاده از interpolation و ثابتهای حاوی کد جاوا اسکریپت، در یک رشتهی جدید قرار میدهند و این رشتهها با اتصال درست در موقعیت خود، کد نهایی جاوا اسکریپت را تولید میکنند.
SFDown
آشنایی اولیه با gRPC
syntax = "proto3"; package helloworld; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; }
Google.Protobuf grpc Grpc.Tools
<ItemGroup> <Protobuf Include="helloworld.proto" /> </ItemGroup>
using System; using System.Collections.Generic; using System.Threading.Tasks; using Helloworld; using Grpc.Core; namespace ServerGrpc { class GreeterImpl : Greeter.GreeterBase { public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context) { System.Console.WriteLine("request made!"); return Task.FromResult(new HelloReply { Message = "Hello " + request.Name }); } } class Program { const int Port = 50051; public static void Main(string[] args) { Server server = new Server { Services = { Greeter.BindService(new GreeterImpl()) }, Ports = { new ServerPort("localhost", Port, ServerCredentials.Insecure) } }; server.Start(); Console.WriteLine("Greeter server listening on port " + Port); Console.WriteLine("Press any key to stop the server..."); Console.ReadKey(); server.ShutdownAsync().Wait(); } } }
obj/Debug/netcoreapp2.2(یا نسخهی دیگری که استفاده میکنید)
protoc helloworld.proto --go_out=plugins=grpc:.
package main import ( "fmt" "golang.org/x/net/context" "google.golang.org/grpc" "gosample/proto" ) func main() { initial() } func initial(){ conn, _ := grpc.Dial("localhost:50051", grpc.WithInsecure()) defer conn.Close() client := helloworld.NewGreeterClient(conn) data, _ := client.SayHello(context.Background(), &helloworld.HelloRequest{Name : "Ali"}) fmt.Println(data) }
https://www.dntips.ir/(xxxxxx)/page.aspx
Response.ApplyAppPathModifier
Set-Cookie: <name>=<value>[; <Max-Age>=<age>] [; expires=<date>][; domain=<domain_name>] [; path=<some_path>][; secure][; HttpOnly]
<httpCookies httpOnlyCookies="true" …>
HttpCookie myCookie = new HttpCookie("myCookie"); myCookie.HttpOnly = true; Response.AppendCookie(myCookie);