التقاط بيانات الطلبات وتنقيتها
في بعض الأحيان لا تكون الأخطاء في شيفرة العميل لديك واضحة كما هو الحال عندما تحصل على شاشة فارغة لأن أيًا من شيفرة JavaScript الخاصة بك لا يعمل. وأحيانًا تكون المشكلة في تطبيقك أنه عند بعض الإجراءات التي يمكن أن يتخذها المستخدم، فإنك تُكوِّن طلبًا إلى الخادم بشكل غير صحيح. شيفرة العميل تعمل، ولا تحصل على أي أخطاء JS في وحدة التحكم، لكن الواجهة الخلفية لديك لا تستلطف حقًا ما ترسله إليها.
باستخدام OpenReplay، يمكنك التقاط الاتصال بين العميل والخادم كجزء من إعادة تشغيل الجلسة القياسية لديك، ومراجعته لاحقًا. لذا دعونا نلقي نظرة على كيفية القيام بذلك وأي نوع من الفائدة يمكننا الحصول عليه منه.
تطبيق نموذجي
Section titled تطبيق نموذجيلأغراض هذا الدليل الإرشادي، أنشأتُ تطبيق React بسيطًا يستخدم Bored API. وهذه واجهة برمجة تطبيقات بسيطة جدًا تُرجع اقتراح نشاط عشوائيًا بناءً على بعض المعاملات. لذا أنشأتُ “I’m bored App”، الذي يبدو هكذا:

ويمكنك أن تجده مباشرًا على Netlify من هنا، أو إذا أردت الاطلاع على الشيفرة لتفحصها بالتفصيل، فهي متاحة بالكامل على GitHub.
يتكوَّن هذا التطبيق من مكوِّنين، المكوِّن SearchForm الذي يتولى عرض هذين الحقلين والزر، إضافةً إلى إرسال الطلب الفعلي إلى الواجهة البرمجية.
أما المكوِّن Suggestion فيقوم ببساطة بعرض الاقتراح داخل صندوق ذي مظهر جميل.
سأركّز على المكوِّن الأول، لأنه الوحيد الذي يرسل طلبات باستخدام الدالة fetch.
لاحظ أن هذه التقنية تعمل أيضًا مع أي طلب يُنفَّذ باستخدام Axios كذلك.
دعونا نلقي نظرة سريعة على المكوِّن لفهم ما يقوم به.
شيفرة المكوِّن SearchForm
Section titled شيفرة المكوِّن SearchFormهذا ليس مكوِّنًا معقدًا، لكن هناك قسمًا وثيق الصلة بشكل خاص بهذه الحالة الاستخدامية تحديدًا، لذا دعونا نلقي عليه نظرة سريعة.
import { Container, Col, Form, Row, Button } from 'react-bootstrap';
const SearchForm = ({setResult, fetcher}) => {
const getSomething = async (evt) => {
evt.preventDefault()
let form = evt.target
const API_URL = "/api/activity?"
let getParams = {}
if(form.participants.value !== '') {
getParams.participants = form.participants.value
}
if(form.priceRange.value !== '') {
let prices = form.priceRange.value.split("_")
getParams.minprice = prices[0]
getParams.maxprice = prices[1]
}
let results = await fetcher(API_URL + new URLSearchParams(getParams), {
mode: 'no-cors'
})
setResult(await results.json())
return false
}
return (
<Container>
<Form onSubmit={getSomething}>
<Row>
<Col>
<Form.Group controlId='participants' >
<Form.Label>Participants</Form.Label>
<Form.Control type='text' name="totalParticipants" placeholder='Leave empty if you dont care...'></Form.Control>
</Form.Group>
</Col>
<Col>
<Form.Group controlId='priceRangeId'>
<Form.Label>Price range</Form.Label>
<Form.Select name="priceRange" >
<option value="" >Select one or leave empty if you dont care</option>
<option value="0.0">Free</option>
<option value="0.1_0.5">Cheap</option>
<option value="0.6_1.0">Expensive</option>
</Form.Select>
</Form.Group>
</Col>
</Row>
<Row className='m-3'>
<Col>
<Form.Group>
<Button variant="primary" type="submit">Get me something!</Button>
</Form.Group>
</Col>
</Row>
</Form>
</Container>
)
}
export default SearchForm
لاحظ الدالة getSomething، فهنا يحدث معظم السحر. تُستدعى هذه الدالة عندما يُطلَق حدث submit الخاص بالنموذج.
وعندما يحدث ذلك، تحصل الدالة على الحدث الاصطناعي مع النموذج المرتبط داخل الخاصية target. نقوم ببساطة بالتقاط القيم من كل من المرشِّحات (حقل الإدخال والقائمة المنسدلة) ثم ننفِّذ الطلب باستخدام الدالة fetch.
لاحظ أن عنوان URL لا يستهدف مباشرةً نقطة النهاية الخاصة بـ BoredAPI. وذلك لأنه، حتى يعمل الطلب ولا يُحظَر بسبب قيود CORS، فقد قمتُ بتهيئة وكيل (proxy) على الواجهة الخلفية لإعادة توجيه جميع الطلبات من /api إلى الواجهة البرمجية الفعلية.
والآن بعد أن رأيتَ الشيفرة، دعونا نلقي نظرة على ما ستحصل عليه إذا قمتَ بتثبيت متعقِّب OpenReplay من دون إضافة fetch.
التقاط البيانات الاعتيادي مع OpenReplay
Section titled التقاط البيانات الاعتيادي مع OpenReplayفي هذا المثال، سأستخدم نسخة NPM من الحزمة؛ إذا كنت لا تعرف كيفية القيام بذلك، فاطّلع على الوثائق ثم عُد إلى هنا.

