بطريقة ما ، كان لدي بضعة أيام راحة ، وقمت برسم خادم GraphQL إلى منصة Docsvision. أدناه سأخبرك كيف سارت الأمور.

تتضمن منصة Docsvision العديد من الأدوات المختلفة لبناء أنظمة سير العمل ، ولكن المكون الرئيسي هو شيء مثل ORM. يوجد محرر بيانات وصفية يمكنك فيه وصف هيكل حقول البطاقة. قد تكون هناك أقسام هيكلية وتجميعية وأشجار ، والتي ، علاوة على ذلك ، يمكن أن تتداخل ، بشكل عام ، كل شيء معقد . يتم إنشاء قاعدة بيانات عن طريق البيانات الوصفية ، وبعد ذلك يمكنك العمل معها من خلال بعض C # API. في كلمة واحدة - خيار مثالي لبناء خادم GraphQL.
ما هي الخيارات
بصراحة ، ليس هناك الكثير من الخيارات وهي كذلك. تمكنت من العثور على مكتبتين فقط:
محدث: في التعليقات اقترحوا أنه لا يزال هناك شوكولاتة ساخنة .
في README ، في البداية أحببت الثانية ، حتى أنني بدأت في فعل شيء معها. لكنه سرعان ما اكتشف أن واجهة برمجة التطبيقات الخاصة بها كانت سيئة للغاية ، ولم تتمكن من التعامل مع مهمة إنشاء مخطط البيانات الوصفية. ومع ذلك ، يبدو أنه تم التخلي عنها بالفعل (آخر التزام قبل عام).
واجهة برمجة تطبيقات graphql-dotnet
مرنة للغاية ، ولكنها في نفس الوقت موثقة بشكل رهيب ومربك وغير بديهي. لفهم كيفية عمل معه، وكان لي أن أرى شفرة المصدر ... ومع ذلك، لقد عملت مع النسخة 0.16
، في حين الآن أحدث 0.17.3
، وأصدرت بالفعل 7 بيتا النسخة 2.0
. لذلك أنا آسف إذا كانت المادة قديمة بعض الشيء.
يجب أن أذكر أيضًا أن المكتبات تأتي مع مجموعات غير موقعة. كان علي إعادة بنائها من المصدر يدويًا لاستخدامها في تطبيق ASP.NET الخاص بنا مع التجميعات الموقعة.
هيكل خادم GraphQL
إذا لم تكن على دراية بـ GraphQL ، يمكنك تجربة مستكشف github . سر صغير - يمكنك الضغط على Ctrl + space للحصول على الإكمال التلقائي. جزء العميل ليس هناك أكثر من GraphiQL ، والتي يمكن ثملها بسهولة على الخادم الخاص بك. ما عليك سوى أخذ index.html وإضافة نصوص برمجية من حزمة npm وتغيير عنوان url في وظيفة GraphQLFetcher إلى عنوان خادمك - هذا كل ما يمكنك لعبه.
فكر في استعلام بسيط:
query { viewer { login, company } }
هنا نرى مجموعة من الحقول - عارض ، في تسجيل الدخول ، شركة. مهمتنا ، مثل الواجهة الرسومية GraphQL ، هي بناء "مخطط" على الخادم يتم فيه معالجة جميع هذه الحقول. في الواقع ، نحن بحاجة فقط إلى إنشاء الهيكل المناسب لكائنات الخدمة مع وصف الحقول ، وتحديد وظائف رد الاتصال لحساب القيم.
يمكن إنشاء المخطط تلقائيًا استنادًا إلى فئات C # ، ولكننا سنمر بالمتشددين - سنفعل كل شيء بأيدينا. ولكن هذا ليس لأنني رجل متهور ، مجرد إنشاء مخطط قائم على البيانات الوصفية هو نص غير قياسي في Graphql-dotnet غير مدعوم بالوثائق الرسمية. لذا ، نحفر قليلاً في أحشائها ، في منطقة غير موثقة.
بعد إنشاء النظام ، يبقى لنا تسليم سلسلة الطلب (والمعلمات) من العميل إلى الخادم بأي طريقة مناسبة (لا يهم كيفية GET و POST و SignalR و TCP ...) ، وتغذية محركها جنبًا إلى جنب مع المخطط. سيقوم المحرك بصق كائن بنتيجة نتحول إلى JSON ونعيده إلى العميل. بدا لي مثل هذا:
// , var schema = GraphQlService.GetCardsSchema(sessionContext); // ( ) var executer = new DocumentExecuter(); // , var dict = await executer.ExecuteAsync(schema, sessionContext, request.Query, request.MethodName).ConfigureAwait(false); // - :) if (dict.Errors != null && dict.Errors.Count > 0) { throw new InvalidOperationException(dict.Errors.First().Message); } // return Json(dict.Data);
يمكنك الانتباه إلى sessionContext
. هذا هو كائن Docsvision الخاص بنا والذي يتم من خلاله الوصول إلى النظام الأساسي. عند إنشاء مخطط ، نعمل دائمًا مع سياق معين ، ولكن أكثر في ذلك لاحقًا.
توليد الدائرة
يبدأ كل شيء بطريقة مؤثرة:
Schema schema = new Schema();
للأسف ، هذا هو المكان الذي ينتهي فيه الرمز البسيط. من أجل إضافة حقل إلى المخطط ، نحتاج إلى:
- وصف نوعه - إنشاء كائن ObjectGraphType أو StringGraphType أو BooleanGraphType أو IdGraphType أو IntGraphType أو DateGraphType أو FloatGraphType.
- وصف الحقل نفسه (الاسم ، المعالج) - إنشاء كائن GraphQL.Types.FieldType
دعونا نحاول وصف هذا الطلب البسيط الذي ذكرته أعلاه. في الطلب ، لدينا عارض حقل واحد. لإضافته إلى استعلام ، يجب عليك أولاً وصف نوعه. نوعه بسيط - كائن ، مع حقلين سلسلة - تسجيل الدخول والشركة. نحن نصف مجال تسجيل الدخول:
var loginField = new GraphQL.Types.FieldType(); loginField.Name = "login"; loginField.ResolvedType = new StringGraphType(); loginField.Type = typeof(string); loginField.Resolver = new MyViewerLoginResolver();
نقوم بإنشاء كائن companyField بنفس الطريقة - ممتاز ، نحن على استعداد لوصف نوع حقل العارض.
ObjectGraphType<UserInfo> viewerType = new ObjectGraphType<UserInfo>(); viewerType.Name = "Viewer"; viewerType.AddField(loginField); viewerType.AddField(companyField);
يوجد نوع ، والآن يمكننا وصف حقل العارض نفسه:
var viewerField = new GraphQL.Types.FieldType(); viewerField.Name = "viewer"; viewerField.ResolvedType = viewerType; viewerField.Type = typeof(UserInfo); viewerField.Resolver = new MyViewerResolver();
حسنًا ، واللمسة الأخيرة ، أضف حقلنا إلى نوع الاستعلام:
var queryType = new ObjectGraphType(); queryType.AddField(viewerField); schema.Query = queryType;
هذا كل شيء ، مخططنا جاهز.
مجموعات ، ترقيم الصفحات ، معالجة المعلمات
إذا لم يعيد الحقل كائنًا واحدًا ، بل مجموعة ، فأنت بحاجة إلى تحديد ذلك صراحة. للقيام بذلك ، ما عليك سوى لف نوع الخاصية في مثيل لفئة ListGraphType. لنفترض أنه إذا أعاد عارض مجموعة ، فسنكتب ببساطة ما يلي:
وفقًا لذلك ، في محلل MyViewerResolver ، سيكون من الضروري إعادة القائمة.
عند ظهور حقول التجميع ، من المهم الاهتمام بالترحيل على الفور. لا توجد آلية جاهزة هنا ، ويتم كل شيء من خلال المعلمات . يمكنك ملاحظة مثال على استخدام المعلمة في المثال أعلاه (يحتوي cardDocument على معلمة معرف). دعنا نضيف مثل هذه المعلمة إلى العارض:
var idArgument = new QueryArgument(typeof(IdGraphType)); idArgument.Name = "id"; idArgument.ResolvedType = new IdGraphType(); idArgument.DefaultValue = Guid.Empty; viewerField.Arguments = new QueryArguments(idArgument);
ثم يمكنك الحصول على قيمة المعلمة في المحلل كما يلي:
public object Resolve(ResolveFieldContext context) { var idArgStr = context.Arguments?["id"].ToString() ?? Guid.Empty.ToString(); var idArg = Guid.Parse(idArgStr);
يتم كتابة GraphQL بحيث لا يستطيع الدليل ، بطبيعة الحال ، التحليل. حسنًا ، حسنًا ، ليس الأمر صعبًا علينا.
طلب بطاقة Docsvision
في تنفيذ GrapqhQL لمنصة Docsvision ، وبناءً على ذلك ، أذهب ببساطة من خلال رمز البيانات الوصفية ( sessionContext.Session.CardManager.CardTypes
) ، وبالنسبة لجميع البطاقات وأقسامها ، أقوم تلقائيًا بإنشاء مثل هذه الكائنات باستخدام sessionContext.Session.CardManager.CardTypes
. والنتيجة شيء مثل هذا:
query { cardDocument(id: "{AF652E55-7BCF-E711-8308-54A05079B7BF}") { mainInfo { name instanceID } } }
هنا cardDocument هو نوع البطاقة ، mainInfo هو اسم القسم الموجود فيه ، والاسم و exampleID هي الحقول في القسم. يستخدم المحللون المقابلون للبطاقة والمقطع والحقل واجهة برمجة تطبيقات CardManager كما يلي:
class CardDataResolver : GraphQL.Resolvers.IFieldResolver { public object Resolve(ResolveFieldContext context) { var sessionContext = (context.Source as SessionContext); var idArg = Guid.Parse(context.Arguments?["id"].ToString() ?? Guid.Empty.ToString()); return sessionContext.Session.CardManager.GetCardData(idArg); } } class SectionResolver : GraphQL.Resolvers.IFieldResolver { CardSection section; public SectionFieldResolver(CardSection section) { this.section = section; } public object Resolve(ResolveFieldContext context) { var idArg = Guid.Parse(context.Arguments?["id"].ToString() ?? Guid.Empty.ToString()); var skipArg = (int?)context.Arguments?["skip"] ?? 0; var takeArg = (int?)context.Arguments?["take"] ?? 15; var sectionData = (context.Source as CardData).Sections[section.Id]; return idArg == Guid.Empty ? sectionData.GetAllRows().Skip(skipArg).Take(takeArg) : new List<RowData> { sectionData.GetRow(idArg) }; } } class RowFieldResolver : GraphQL.Resolvers.IFieldResolver { Field field; public RowFieldResolver(Field field) { this.field = field; } public object Resolve(ResolveFieldContext context) { return (context.Source as RowData)[field.Alias]; } }
بالطبع ، يمكنك هنا طلب البطاقات فقط عن طريق الهوية ، ولكن من السهل إنشاء مخطط بنفس الطريقة للوصول إلى التقارير والخدمات المتقدمة وأي شيء آخر. باستخدام واجهة برمجة التطبيقات هذه ، يمكنك الحصول على أي بيانات من قاعدة بيانات Docsvision ببساطة عن طريق كتابة جافا سكريبت المناسبة - فهي ملائمة جدًا لكتابة النصوص البرمجية والإضافات الخاصة بك.
الخلاصة
مع GrapqhQL في .NET ، فإن الأمور ليست سهلة. هناك مكتبة حيوية إلى حد ما ، بدون مورد موثوق به ومستقبل غير مفهوم ، واجهة برمجة تطبيقات غير مستقرة وغريبة ، غير معروفة في كيفية تصرفها تحت الحمل ومدى استقرارها. ولكن لدينا ما لدينا ، يبدو أنه يعمل ، لكن العيوب في الوثائق والباقي يعوضها انفتاح شفرة المصدر.
ما وصفته في هذه المقالة هو واجهة برمجة تطبيقات غير موثقة بشكل متزايد ، والتي استكشفتها من خلال كتابة المصدر ودراسته. فقط لم يعتقد مؤلفو المكتبة أن شخصًا ما سيحتاج إلى إنشاء الدائرة تلقائيًا - حسنًا ، ماذا يمكنك أن تفعل ، هذا مفتوح المصدر.
لقد كُتب كل هذا خلال عطلة نهاية الأسبوع ، ومن تلقاء نفسه ، حتى الآن ليس أكثر من نموذج أولي. في حزمة Docsvision القياسية ، من المحتمل أن يظهر هذا ، ولكن عندما - لا يزال من الصعب القول. ومع ذلك ، إذا كنت ترغب في فكرة الوصول إلى قاعدة بيانات Docsvision مباشرة من JavaScrpit دون كتابة ملحقات الخادم ، فاكتب. وكلما زاد اهتمام الشركاء ، زاد الاهتمام بهذا الأمر.