این پروژه یک فایل اسکریپت بیشتر نیست. بنابراین برای کار کردن نیاز به الحاق آن به صفحه هست (مثل تمام پروژههای جاوا اسکریپتی). ضمنا این مطلب رو برای سؤال پرسیدن باید رعایت کنی: آناتومی یک گزارش خطای خوب . کسی نمیدونه این ارور میده یعنی چی؟ چه خطایی میده؟ کسی مونیتورت رو نمیتونه از راه دور ببینه. باید توضیح بدی. تشریحش کنی با کمک این ابزار: نحوه استفاده از افزونه Firebug برای دیباگ برنامههای ASP.NET مبتنی بر jQuery
ASP.NET MVC #23
- اگر از متدهای توکار خود ASP.NET MVC برای تولید لینکها استفاده کنید، این لینکها صرفا بر مبنای اطلاعات مسیریابی تعریف شده تولید میشوند.
- باید به application pool برنامه مراجعه کنید.
- روی 2003 خیر. پس از آن به صورت پیش فرض وجود دارد.
- مراجعه کنید به application pool برنامه. احتمالا روی دات نت 2 است؛ بجای 4.
توضیحات بیشتر در اینجا
- یک اکشن متد ساده برای صفحهی Home طراحی کنید.
در این مثال که از Ef Code First و الگوی Unit Of Work استفاده کرده ام ، قصد نمایش اطلاعات در یک ListView و صفحه بندی آن را بصورت chunk chunk دارم.
برای آشنایی بیشتر با الگوی Unit Of Work میتوانید به مقاله UOW در همین سایت مراجعه کنید.
ابتدا یک کنترل ListView روی صفحه ایجاد میکنیم. برای آشنایی بیشتر با این کنترل و بررسی قابلیتهای آن میتوان از این مقاله معرفی کنترلهای ListView و DataPager استفاده کنید.
سپس با توجه به الگوی Unit Of Work از یک مدل ساده استفاده میکنیم. همچنین برای بارگذاری اطلاعات به صورت صفحه به صفحه، نیاز به داشتن Index رکورد و PageSize، جهت محاسبه تعداد رکورد مورد نیاز داریم.
public class User { public Int64 UserId { get; set; } public String UserName { get; set; } }
public interface IUserService { int GetCustomerCount(); List<User> GetCustomers(int StartIndex, int PageSize); }
public class ImplUserService : IUserService { IUnitOfWork _uow; IDbSet<User> _user; public ImplUserService(IUnitOfWork uow) { _uow = uow; _user = uow.Set<User>(); } public int GetCustomerCount() { int totalCount = _user.ToList().Count; return totalCount; } public List<User> GetCustomers(int StartIndex, int PageSize) { return _user.OrderBy(i => i.UserId).Skip(StartIndex).Take(PageSize).ToList(); } }
متد GetCustomer که کار اصلی را انجام میدهد از دو پارامتر استفاده میکند: StartIndex نقطه شروع و PageSize تعداد رکورد مورد نظر. در اینجا از دستورات Linq استفاده شده و دستور Skip مشخص میکند از کدام شماره رکورد به بعد شروع به واکشی نماید و دستور Take مشخص میکند که چه تعداد رکورد را واکشی نماید.
حالا به سراغ کدهای HTML میرویم. در آنجا علاوه بر ListView نیاز به DataPager جهت صفحه بندی و Object DataSource جهت کنترل بارگذاری اطلاعات به صورت chunk chunk داریم.
<asp:ListView ID="ListView1" runat="server" DataSourceID="ObjectDataSource1"> <ItemTemplate> <tr style="background-color: #DCDCDC; color: #000000;"> <td> <asp:Label ID="UserIdLabel" runat="server" Text='<%# Eval("UserId") %>' /> </td> <td> <asp:Label ID="UserNameLabel" runat="server" Text='<%# Eval("UserName") %>' /> </td> </tr> </ItemTemplate> <LayoutTemplate> <table runat="server"> <tr runat="server"> <td runat="server"> <table id="itemPlaceholderContainer" runat="server" border="1" style="background-color: #FFFFFF; border-collapse: collapse; border-color: #999999; border-style: none; border-width: 1px;"> <tr runat="server" style="background-color: #DCDCDC; color: #000000;"> <th runat="server"> UserId </th> <th runat="server"> UserName </th> </tr> <tr id="itemPlaceholder" runat="server"> </tr> </table> </td> </tr> <tr runat="server"> <td runat="server" style="text-align: center; background-color: #CCCCCC; color: #000000;"> <asp:DataPager ID="DataPager1" runat="server" PageSize="2"> <Fields> <asp:NextPreviousPagerField ButtonType="Button" ShowFirstPageButton="True" ShowNextPageButton="False" ShowPreviousPageButton="False" /> <asp:NumericPagerField /> <asp:NextPreviousPagerField ButtonType="Button" ShowLastPageButton="True" ShowNextPageButton="False" ShowPreviousPageButton="False" /> </Fields> </asp:DataPager> </td> </tr> </table> </LayoutTemplate> </asp:ListView>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" EnablePaging="True" SelectCountMethod="GetCustomerCount" SelectMethod="GetCustomers" TypeName="UserService.ImplUserService" EnableViewState="False" MaximumRowsParameterName="PageSize" StartRowIndexParameterName="StartIndex">
نکته مهم:
اگر الان برنامه را اجرا کنید با خطای No parameterless constructor defined for this object ر وبرو خواهید شد که جهت حل این مشکل از کد زیر استفاده میکنیم.
protected void ObjectDataSource1_ObjectCreating(object sender, ObjectDataSourceEventArgs e) { e.ObjectInstance = ObjectFactory.GetInstance<IUserService>(); }
حال با اجرای برنامه و Trace آن متوجه خواهید شد که با کلیک بر روی شماره صفحه، تنها به تعداد رکوردهای همان صفحه، واکشی خواهیم داشت.
با تشکر از راهنماییهای آقای نصیری، امیدوارم این مقاله برای دوستان مفید باشه. منتظر نظرات دوستان هستم و اینکه چه جوری بتونیم اینکار رو با jquery ajax و به صورت سبکتر انجام بدیم.
(Data Mining Algorithms (Analysis Services - Data Mining
(Algorithm Parameters (SQL Server Data Mining Add-ins
1 - Microsoft Association Rules
به منظور ایجاد قوانینی که توصیف کننده این موضوع باشد که چه مواردی احتمالاً با یکدیگر در تراکنشها ظاهر میشوند، استفاده میشود.
Range | Default | Parameter |
(...,1] | 200000 | MAXIMUM_ITEMSET_COUNT |
[0,500] | 3 | MAXIMUM_ITEMSET_SIZE |
(...,0.0) | 1.0 | MAXIMUM SUPPORT |
(...,...) | 999999999- | MINIMUM IMPORTANCE |
[1,500] | 1 | MINIMUM_ITEMSET_SIZE |
[0.0,1.0] | 0.4 | MINIMUM PROBABILITY |
(...,0.0] | 0.0 | MINIMUM SUPPORT |
2 - Microsoft Clustering
به منظور شناسائی روابطی که در یک مجموعه داده ممکن است از طریق مشاهده منطقی به نظر نرسد، استفاده میشود. در واقع این الگوریتم با استفاده از تکنیکهای تکرار شونده رکوردها را در خوشه هایی که حاوی ویژگیهای مشابه هستند گروه بندی میکند.
Range | Default | Parameter |
(...,0] | 10 | CLUSTER COUNT |
(...,0] | 0 | CLUSTER SEED |
1,2,3,4 | 1 | CLUSTERING METHOD |
[0,65535] | 255 | MAXIMUM_INPUT_ATTRIBUTES |
[2,65535],0 | 100 | MAXIMUM STATES |
(...,0) | 1 | MINIMUM SUPPORT |
[1,50] | 10 | MODELLING_CARDINALITY |
(...,100],0 | 50000 | SAMPLE SIZE |
(...,0) | 10 | STOPPING TOLERANCE |
3 - Microsoft Decision Trees
مبتنی بر روابط بین ستونهای یک مجموعه داده ای باعث پیش بینی روابط مدلها میشود، که به صورت یک سری درختوار ویژگیها در آن شکسته میشوند.
به منظور انجام پیش بینی از هر دو ویژگی پیوسته و گسسته پشتیبانی میشود.
Range | Default | Parameter |
(0.0,1.0) | COMPLEXITY_PENALTY | |
FORCE REGRESSOR | ||
[0,65535] | 255 | MAXIMUM_INPUT_ATTRIBUTES |
[0,65535] | 255 | MAXIMUM_OUTPUT_ATTRIBUTES |
(...,0.0) | 10.0 | MINIMUM SUPPORT |
1,3,4 | 4 | SCORE METHOD |
[1,3] | 3 | SPLIT METHOD |
4 - Microsoft Linear Regression
چنانچه یک وابستگی خطی میان متغیر هدف و متغیرهای مورد بررسی وجود داشته باشد، کارآمدترین رابطه میان متغیر هدف و ورودیها را پیدا میکند.
به منظور انجام پیش بینی از ویژگی پیوسته پشتیبانی میکند.
Range | Default | Parameter |
| FORCE REGRESSOR | |
[0,65535] | 255 | MAXIMUM_INPUT_ATTRIBUTES |
[0,65535] | 255 | MAXIMUM_OUTPUT_ATTRIBUTES |
5 - Microsoft Logistic Regression
به منظور تجزیه و تحلیل عواملی که در یک تصمیم گیری مشارکت دارند که پی آمد آن به وقوع یا عدم وقوع یک رویداد میانجامد از این الگوریتم استفاده میشود.
جهت انجام پیش بینی از هر دو ویژگی پیوسته و گسسته پشتیبانی میکند.
Range | Default | Parameter |
(0,100) | 30 | HOLDOUT_PERCENTAGE |
(...,...) | 0 | HOLDOUT SEED |
[0,65535] | 255 | MAXIMUM_INPUT_ATTRIBUTES |
[0,65535] | 255 | MAXIMUM_OUTPUT_ATTRIBUTES |
[2,65535],0 | 100 | MAXIMUM STATES |
(...,0] | 10000 | SAMPLE SIZE |
6 - Microsoft Naïve Bayes
احتمال ارتباط میان تمامی ستونهای ورودی و ستونهای قابل پیش بینی را پیدا میکند. همچنین این الگوریتم برای تولید سریع مدل کاوش به منظور کشف ارتباطات بسیار سودمند میباشد. تنها از ویژگیهای گسسته یا گسسته شده پشتیبانی میکند و با تمامی ویژگیهای ورودی به شکل مستقل رفتار میکند.
Range | Default | Parameter |
[0,65535] | 255 | MAXIMUM_INPUT_ATTRIBUTES |
[0,65535] | 255 | MAXIMUM_OUTPUT_ATTRIBUTES |
[2,65535],0 | 100 | MAXIMUM STATES |
(0,1) | 0.5 | MINIMUM_DEPENDENCY_PROBABILITY |
7 - Microsoft Neural Network
به منظور تجزیه و تحلیل دادههای ورودی پیچیده یا مسائل بیزنسی که برای آنها مقدار قابل توجهی داده آموزشی در دسترس میباشد اما به آسانی نمیتوان با استفاده از الگوریتمهای دیگر این قوانین را بدست آورد، استفاده میشود. با استفاده از این الگوریتم میتوان چندین ویژگی را پیش بینی نمود. همچنین این الگوریتم میتواند به منظور طبقه بندی برای ویژگیهای گسسته و ویژگیهای پیوسته رگرسیون مورد استفاده قرار گیرد.
Range | Default | Parameter |
(...,0] | 4.0 | HIDDEN_NODE_RATIO |
(0,100) | 30 | HOLDOUT PERCENTAGE |
(...,...) | 0 | HOLDOUT SEED |
[0,65535] | 255 | MAXIMUM_INPUT_ATTRIBUTES |
[0,65535] | 255 | MAXIMUM_OUTPUT_ATTRIBUTES |
[2,65535],0 | 100 | MAXIMUM STATES |
(...,0] | 10000 | SAMPLE SIZE |
8 - Microsoft Sequence Clustering
به منظور شناسائی ترتیب رخدادهای مشابه در یک دنباله استفاده میشود. در واقع این الگوریتم ترکیبی از تجزیه تحلیل توالی و خوشه را فراهم میکند.
Range | Default | Parameter |
(...,0] | 10 | CLUSTER COUNT |
[2,65535],0 | 64 | MAXIMUM_SEQUENCE_STATES |
[2,65535],0 | 100 | MAXIMUM STATES |
(...,0] | 10 | MINIMUM SUPPORT |
9 - Microsoft Time Series
به منظور تجزیه و تحلیل دادههای زمانی (دادههای مرتبط با زمان) در یک درخت تصمیم گیری خطی استفاده میشود. الگوهای کشف شده میتوانند به منظور پیش بینی مقادیر آینده در سریهای زمانی استفاده شوند.
Range | Default | Parameter |
[0.0,1.0] | 0.6 | AUTO_DETECT_PERIODICITY |
(1.0,...) | 0.1 | COMPLEXITY_PENALTY |
ARIMA,ARTXP,MIXED | MIXED | FORECAST METHOD |
[0,100] | 1 | HISTORIC_MODEL_COUNT |
(...,1] | 10 | HISTORIC_MODEL_GAP |
[0.0,1.0] | 1.0 | INSTABILITY_SENSITIVITY |
[...,column maximum] | 1E308+ | MAXIMUM_SERIES_VALUE |
[column minimum,...] | 1E308- | MINIMUM_SERIES_VALUE |
(...,1] | 10 | MINIMUM SUPPORT |
None,Previous,Mean | None | MISSING_VALUE_SUBSTITUTION |
{...list of integers...} | {1} | PERIODICITY_HINT |
[0.0,1.0] | 0.5 | PREDICTION SMOOTHING |
<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0"> <edmx:DataServices> <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="OwinAspNetCore.Models"> <EntityType Name="Product"> <Key> <PropertyRef Name="Id"/> </Key> <Property Name="Id" Type="Edm.Int32" Nullable="false"/> <Property Name="Name" Type="Edm.String"/> <Property Name="Price" Type="Edm.Decimal" Nullable="false"/> </EntityType> </Schema> <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="Default"> <Function Name="TestFunction" IsBound="true"> <Parameter Name="bindingParameter" Type="Collection(OwinAspNetCore.Models.Product)"/> <Parameter Name="Val" Type="Edm.Int32" Nullable="false"/> <Parameter Name="Name" Type="Edm.String"/> <ReturnType Type="Edm.Int32" Nullable="false"/> </Function> <EntityContainer Name="Container"> <EntitySet Name="Products" EntityType="OwinAspNetCore.Models.Product"/> </EntityContainer> </Schema> </edmx:DataServices> </edmx:Edmx>
string postUrl = "http://localhost:port/...."; HttpClient client = new HttpClient(); var response = client.PostAsync(postUrl, new StringContent(JsonConvert.SerializeObject(new { Rating = 5 }), Encoding.UTF8, "application/json")).Result;
آن را نصب نموده و بعد از تکمیل شدن، visual studio را restart کنید.
پروژهی console خود را باز کرده و از طریق Add -> new item، آیتم OData client را جستجو کرده و با نام ProductClient.tt آن را تولید نمایید (نام آن اختیاری است):
فایل ProductClient.tt را که یک T4 code generator میباشد، باز کرده و مقدار ثابت MetadataDocumentUri را به آدرس سرویس odata خود تغییر دهید:
public const string MetadataDocumentUri = "http://localhost:port/odata/";
روی این آیتم کلیک راست و گزینهی Run Custom tool را انتخاب نمایید. این تمام کاری است که نیاز به انجام دادن دارید.
حال فایل Program.cs را باز کرده و آنرا اینگونه تغییر دهید:
using ConsoleApplication1.OwinAspNetCore.Models; using System; using System.Linq; namespace ConsoleApplication1 { public class Program { static void Main(string[] args) { Uri uri = new Uri("http://localhost:24977/odata"); //var context = new Default.Container(uri); var context = new TestNameSpace.TestNameSpace(uri); //get var products = context.Products.Where(pr => pr.Name.Contains("a")) .Take(1).Select(pr => new { Firstname = pr.Name, PriceValue = pr.Price }).ToList(); //add context.AddToProducts(new Product() { Name = "Name1", Price = 123 }); //update Product p = context.Products.First(); p.Name = "changed"; context.UpdateObject(p); //delete context.DeleteObject(context.Products.Last()); //commit context.SaveChanges(); } } }
مشاهده میفرمایید که همهی عملیاتهای لازم برای CRUD، به شرط اینکه در سمت سرور طراحی شده باشند، به راحتی از سمت کلاینت قابل فراخوانی خواهند بود.
از این ویژگی فوق العاده میتوان حتی در کلاینتها جاوااسکریپتی نیز استفاده کرد. فرض کنید نرم افزار تحت وبی را با استفاده از jquery یا angularjs طراحی کردهاید. قاعدتا فراخوانی درخواستهای شما به سمت سرور، چیزی شبیه به این خواهد بود:
//angularjs $http.get("/products/get", {Name: "Test", Company: "Test"}) .then(function(response) { console.log(response.data); }); //jquery $.get("/products/get", {Name: "Test", Company: "Test"}, function(data, status){ console.log("Data: " + data); });
با استفاده از odata و typescript و یک library مربوط به odata client در سمت کلاینت، نرم افزار شما بجای موارد، بالا چیزی شبیه به مثال زیر خواهد بود (با همراه داشتن strongly typed و intellisense کامل)
let product1 = await context.products.filter(c => c.Name.contains("Ali")).toArray(); let product2 = await context.products.getSomeFunction(1, 'Test'); context.product.add({Name: 'Test'} as Product); await context.saveChanges()
در مقالههای آتی به ویژگیهای بیشتری از Odata خواهیم پرداخت.
FirstName
firstName
strFirstName lblFirstName
<Company>.<Technology|Produt|Project>[.<Feature>][.<SubNamespace>]
Microsoft.Reporting.WebForms Kara.Support.Manager.Enums Kara.CSS.HQ.WebUI.Configuration
PackageManager , PacakgeConfigGenerator Circle , Utility , Package
CreateConfig , classdata CManager , ClsPackage , Config_Creator , Config1389
CenterCollection , PackageCollection
public delegate void Logger (string log); public delegate void LoggingCallback (object sender, string reason);
AddEventArgs , EditEventArgs , DeleteEventArgs
Adding , Add , Added
public delegate void <EventName>EventHandler (object sender, <EventName>EventArgs e);
public event EventHandler <AddEventArgs> Adding;
DisplayNameAttribute , MessageTypeAttribute
IComponent (اسم) IConnectionProvider (موصوف) ICloneable (صفت)
public enum FileMode { Append, Read, … }
[Flag] public enum KeyModifiers { Alt = 1, Control = 2, Shift = 4 }
public enum OperationState { DoneState, FaultState, RollbackState }
AddDays , Save , DeleteRow , BindData , Close , Open
Radius , ReportType , DataSource , Mode , CurrentCenterId
public CenterCollection Centers { get; set; }
if (list.Contains(item)) if (regularExpression.Matches(text)) if (stream.CanSeek) if (context.Created) if (form.Enabled)
if (list.IsContains(item)) if (regularExpression.Match(text)) if (stream.Seekable) if (context.IsCreated) if (form.IsEnabled)
public Color Color { get; set; }
firstName , e , id , packageId , centerName , name
public static MyType operator +(MyType left, MyType right) public static bool operator ==(MyType left, MyType right)
public static MyType operator ++(MyType value)
public static MyType operator /(MyType dividend, MyType divisor)
public static MyType operator -(MyType d1, MyType d2) // incorrect!
Area , DataBinder , PublicCacheName
_centersList _firstName _currentCenter
parameterType , packageOperationTypeId
lblName (Label) txtHeader (TextBox) btnSave (Button)
ArgumentNullException , InvalidOperaionException
System.Data
<Company>.<Component>.dll <Company>.<Project|Product|Technology>.<Component>.dll
Microsoft.CSharp.dll , Kara.CSS.Manager.dll
public int IComparer<T> {…} public delegate bool Predicate<T> (T item)
public int ISessionChannel<TSession> {…} public delegate TOutput Converter<TInput, TOutput> (TInput from) public class Nullable<T> {…} public class List<T> {…}
ArgumentExceptionIllegalCharacters ArgumentExceptionInvalidName ArgumentExceptionFileNotFound
Pascal Casing |
camel Casing |
Wrong |
Callback |
callback |
CallBack |
BitFlag |
bitFlag |
Bitflag / bitflag |
Canceled |
canceled |
Cancelled |
DoNot |
doNot |
Donot / Don’t |
|
|
|
Endpoint |
endpoint |
EndPoint / endPoint |
FileName |
fileName |
Filename / filename |
Gridline |
gridline |
GridLine / gridLine |
Hashtable |
hashtable |
HashTable / hashTable |
Id |
id |
ID |
Indexes |
indexes |
Indices |
LogOff |
logOff |
Logoff / LogOut ! |
LogOn |
logOn |
Logon / LogIn ! |
SignOut |
signOut |
Signout / SignOff |
SignIn |
signIn |
Signin / SignOn |
Metadata |
metadata |
MetaData / metaData |
Multipanel |
multipanel |
MultiPanel / multiPanel |
Multiview |
multiview |
MultiView / multiView |
Namespace |
namespace |
NameSpace / nameSpace |
Ok |
ok |
OK |
Pi |
pi |
PI |
Placeholder |
placeholder |
PlaceHolder / placeHolder |
UserName |
username |
Username / username |
WhiteSpace |
whiteSpace |
Whitespace / whitespace |
Writable |
writable |
Writeable / writeable |
public class Color { … public static Color FromArgb(…) { … } }
در Blazor میتوان مسیریابیهای پارامتری را به صورت زیر نیز تعریف کرد:
@page "/post/edit/{EditId:int}"
که در اینجا EditId، یک پارامتر مسیریابی از نوع int تعریف شده و به صورت زیر در کدهای صفحهی مرتبط، قابل دسترسی است:
[Parameter] public int? EditId { set; get; }
int تعریف شدهی در این مسیریابی، یک routing constraint و یا یک قید مسیریابی محسوب میشود و استفادهی از آن، چنین مزایایی را به همراه دارد:
- در این حالت فقط EditId های عددی پردازش میشوند و اگر رشتهای دریافت شود، کاربر با خروجی از نوع 404 و یا «یافت نشد»، مواجه خواهد شد.
- امکان اعتبارسنجی مقادیر دریافتی، پیش از ارسال آنها به صفحه و پردازش صفحه.
قیود پیشفرض تعریف شدهی در Blazor
اگر به مستندات مسیریابی Blazor مراجعه کنیم، بهنظر فقط این موارد را میتوان بهعنوان قیود پارامترهای مسیریابی تعریف کرد:
bool, datetime, decimal, double, float, guid, int, long, nonfile
و ... توضیحاتی در مورد اینکه آیا امکان بسط آنها وجود دارند یا خیر، فعلا در مستندات رسمی آن، ذکر نشدهاند.
در Blazor 8x میتوان از قیود مسیریابی سفارشی ASP.NET Core نیز استفاده کرد!
ASP.NET Core سمت سرور، به همراه امکان سفارشی سازی قیودمسیریابی خود نیز هست که آنرا میتوان به کمک اینترفیسIRouteConstraint پیاده سازی کرد:
namespace Microsoft.AspNetCore.Routing { public interface IRouteConstraint { bool Match( HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection); } }
جالب اینجا است که میتوان این نمونههای سفارشی را حداقل در نگارش جدید Blazor 8x SSR نیز استفاده کرد؛ هرچند در مستندات رسمی Blazor هنوز به آن اشارهای نشدهاست.
در امضای متد Match فوق، دو پارامتر routeKey و values آن بیش از مابقی مهم هستند:
- routeKey مشخص میکند که الان کدام پارامتر مسیریابی (مانند EditId در این مطلب) در حال پردازش است.
- values، یک دیکشنری است که کلید هر عضو آن، پارامتر مسیریابی و مقدار آن، مقدار دریافتی از URL جاری است.
- اگر این متد مقدار true را برگرداند، یعنی مسیریابی وارد شدهی به آن، با موفقیت پردازش و اعتبارسنجی شده و میتوان صفحهی مرتبط را نمایش داد؛ در غیراینصورت، کاربر پیام یافت نشدن آن صفحه و مسیر درخواستی را مشاهده میکند.
پیاده سازی یک قید سفارشی رمزگشایی پارامترهای مسیریابی
فرض کنید قصد ندارید که پارامترهای مسیریابی ویرایش رکوردهای خاصی را دقیقا بر اساس Id متناظر عددی آنها در بانک اطلاعاتی، نمایش دهید؛ برای مثال نمیخواهید دقیقا آدرس post/edit/1 را به کاربر نمایش دهید؛ چون نمایش این اعداد عموما ساده و ترتیبی، حدس زدن آنها را ساده کرده و ممکن است در آینده مشکلات امنیتی را به همراه داشته باشد.
میخواهیم از آدرسهای متداول و سادهی عددی زیر:
@page "/post/edit/{EditId:int}"
به آدرس رمزنگاری شدهی زیر برسیم:
@page "/post/edit/{EditId:encrypt}"
اگر به این آدرس جدید دقت کنید، در اینجا از نام قید جدیدی به نام encrypt استفاده شدهاست که جزو قیود پیشفرض سیستم مسیریابی Blazor نیست. روش تعریف آن به صورت زیر است:
using System.Globalization; using DNTCommon.Web.Core; namespace Blazor8xSsrComponents.Utils; public class EncryptedRouteConstraint(IProtectionProviderService protectionProvider) : IRouteConstraint { public const string Name = "encrypt"; public bool Match(HttpContext? httpContext, IRouter? route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) { ArgumentNullException.ThrowIfNull(routeKey); ArgumentNullException.ThrowIfNull(values); if (!values.TryGetValue(routeKey, out var routeValue)) { return false; } var valueString = Convert.ToString(routeValue, CultureInfo.InvariantCulture); values[routeKey] = string.IsNullOrEmpty(valueString) ? null : protectionProvider.Decrypt(valueString); return true; } }
توضیحات:
- در قیود سفارشی میتوان سرویسها را به سازندهی کلاس تزریق کرد و برای مثال از سرویس IProtectionProviderService که در کتابخانهی DNTCommon.Web.Core تعریف شده، برای رمزگشایی اطلاعات رسیده، استفاده کردهایم.
- یک نام را هم برای آن درنظر گرفتهایم که از این نام در فایل Program.cs به صورت زیر استفاده میشود:
builder.Services.Configure<RouteOptions>(opt => { opt.ConstraintMap.Add(EncryptedRouteConstraint.Name, typeof(EncryptedRouteConstraint)); });
یعنی زمانیکه سیستم مسیریابی به قید جدیدی به نام encrypt میرسد:
@page "/post/edit/{EditId:encrypt}"
آنرا در لیست ConstraintMap ای که به نحو فوق به سیستم معرفی شده، جستجو میکند. اگر این نام یافت شد، سپس کلاس EncryptedRouteConstraint متناظر را نمونه سازی کرده و در جهت پردازش مسیر رسیده، مورد استفاده قرار میدهد.
- در کلاس EncryptedRouteConstraint و متد Match آن، مقدار رشتهای EditId دریافت شدهی از طریق آدرس جاری درخواستی، رمزگشایی شده و بجای مقدار فعلی رمزنگاری شدهی آن درج میشود. همین اندازه برای مقدار دهی خودکار پارامتر EditId ذیل در صفحات مرتبط، کفایت میکند:
[Parameter] public string? EditId { set; get; }
یعنی دیگر نیازی نیست تا در صفحات مرتبط، کار رمزگشایی EditId، به صورت دستی انجام شود.