هذه هي واجهة إعادة تشغيل الجلسة افتراضيًا. لاحظ كيف أنني في النصف السفلي قد حدَّدتُ بالفعل علامة التبويب “Network”، لكنها رغم أنها تُظهر الطلبات التي يجري إجراؤها، فإنها لا تتضمن أي تفاصيل عنها. وحتى إذا نقرتَ على أحدها، فستحصل على الحد الأدنى من التفاصيل المتاحة:

فما الذي يمكننا فعله إذًا؟ يمكنك تمكين التقاط معلومات الطلب باستخدام كائن خيارات الشبكة. دعونا ننظر في ذلك.
التقاط بيانات الطلبات في عمليات إعادة تشغيل الجلسات
Section titled التقاط بيانات الطلبات في عمليات إعادة تشغيل الجلساتمن أجل ذلك، كل ما علينا فعله هو إضافة خيار تهيئة عند إنشاء نسخة من المتعقِّب.
لذا الآن، عندما تكتب سطر new tracker(...)، ستضيف خاصية جديدة:
import Tracker from '@openreplay/tracker';
const tracker = new Tracker({
projectKey: "<your project key>",
network: {
capturePayload: true //start capturing the payload of every request
}
});
هذا كل ما نحتاج إلى فعله، فمن الآن فصاعدًا، في كل مرة تُجري فيها طلبًا، سيُسجِّل المتعقِّب البيانات. الآن انشر التغيير، واختبر التطبيق، وأغلق علامة التبويب وانتظر دقيقتين. ينبغي أن تظهر الجلسة قريبًا بما يكفي ويمكنك الضغط على زر “play”.
فحص الاتصال بين العميل والخادم
Section titled فحص الاتصال بين العميل والخادملأغراض المثال، دعونا ننظر أيضًا في مشكلة بدأتُ ألاحظها بعد أن نشرتُ التطبيق.
لاحظ صندوق التحذير الذي أحصل عليه في هذه الحالة:

بصفتي المطوِّر الذي برمج هذا، أعرف ما يجب فعله لاختباره وفهم أين يكمن الخلل. لكن بصفتي مستخدمًا، فإن الخطأ لا يخبرني في الواقع بالكثير، وقد لا أكون قادرًا على إيصال ذلك بطريقة يمكن لفريق التطوير فهمها. لذا بدلًا من ذلك، بصفتي مستخدمًا، يمكنني ببساطة أن أشكو إلى الشركة من أن تطبيقها لا يعمل، ويمكنك أنت، بصفتك المطوِّر المسؤول عن التطبيق، أن تلقي نظرة على جلستي وتفحص الطلب الذي أرسله العميل والاستجابة الواردة من الخادم.

انظر إلى واجهة إعادة تشغيل الجلسة الآن. داخل علامة التبويب Network يمكنك رؤية الطلبات التي ما فتئنا نجريها إلى الواجهة البرمجية الخارجية.
كل ما علينا فعله الآن هو إيجاد اللحظة التي نحصل فيها على استجابة الخطأ والنظر إلى الطلبات التي يجري إجراؤها. على الأرجح أنك سترى المشكلة داخل تفاصيل الطلب. في حالتنا، يقول الخطأ “Failed to query due to error in arguments”، ما يعني أنه عندما نختار الخيار “Free” في القائمة المنسدلة، فإننا لا نرسل طلبًا صالحًا. لذا دعونا نلقي نظرة على تفاصيله.

هل ترى المشكلة؟ دعني أساعدك:

