توفر مكتبة RestClient موصلاً بسيطًا لاستهلاك خدمات REST.
لاستخدامها في مشروعك، قم بإضافة حزمة Mafe.RestClient NuGet إلى مشروعك.
الهدف من RestClient هو تمكين المطورين من الاتصال بسهولة بأي خادم بطريقة سهلة حقًا. ما عليك سوى تحديد كائن نقل البيانات (Dto) الخاص بك والبدء في اللعب مع العميل!
استخدم طريقة Build() لإنشاء RestBuilder من Rest:
var rest = Rest . Build ( ) ;
لإنشاء مكالمة الحصول بسيطة، فقط قم بما يلي:
var rest = Rest . Build ( ) ;
var result = rest . Url ( "[URL]" ) . Get ( ) ;
أو يمكننا استخدام طريقة GetAsync ():
var rest = Rest . Build ( ) ;
var result = await rest . Url ( "[URL]" ) . GetAsync ( ) ;
عندما تجد كلمة "[URL]" في هذا المستند، فإنها تشير إلى تعريف عنوان URL الأساسي لواجهة برمجة تطبيقات الويب.
يمكننا تحديد نقطة نهاية Root() واستخدامها لبناء الطلب.
public RestBuilder Root ( ) => rest . Url ( "https://mywebapi.mydomain.com" ) ;
وبعد ذلك، يمكننا استخدامه، مثل هذا:
public RestBuilder Root ( ) => Rest . Build ( ) . Url ( "https://mywebapi.mydomain.com" ) ;
var result = Root ( )
. Command ( "/my-action" )
. Payload ( "mystring" )
. Post ( ) ;
لاستخدام RestProperties لتكوين نقطة جذر الراحة الخاصة بك. لإنشاء تكوين بسيط، تماما مثل هذا:
RestProperties properties = new RestProperties
{
EndPoint = new Uri ( "[URL]" ) , //if you use .Url("[URL]") you override it
BufferSize = 4096 ,
CertificateOption = ClientCertificateOption . Manual ,
Timeout = TimeSpan . FromMinutes ( 2 )
} ;
استخدم طريقة Build() مع الخصائص لإنشاء RestBuilder من Rest:
var rest = Rest . Build ( properties ) ;
يعد التحقق من صحة شهادات X.509 أمرًا ضروريًا لإنشاء جلسات SSL/TLS آمنة وغير عرضة لهجمات الوسيط.
يتضمن التحقق من صحة سلسلة الشهادات الخطوات التالية:
لا يوصى بإعادة اختراع العجلة من خلال تنفيذ التحقق من صحة سلسلة الشهادات المخصصة.
توفر مكتبات TLS وظائف التحقق من صحة الشهادات المضمنة التي يجب استخدامها.
List < string > validCerts = new List < string > ( ) {
"CERT STRING"
} ;
var result = Rest . Build ( )
. CertificateValidation ( ( sender , certificate , chain , errors ) =>
{
// for development, trust all certificates
if ( development ) return true ;
// Compliant: trust only some certificates
return errors == SslPolicyErrors . None
&& validCerts . Contains ( certificate . GetCertHashString ( ) ) ;
} )
. Url ( "[URL]" )
. Get ( ) ;
كما هو محدد بواسطة HTTP/1.1 [RFC2617]، يجب أن يرسل التطبيق رمز الوصول مباشرة في رأس طلب التفويض. يمكنك القيام بذلك عن طريق تضمين قيمة Access_token الخاصة بالرمز المميز للحامل في نص طلب HTTP باسم "التفويض: Bearer {access_token_value}".
إذا كان لدى مستخدم تمت مصادقته رمز وصول أو رمز تحديث لحامل الرمز المميز منتهي الصلاحية، فسيتم إرجاع الخطأ "401 - غير مصرح به (رمز تحديث غير صالح أو منتهي الصلاحية)".
var result = Rest . Build ( )
. Authentication ( ( ) => new AuthenticationHeaderValue ( "Bearer" , "[Token]" ) )
. Url ( "[URL]" )
. Get ( ) ;
يحافظ الرمز المميز لحامله (مع خصائص Access_token أو Refresh_token النشطة) على مصادقة المستخدم دون مطالبته بإعادة إدخال بيانات الاعتماد الخاصة به بشكل متكرر. يمكن استخدام Access_token طالما أنه نشط، وهو ما يصل إلى ساعة واحدة بعد تسجيل الدخول أو التجديد. يكون رمز التحديث نشطًا لمدة 336 ساعة (14 يومًا). بعد انتهاء صلاحية Access_token، يمكن استخدام Refresh_token النشط للحصول على زوج Access_token / Refresh_token جديد كما هو موضح في المثال التالي. يمكن أن تستمر هذه الدورة لمدة تصل إلى 90 يومًا، وبعد ذلك يجب على المستخدم تسجيل الدخول مرة أخرى. إذا انتهت صلاحية Refresh_token، فلا يمكن تجديد الرموز المميزة ويجب على المستخدم تسجيل الدخول مرة أخرى.
لتحديث رمز مميز، استخدم "RefreshTokenInvoc" تلقائيًا.
var url = "[URL]" ;
var result = Rest . Build ( )
. Authentication ( ( ) => new AuthenticationHeaderValue ( "Bearer" , "[Token]" ) )
. RefreshToken ( true )
. RefreshTokenInvoke ( async ( ) =>
{
var result = await rest
. Url ( url )
. Command ( "/refresh" )
. GetAsync < TokenObjectResponse > ( ) ;
doSomethings ( ) ; //store the token inside your env.
return result ;
} )
. Command ( "/detail" )
. Url ( url )
. Get ( ) ;
يجب إبطال رمز التحديث:
فئة NetworkCredential هي فئة أساسية توفر بيانات الاعتماد في أنظمة المصادقة المستندة إلى كلمة المرور مثل Basic وDigest وNTLM وKerberos. تقوم الفئات التي تطبق واجهة ICredentials، مثل فئة CredentialCache، بإرجاع كائنات NetworkCredential. لا تدعم هذه الفئة أساليب المصادقة المستندة إلى المفتاح العام مثل مصادقة عميل طبقة مآخذ التوصيل الآمنة (SSL).
var result = Rest . Build ( )
. NetworkCredential ( "myUsername" , "myPassword" )
. Url ( "[URL]" )
. Get ( ) ;
var result = rest
. NetworkCredential ( ( ) => new System . Net . NetworkCredential ( "myUsername" , "myPassword" ) )
. Url ( "[URL]" )
. Get ( ) ;
تحتوي مجموعة الرؤوس على رؤوس البروتوكول المرتبطة بالطلب. تتيح لك طريقة Header((h)=>{}) إضافة قائمة المفاتيح.
var result = Rest . Build ( )
. Header ( ( h ) => {
if ( ! h . Contains ( "auth-custom" ) )
h . Add ( "auth-custom" , "value" ) ;
} )
. Url ( "[URL]" )
. Get ( ) ;
يدعم RestClient نوعين من التسلسل: Xml وJson، ولكن من الممكن تنفيذ ISerializerContent لتخصيص التسلسل. يستخدم RestClient .Json() لإجراء تسلسل لكائن في json.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Json ( )
. Get < ResponseObject > ( ) ;
من الممكن تمرير خيارات مُسلسل json إلى طريقة .Json()، مثل هذا:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Json ( new JsonSerializerOptions {
WriteIndented = true
} )
. Get < ResponseObject > ( ) ;
يعتبر رمز المقتطف أعلاه استخدام مكتبة System.Text.Json. إذا كنا نستخدم Netwnsoft مثل هذا:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Json ( new JsonSerializerSettings {
Formatting = Formatting . Indented
} )
. Get < ResponseObject > ( ) ;
يستخدم RestClient .Xml() لإجراء تسلسل لكائن في ملف XML.
var result = rest
. Url ( "[URL]" )
. Xml ( )
. Get < ResponseObject > ( ) ;
من الممكن تمرير الإعدادات إلى طريقة .Xml()، مثل هذا:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Xml ( new XmlReaderSettings { Indent = true } , new XmlWriterSettings { IgnoreWhitespace = true } )
. Get < ResponseObject > ( ) ;
فيما يلي مثال حول كيفية إجراء تسلسل مخصص من خلال تطبيق واجهة ISerializerContent:
public class MyCustomSerializer : ISerializerContent
{
public string MediaTypeAsString => throw new NotImplementedException ( ) ;
public object DeserializeObject ( string value , Type typeOf , object options = null )
{
throw new NotImplementedException ( ) ;
}
public string SerializeObject ( object value , Type typeOf , object options = null )
{
throw new NotImplementedException ( ) ;
}
}
الآن، يمكننا استخدام MyCustomSerializer مثل هذا:
var result = Rest . Build ( )
. Url ( "[URL]" )
. CustomSerializer ( new MyCustomSerializer { } )
. Get < ResponseObject > ( ) ;
يمكن استخدام BufferSize لتعيين حجم المخزن المؤقت أثناء التحميل والتنزيل. القيمة الافتراضية هي 80 كيلو بايت
var result = Rest . Build ( )
. Url ( "[URL]" )
. BufferSize ( 4096 * 5 * 5 ) //100Kb
. Get ( ) ;
تمكين ضغط gzip أثناء الاتصال بمورد محدد:
var result = Rest . Build ( )
. Url ( "[URL]" )
. EnableGZipCompression ( )
. Get ( ) ;
تقوم المكتبة بإلغاء ضغط الاستجابة تلقائيًا.
يعد GET أحد أكثر طرق HTTP شيوعًا ويتم استخدام GET لطلب البيانات من مورد محدد
var rest = Rest . Build ( ) ;
var result = rest
. Url ( "[URL]" )
. Get ( ) ;
var result = await rest
. Url ( "[URL]" )
. GetAsync ( ) ;
بعض الملاحظات الأخرى حول طلبات GET:
لاحظ أنه يتم إرسال سلسلة الاستعلام (أزواج الاسم/القيمة) في عنوان URL لطلب GET.
في بعض الحالات نحتاج إلى استخدام الوسائط كسلسلة استعلام. يمكننا استخدام معلمة الطريقة (المفتاح، القيمة) لحلها على النحو التالي:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/path" )
. Parameter ( "id" , "10" )
. Parameter ( "type" , "myType" )
. Get ( ) ;
عنوان URL الذي تم إنشاؤه هو: [URL]/path?id=10&type=myType
يتم استخدام POST لإرسال البيانات إلى الخادم لإنشاء/تحديث المورد. يتم تخزين البيانات المرسلة إلى الخادم باستخدام POST في حمولة الطلب الخاصة بطلب HTTP:
var rest = Rest . Build ( ) ;
var result = rest
. Url ( "[URL]" )
. Command ( "/action" )
. Payload ( new Object { } )
. Post < ResponseObject > ( ) ;
var result = await rest
. Url ( "[URL]" )
. Command ( "/action" )
. Payload ( new Object { } )
. PostAsync < ResponseObject > ( ) ;
يعد النشر طريقة http شائعة أخرى، والتي يتم استخدامها من أجل:
يتم استخدام PUT لإرسال البيانات إلى الخادم لإنشاء/تحديث المورد.
الفرق بين POST و PUT هو أن طلبات PUT غير فعالة. أي أن استدعاء نفس طلب PUT عدة مرات سيؤدي دائمًا إلى نفس النتيجة. في المقابل، فإن استدعاء طلب POST بشكل متكرر له آثار جانبية تتمثل في إنشاء نفس المورد عدة مرات.
var rest = Rest . Build ( ) ;
var result = rest
. Url ( "[URL]" )
. Command ( "/action" )
. Payload ( new Object { } )
. Put < ResponseObject > ( ) ;
var result = await rest
. Url ( "[URL]" )
. Payload ( new Object { } )
. PutAsync < ResponseObject > ( ) ;
الأسلوب DELETE يحذف المورد المحدد.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. Payload ( new Object { } )
. Delete < ResponseObject > ( ) ;
var result = await Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. Payload ( new Object { } )
. DeleteAsync < ResponseObject > ( ) ;
تقوم طريقة DOWNLOAD بتنزيل المورد المحدد.
var result = Rest . Build ( )
. Download ( "[URL]" ) ;
var result = await rest
. DownloadAsync ( "[URL]" ) ;
من الممكن عرض حالة التنزيل باستخدام OnDownloadProgress.
var rest = Rest . Build ( ) ;
var result = await rest
. OnDownloadProgress ( ( d ) => Console . WriteLine ( $ " { d . CurrentBytes } / { d . TotalBytes } " ) )
. DownloadAsync ( "[URL]" ) ;
نشر إشعار بضرورة إلغاء العمليات.
يتيح CancellationToken الإلغاء التعاوني بين سلاسل الرسائل أو عناصر عمل تجمع مؤشرات الترابط أو كائنات المهام. يمكنك إنشاء رمز مميز للإلغاء عن طريق إنشاء كائن CancellationTokenSource، الذي يدير رموز الإلغاء التي تم استردادها من CancellationTokenSource الخاص به.
يستخدم المثال التالي رمز الإلغاء لإيقاف التنفيذ:
// Define the cancellation token.
CancellationTokenSource source = new CancellationTokenSource ( ) ;
CancellationToken token = source . Token ;
// Schedules a cancel operation on this System.Threading.CancellationTokenSource
// after the specified number of milliseconds
token . CancelAfter ( 3000 ) ;
var file1 = Rest . Build ( ) . DownloadAsync ( "[URL FILE1]" , token . Token ) ;
var file2 = Rest . Build ( ) . DownloadAsync ( "[URL FILE2]" , token . Token ) ;
var get1 = Rest . Build ( ) . Url ( "[URL GET1]" ) . GetAsync < MyObject > ( token . Token ) ;
Task . WaitAll ( file1 , file2 , get1 ) ;
بعد طلب الإلغاء، فإنه يطرح TaskCancellatedException. سيتم تغليف الاستثناء في كائن RestResult.
الأسلوب CUSTOM لتخصيص المورد المحدد.
HttpMethod PATCH = new HttpMethod ( "PATCH" ) ;
var rest = Rest . Build ( ) ;
var result = rest
. Url ( "[URL]" )
. Command ( "/action" )
. Payload ( new Object { } )
. CustomCall < ResponseObject > ( PATCH ) ;
var result = await rest
. Url ( "[URL]" )
. Command ( "/action" )
. Payload ( new Object { } )
. CustomCallAsync < ResponseObject > ( PATCH ) ;
يستخدم RestClient طريقة Playload(obj) لتعيين كائن عند الطلب:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. Payload ( new RequestObject { } )
. Post < ResponseObject > ( ) ;
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. Payload ( new RequestObject { } )
. Put < ResponseObject > ( ) ;
عند الضرورة، يمكننا استخدام الطلب باعتباره عنوان URL مشفرًا للنموذج. لاستخدامه نحتاج إلى تمكينه، مثل هذا:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. EnableFormUrlEncoded ( true )
وبعد ذلك يمكننا تمرير المعلمات كقاموس:
var params = new Dictionary < string , string > ( ) ;
params . Add ( "key1" , "value1" ) ;
params . Add ( "key2" , "value2" ) ;
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. EnableFormUrlEncoded ( true )
. FormUrlEncoded ( params )
. Post ( ) ;
من الممكن تمرير المعلمات داخل المعالج وتمكين عنوان URL المشفر للنموذج:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. FormUrlEncoded ( true , ( p ) =>
{
p . Add ( "key1" , "value1" ) ;
p . Add ( "key2" , "value2" ) ;
} )
. Post ( ) ;
يحدث OnUploadProgress عند تشغيل الطلب وخروج البيانات. يمكننا الحصول على نسبة مئوية من البيانات التي يتم تحميلها مثل هذا:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnUploadProgress ( p =>
{
DoSomethings ( p . ProgressPercentage ) ;
} ) //occurs during request
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
يحدث OnDownloadProgress عند تشغيل الاستجابة وورود البيانات. يمكننا الحصول على نسبة مئوية من البيانات التي يتم تنزيلها على النحو التالي:
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnDownloadProgress ( p =>
{
DoSomethings ( p . ProgressPercentage ) ;
} ) //occurs during response
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
القيمة الافتراضية هي 100.000 مللي ثانية (100 ثانية). لتعيين مهلة لا نهائية، قم بتعيين قيمة الخاصية إلى InfiniteTimeSpan. قد يستغرق استعلام نظام اسم المجال (DNS) ما يصل إلى 15 ثانية للعودة أو انتهاء المهلة. إذا كان طلبك يحتوي على اسم مضيف يتطلب حلاً وقمت بتعيين المهلة على قيمة أقل من 15 ثانية، فقد يستغرق الأمر 15 ثانية أو أكثر قبل طرح استثناء للإشارة إلى انتهاء المهلة في طلبك.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Timeout ( 3200 ) //milliseconds
. Get < ResponseObject > ( ) ;
var result = Rest . Build ( )
. Url ( "[URL]" )
. Timeout ( TimeSpan . FromMinutes ( 10 ) )
. Get < ResponseObject > ( ) ;
OnStart هو حدث يتم تشغيله عند بدء الطلب.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnStart ( ( e ) => {
DoSomethings ( e ) ;
} )
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
OnPreviewContentRequestAsString هو حدث يتم تشغيله عندما يكون الطلب جاهزًا ولم يتم إرساله بعد.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnPreviewContentRequestAsString ( ( e ) => {
DoSomethings ( e ) ;
} )
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
OnPreviewContentResponseAsString هو حدث يتم تشغيله عند تلقي الاستجابة ولم يتم إلغاء تسلسله بعد.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnPreviewContentResponseAsString ( ( e ) => {
DoSomethings ( e ) ;
} )
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
يحدث OnPreResult عندما يكتمل الطلب ولكنه لم يكتمل بعد. عندما يتم رفع OnPreResult يمكننا القيام بشيء ما، على سبيل المثال الحصول على نتيجة الطلب واستخدامها.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnPreCompleted ( ( r ) => {
DoSomethings ( r . Result ) ;
} )
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
يحدث OnException عندما يثير الطلب استثناءً.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnException ( ( e ) => {
DoSomethings ( e ) ;
} )
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
يحدث OnCompleted عند اكتمال الطلب.
var result = Rest . Build ( )
. Url ( "[URL]" )
. Command ( "/action" )
. OnCompleted ( ( e ) => {
DoSomethings ( e ) ;
} )
. Payload ( new BigObject { } )
. Post < ResponseObject > ( ) ;
يسمح RestClient بإنشاء طبقة شبكة مرنة وقوية وهو سهل الاستخدام للغاية. ستجد أدناه عرضًا كاملاً للكود ومثالًا كاملاً للكود.
public class NetworkService {
//building context
public RestBuilder Root ( )
=> Rest . Build ( )
. CertificateValidation ( ( sender , certificate , chain , errors ) =>
{
if ( development ) return true ;
return errors == SslPolicyErrors . None
&& validCerts . Contains ( certificate . GetCertHashString ( ) ) ;
} )
. Url ( "[URL]" ) ;
public RestBuilder RootAuthentication ( )
=> Root ( )
. Authentication ( ( ) => new AuthenticationHeaderValue ( "Bearer" , "[Token]" ) )
. RefreshToken ( )
. RefreshTokenInvoke ( async ( ) => await PostRefreshAsync ( new RefreshRequest { } ) ) ;
public RestBuilder UsersRoot ( )
=> Root ( ) . Command ( "/Users" ) ;
public RestBuilder DimensionsRoot ( )
=> Root ( ) . Command ( "/Dimensions" ) ;
public RestBuilder EventsRoot ( )
=> RootAuthentication ( ) . Command ( "/Events" ) ;
//requests
public async Task < RestResult < LoginResponse > > PostLoginAsync ( LoginRequest request )
=> await UsersRoot ( )
. Command ( "/Login" ) //[URL]/Users/Login
. Payload ( request )
. PostAsync < LoginResponse > ( ) ;
public async Task < RestResult < RuleResponse > > GetRulesAsync ( )
=> await UsersRoot ( )
. Command ( "/Rules" )
. GetAsync < RuleResponse > ( ) ;
public async Task < RestResult < RefreshResponse > > PostRefreshAsync ( RefreshRequest request )
=> await UsersRoot ( )
. Command ( "/Refresh" )
. Payload ( request )
. PostAsync < RefreshResponse > ( ) ;
public async Task < RestResult < CountryResponse > > PostCountriesAsync ( CountryRequest request )
=> await DimensionsRoot ( )
. Command ( "/Countries" )
. Payload ( request )
. PostAsync < CountryResponse > ( ) ;
public async Task < RestResult < EventDetailResponse > > PostEventDetailAsync ( EventDetailRequest request )
=> await EventsRoot ( )
. Command ( "/EventDetail" )
. Payload ( request )
. PostAsync < EventDetailResponse > ( ) ;
}