نعم، أنا أرسل قيمة undefined كقيمة للخاصية maxprice. لقد أغفلتُ ذلك تمامًا في منطقي، والتقطتُه أثناء فحص الطلب.
صحيح أن إصلاحه سهل الآن بعد أن عرفتُ أين تكمن المشكلة، لكن بفضل هذه العملية كنتُ سأتمكن إما من رفع تقرير خطأ مفصَّل جدًا، أو من مساعدة المطوِّر مباشرةً على تحديد المشكلة وحلها من دون الحاجة إلى اختبارها بنفسي وإعادة إنتاج الحادثة.
وضع الخصوصية على المحك
Section titled وضع الخصوصية على المحكحسنًا، دعونا نأخذ هذا المثال إلى أبعد قليلًا؛ دعونا نتظاهر بأنني أحتاج أيضًا إلى رقم هاتف المستخدم من أجل هذا الطلب. من الواضح أنني لا أحتاج إليه، لكن جارِني للحظة.
سأضيف الحقل إلى النموذج، وسأحدِّث الشيفرة لالتقاط تلك القيمة وإرسالها كجزء من الطلب.
أما HTML الخاص بالنموذج فهو مجرد إضافة عنصر Col جديد على النحو التالي:
<!-- previous code -->
<Col>
<Form.Group controlId='phoneNumber'>
<Form.Label>Phone Number</Form.Label>
<Form.Control type='number' name="phoneNumber" placeholder='Enter your phone number here please'></Form.Control>
</Form.Group>
</Col>
<!-- rest of the code -->
وإضافة محتويات هذا الحقل إلى الطلب الفعلي لا تحتاج سوى إلى سطر واحد من الشيفرة:
getParams.phonenumber = form.phoneNumber.value
والآن، ماذا يحدث إذا استخدمنا هذه الشيفرة الجديدة والتقطنا جلسة باستخدام OpenReplay؟ حسنًا، يحدث شيئان:
- إن عملية إعادة التشغيل التي تشاهدها ستقوم تلقائيًا بتنقية محتوى حقل رقم الهاتف ولن يُعرَض لأي شخص يشاهده.
- لكن معلومات الطلب التي التقطتها الإضافة ستُظهر القيمة.
تُظهر لقطة الشاشة التالية ما وصفته للتو:

في الجزء الأيمن من الشاشة، يمكنك رؤية رقم الهاتف كاملًا. يحدث هذا لأنه في حين أن المتعقِّب العادي يمكنه فهم أن حقل رقم الهاتف هو حقل رقمي، فإنه لن يلتقط ما يُدخَل فيه تحسبًا لأن يمثِّل الرقم معلومات شخصية. لكن على جانب الطلب، لا يمكننا في الواقع وضع هذا الافتراض، إذ كان بإمكان المطوِّر أن يفعل أي شيء بالبيانات، أو حتى باسم المعامل. فالسؤال إذًا هو: هل يمكننا حماية خصوصية مستخدمنا باستخدام هذه الإضافة؟
والجواب، ويسرّني أن أُبلّغ به، هو: نعم نستطيع.
تنقية بيانات الطلب
Section titled تنقية بيانات الطلبإذا عدتَ إلى بداية هذا الدليل، عندما هيّأتُ خيارات الشبكة، فسترى أنني لم أذكر أي شيء عن التنقية. لكن، كجزء من تلك الخيارات، يمكنك تحديد دالة استدعاء راجعة (callback) مخصَّصة لتنقية البيانات. تتلقى دالة الاستدعاء هذه خاصية واحدة تحتوي على كل من كائن الطلب وكائن الاستجابة. ويمكنك بعد ذلك أن تختار تعديلهما كيفما تشاء؛ فهما لن يؤثرا في الطلب الفعلي ولكنهما سيغيّران الطريقة التي تُعرَض بها البيانات في واجهة OpenReplay.
على سبيل المثال، لنقل إنني أريد تغيير الخاصية “phonenumber” وإزالة الأرقام منها حتى نتجنب تسريب تلك المعلومات. يمكن القيام بذلك على النحو التالي:
const tracker = new Tracker({
projectKey: "<your project id>",
network: {
capturePayload: true,
sanitizer: (data) => { //we change the content of the "phonenumber" parameter from the url
data.url = data.url.replace(/phonenumber=([0-9]+)/, "phonenumber=XXXXXX")
return data
}
}
});
كما ترى، التغيير بسيط، فنحن نستبدل الأرقام فقط في هذه الخاصية، لذا يبدو الطلب الآن هكذا في واجهتنا:

والآن أصبحت بيانات مستخدمك آمنة مرة أخرى.
هل لديك أسئلة؟
Section titled هل لديك أسئلة؟إذا أردت الاطلاع على الشيفرة لتفحص هذا المثال بالتفصيل، فيمكنك أن تجدها هنا على GitHub. إذا واجهتَ أي مشكلات في إعداد إضافة Fetch أو المتعقِّب نفسه، فيُرجى التواصل معنا عبر مجتمعنا على Slack وطرح أسئلتك على مطوِّرينا مباشرةً!