پس زمینه
کارگران خدمات به توسعه دهندگان وب این توانایی را می دهند که به درخواست های شبکه ای که توسط برنامه های کاربردی وب آنها ارائه می شود پاسخ دهند، به آنها اجازه می دهد حتی در حالت آفلاین به کار خود ادامه دهند، با lie-fi مبارزه کنند و تعاملات حافظه پنهان پیچیده مانند stale-while-revalidate را پیاده سازی کنند. اما کارگران خدمات از لحاظ تاریخی به یک منشاء خاص گره خورده اند - به عنوان صاحب یک برنامه وب، این مسئولیت شماست که یک سرویس دهنده را بنویسید و مستقر کنید تا تمام درخواست های شبکه را که برنامه وب شما می کند را رهگیری کند. در آن مدل، هر سرویسکار مسئول رسیدگی به درخواستهای متقاطع است، برای مثال به یک API شخص ثالث یا فونتهای وب.
اگر یک ارائهدهنده شخص ثالث یک API، یا فونتهای وب، یا سایر سرویسهای معمولی این قدرت را داشته باشد که سرویسکار خود را که فرصت رسیدگی به درخواستهای ارسالشده توسط منابع دیگر به مبدأ خود را داشته باشد، داشته باشد؟ ارائهدهندگان میتوانند منطق شبکه سفارشی خود را پیادهسازی کنند و از یک نمونه کش معتبر برای ذخیره پاسخهای خود استفاده کنند. اکنون، به لطف واکشی خارجی ، این نوع استقرار کارگر خدمات شخص ثالث یک واقعیت است.
استقرار یک سرویسکار که واکشی خارجی را پیادهسازی میکند برای هر ارائهدهنده سرویسی که از طریق درخواستهای HTTPS از مرورگرها به آن دسترسی پیدا میکند منطقی است—فقط به سناریوهایی فکر کنید که در آن میتوانید یک نسخه مستقل از شبکه از سرویس خود را ارائه دهید، که در آن مرورگرها میتوانند از مزایای یک سرویس استفاده کنند. کش منابع رایج خدماتی که می توانند از این مزیت بهره مند شوند عبارتند از:
- ارائه دهندگان API با رابط های RESTful
- ارائه دهندگان فونت وب
- ارائه دهندگان تجزیه و تحلیل
- ارائه دهندگان میزبانی تصویر
- شبکه های تحویل محتوای عمومی
به عنوان مثال، تصور کنید که شما یک ارائه دهنده تجزیه و تحلیل هستید. با استقرار یک کارگر خدمات واکشی خارجی، میتوانید اطمینان حاصل کنید که تمام درخواستهای سرویس شما که در زمانی که کاربر آفلاین است با شکست مواجه میشوند، در صف قرار میگیرند و پس از بازگشت اتصال دوباره پخش میشوند. در حالی که این امکان برای مشتریان یک سرویس وجود دارد که رفتار مشابهی را از طریق کارمندان خدمات شخص اول اجرا کنند ، الزام هر مشتری برای نوشتن منطق سفارشی برای سرویس شما به اندازه تکیه بر یک کارگر خدمات واکشی خارجی مشترکی که شما مستقر می کنید، مقیاس پذیر نیست.
پیش نیازها
نشانه آزمایشی مبدا
واکشی خارجی هنوز آزمایشی در نظر گرفته می شود. برای اینکه این طرح قبل از اینکه به طور کامل مشخص شود و توسط فروشندگان مرورگر مورد توافق قرار گیرد، پیش از موعد پخته نشود، در Chrome 54 بهعنوان نسخه آزمایشی اصلی پیادهسازی شده است. تا زمانی که واکشی خارجی آزمایشی باقی بماند، برای استفاده از این ویژگی جدید با سرویسی که میزبانی میکنید، باید توکنی را درخواست کنید که با مبدأ خاص سرویس شما مطابقت داشته باشد. این توکن باید به عنوان یک سرصفحه پاسخ HTTP در تمام درخواستهای متقاطع برای منابعی که میخواهید از طریق واکشی خارجی مدیریت کنید، و همچنین در پاسخ منبع جاوا اسکریپت کارگر سرویس شما گنجانده شود:
Origin-Trial: token_obtained_from_signup
آزمایشی در مارس 2017 به پایان می رسد. در آن مرحله، ما انتظار داریم که تغییرات لازم برای تثبیت ویژگی را مشخص کرده و (امیدواریم) آن را به طور پیش فرض فعال کنیم. اگر تا آن زمان واکشی خارجی بهطور پیشفرض فعال نشده باشد، عملکرد مرتبط با توکنهای آزمایشی مبدا دیگر کار نخواهد کرد.
برای تسهیل آزمایش واکشی خارجی قبل از ثبت نام برای یک نشانه رسمی آزمایشی اولیه، میتوانید با رفتن به chrome://flags/#enable-experimental-web-platform-features
و فعال کردن " از الزامات Chrome برای رایانه محلی خود عبور کنید. ویژگیهای پلتفرم وب آزمایشی" پرچم. لطفاً توجه داشته باشید که این کار باید در هر نمونه از Chrome که میخواهید در آزمایشهای محلی خود استفاده کنید انجام شود، در حالی که با یک نشانه آزمایشی مبدا، این ویژگی برای همه کاربران Chrome شما در دسترس خواهد بود.
HTTPS
مانند تمام استقرارهای سرویسکار، سرور وب که برای سرویس دادن به منابع و اسکریپت کارگر سرویس خود استفاده میکنید باید از طریق HTTPS قابل دسترسی باشد . علاوه بر این، رهگیری واکشی خارجی فقط برای درخواستهایی اعمال میشود که از صفحات میزبانی شده در مبدأ امن نشات میگیرند، بنابراین مشتریان سرویس شما باید از HTTPS استفاده کنند تا از اجرای واکشی خارجی شما استفاده کنند.
استفاده از واکشی خارجی
با وجود پیشنیازها، بیایید جزئیات فنی مورد نیاز برای راهاندازی و راهاندازی یک کارگر خدمات واکشی خارجی را بررسی کنیم.
ثبت نام کارگر خدماتی
اولین چالشی که احتمالاً با آن برخورد خواهید کرد این است که چگونه کارمند خدمات خود را ثبت نام کنید. اگر قبلاً با کارگران خدماتی کار کرده اید، احتمالاً با موارد زیر آشنا هستید:
// You can't do this!
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js');
}
این کد جاوا اسکریپت برای ثبت نام کارمند خدمات شخص اول در زمینه یک برنامه وب منطقی است که توسط کاربر در حال پیمایش به آدرس اینترنتی شما کنترل می شود. اما زمانی که تنها مرورگر تعاملی که با سرور شما خواهد داشت، درخواست یک منبع فرعی خاص است، نه یک ناوبری کامل، این یک رویکرد مناسب برای ثبت نام یک سرویس کار شخص ثالث نیست. اگر مرورگر، مثلاً، تصویری را از یک سرور CDN که شما نگهداری میکنید درخواست کند، نمیتوانید آن قطعه جاوا اسکریپت را به پاسخ خود اضافه کنید و انتظار داشته باشید که اجرا شود. روش متفاوتی برای ثبت نام کارگر سرویس، خارج از زمینه اجرای عادی جاوا اسکریپت، مورد نیاز است.
راه حل به شکل یک هدر HTTP است که سرور شما می تواند در هر پاسخی شامل شود:
Link: </service-worker.js>; rel="serviceworker"; scope="/"
بیایید آن هدر مثال را به اجزای آن تقسیم کنیم، که هر کدام با یک ;
شخصیت
-
</service-worker.js>
مورد نیاز است و برای تعیین مسیر فایل Service Worker شما استفاده می شود (به جای/service-worker.js
مسیر مناسب اسکریپت خود را جایگزین کنید). این به طور مستقیم با رشتهscriptURL
مطابقت دارد که در غیر این صورت به عنوان اولین پارامتر بهnavigator.serviceWorker.register()
ارسال می شود. مقدار باید در نویسههای<>
محصور شود (همانطور که توسط مشخصات سرصفحهLink
مورد نیاز است)، و اگر URL نسبی به جای مطلق ارائه شود، به عنوان نسبی به محل پاسخ تفسیر میشود. -
rel="serviceworker"
نیز مورد نیاز است، و باید بدون نیاز به سفارشی سازی گنجانده شود. -
scope=/
یک اعلان دامنه اختیاری است، معادل رشتهoptions.scope
که می توانید به عنوان پارامتر دوم بهnavigator.serviceWorker.register()
ارسال کنید. برای بسیاری از موارد استفاده، شما با استفاده از محدوده پیشفرض مشکلی ندارید ، بنابراین به راحتی آن را کنار بگذارید، مگر اینکه بدانید به آن نیاز دارید. همان محدودیتها در مورد حداکثر محدوده مجاز، همراه با توانایی کاهش آن محدودیتها از طریق هدرService-Worker-Allowed
، برای ثبتهای هدرLink
اعمال میشود.
درست مانند ثبت نام کارگر سرویس "سنتی"، با استفاده از هدر Link
یک سرویس کار نصب می شود که برای درخواست بعدی که در مقابل محدوده ثبت شده ارائه می شود، استفاده می شود. بدنه پاسخ که شامل هدر ویژه میشود همانطور که هست استفاده میشود و فوراً در دسترس صفحه قرار میگیرد، بدون اینکه منتظر بماند تا کارمند خدمات خارجی نصب را تمام کند.
به یاد داشته باشید که واکشی خارجی در حال حاضر بهعنوان آزمایشی مبدأ اجرا میشود، بنابراین در کنار سرصفحه پاسخ پیوند خود، باید یک سرصفحه Origin-Trial
معتبر نیز قرار دهید. حداقل مجموعه ای از هدرهای پاسخ برای اضافه کردن به منظور ثبت نام کارگر خدمات واکشی خارجی شما است
Link: </service-worker.js>; rel="serviceworker"
Origin-Trial: token_obtained_from_signup
اشکال زدایی ثبت نام
در طول توسعه، احتمالاً می خواهید تأیید کنید که کارگر خدمات واکشی خارجی شما به درستی نصب شده و درخواست ها را پردازش می کند. چند چیز وجود دارد که میتوانید در ابزارهای برنامهنویس Chrome بررسی کنید تا تأیید کنید که کارها همانطور که انتظار میرود کار میکنند.
آیا سرصفحه های پاسخ مناسب ارسال می شوند؟
برای ثبت نام کارگر خدمات واکشی خارجی، باید یک هدر پیوند را روی پاسخ به منبعی که در دامنه شما میزبانی شده است، تنظیم کنید، همانطور که قبلا در این پست توضیح داده شد. در طول دوره Origin Trial، و با فرض اینکه chrome://flags/#enable-experimental-web-platform-features
مجموعه ای ندارید، همچنین باید یک سرصفحه پاسخ Origin-Trial
تنظیم کنید. میتوانید تأیید کنید که سرور وب شما این هدرها را با نگاه کردن به ورودی در پانل شبکه DevTools تنظیم میکند:
آیا کارگر خدمات واکشی خارجی به درستی ثبت نام کرده است؟
همچنین میتوانید با مشاهده فهرست کامل سرویسکاران در پنل برنامه DevTools، ثبتنام سرویسکار اساسی، از جمله دامنه آن را تأیید کنید. مطمئن شوید که گزینه "نمایش همه" را انتخاب کنید، زیرا به طور پیش فرض، شما فقط کارگران خدمات را برای مبدا فعلی می بینید.
کنترل کننده رویداد نصب
اکنون که سرویسکار شخص ثالث خود را ثبت کردهاید، مانند هر سرویسکار دیگری، فرصتی برای پاسخ به رویدادهای install
و activate
خواهد داشت. میتواند از این رویدادها برای پر کردن حافظههای پنهان با منابع مورد نیاز در طول رویداد install
یا حذف کشهای قدیمی در رویداد activate
استفاده کند.
فراتر از فعالیتهای ذخیره رویداد install
معمولی، یک مرحله اضافی وجود دارد که در کنترل کننده رویداد install
توسط کارگر خدمات شخص ثالث شما لازم است. کد شما باید registerForeignFetch()
فراخوانی کند، مانند مثال زیر:
self.addEventListener('install', event => {
event.registerForeignFetch({
scopes: [self.registration.scope], // or some sub-scope
origins: ['*'] // or ['https://example.com']
});
});
دو گزینه پیکربندی وجود دارد که هر دو مورد نیاز است:
-
scopes
آرایهای از یک یا چند رشته را میگیرد، که هر کدام نشاندهنده محدودهای برای درخواستهایی هستند که یک رویدادforeignfetch
را راهاندازی میکنند. اما صبر کنید ، ممکن است فکر کنید، من قبلاً در هنگام ثبت نام کارگر خدمات یک محدوده تعریف کرده ام! این درست است، و دامنه کلی هنوز مرتبط است - هر محدوده ای که در اینجا مشخص می کنید باید یا برابر یا زیر دامنه دامنه کلی کارمند خدمات باشد. محدودیتهای دامنه اضافی در اینجا به شما امکان میدهد یک سرویسکار همهمنظوره را مستقر کنید که میتواند هم رویدادهایfetch
شخص اول (برای درخواستهای انجامشده از سایت شما) و هم رویدادهایforeignfetch
شخص ثالث (برای درخواستهای انجامشده از دامنههای دیگر) را مدیریت کند و واضح است که فقط زیر مجموعه ای از دامنه بزرگتر شما بایدforeignfetch
فعال کند. در عمل، اگر یک سرویسکار اختصاص داده شده برای رسیدگی به رویدادهای شخص ثالث،foreignfetch
، مستقر میکنید، فقط میخواهید از یک محدوده صریح و منفرد استفاده کنید که با محدوده کلی کارمند خدمات شما برابر است. این همان کاری است که مثال بالا با استفاده از مقدارself.registration.scope
انجام می دهد. -
origins
همچنین آرایهای از یک یا چند رشته را میگیرد و به شما اجازه میدهد تا کنترلرforeignfetch
خود را محدود کنید تا فقط به درخواستهای دامنههای خاص پاسخ دهد. برای مثال، اگر به صراحت به «https://example.com» اجازه دهید، پس درخواستی از یک صفحه میزبانی شده درhttps://example.com/path/to/page.html
برای منبعی که از محدوده واکشی خارجی شما ارائه شده است، ارائه شده است. کنترل کننده واکشی خارجی شما را فعال می کند، اما درخواست های ارسال شده ازhttps://random-domain.com/path/to/page.html
کنترل کننده شما را فعال نمی کند. مگر اینکه دلیل خاصی برای راهاندازی منطق واکشی خارجی خود برای زیرمجموعهای از مبداهای راه دور داشته باشید، فقط میتوانید'*'
را به عنوان تنها مقدار در آرایه مشخص کنید و همه مبدا مجاز خواهند بود.
کنترل کننده رویداد خارجی
اکنون که سرویسکار شخص ثالث خود را نصب کردهاید و از طریق registerForeignFetch()
پیکربندی شده است، فرصتی برای رهگیری درخواستهای منبع فرعی متقاطع به سرور شما که در محدوده واکشی خارجی قرار دارند، خواهد داشت.
در یک سرویسکار سنتی و شخص اول، هر درخواست یک رویداد fetch
را ایجاد میکند که کارمند خدمات شما فرصتی برای پاسخگویی به آن دارد. به کارمند خدمات شخص ثالث ما فرصتی داده می شود تا یک رویداد کمی متفاوت به نام foreignfetch
را مدیریت کند. از نظر مفهومی، این دو رویداد کاملاً شبیه هم هستند و به شما این فرصت را میدهند که درخواست ورودی را بررسی کنید و به صورت اختیاری از طریق respondWith()
به آن پاسخ دهید:
self.addEventListener('foreignfetch', event => {
// Assume that requestLogic() is a custom function that takes
// a Request and returns a Promise which resolves with a Response.
event.respondWith(
requestLogic(event.request).then(response => {
return {
response: response,
// Omit to origin to return an opaque response.
// With this set, the client will receive a CORS response.
origin: event.origin,
// Omit headers unless you need additional header filtering.
// With this set, only Content-Type will be exposed.
headers: ['Content-Type']
};
})
);
});
علیرغم شباهت های مفهومی، در عمل هنگام فراخوانی respondWith()
در ForeignFetchEvent
چند تفاوت وجود دارد. به جای ارائه یک Response
(یا Promise
که با یک Response
حل می شود) به respondWith()
، مانند کاری که با FetchEvent
انجام می دهید ، باید یک Promise
که با یک Object با ویژگی های خاص حل می شود به respondWith()
ForeignFetchEvent
ارسال کنید:
-
response
مورد نیاز است، و باید روی شیResponse
تنظیم شود که به مشتری درخواست کننده بازگردانده می شود. اگر چیزی غیر از یکResponse
معتبر ارائه دهید، درخواست مشتری با یک خطای شبکه خاتمه می یابد. برخلاف هنگام فراخوانیrespondWith()
در کنترل کننده رویدادfetch
، باید در اینجا یکResponse
ارائه دهید، نه یکPromise
که با یکResponse
حل می شود! شما می توانید پاسخ خود را از طریق یک زنجیره وعده بسازید و آن زنجیره را به عنوان پارامتر بهrespondWith()
foreignfetch
ارسال کنید، اما زنجیره باید با یک Object حل شود که حاوی ویژگیresponse
است که روی یک شیResponse
تنظیم شده است. شما می توانید نمایشی از این را در نمونه کد بالا مشاهده کنید. -
origin
اختیاری است، و برای تعیین اینکه آیا پاسخی که برگردانده شده است مبهم است یا خیر استفاده می شود. اگر این را کنار بگذارید، پاسخ غیرشفاف خواهد بود و مشتری دسترسی محدودی به بدنه و سرصفحه پاسخ خواهد داشت. اگر درخواست باmode: 'cors'
انجام شده باشد، بازگرداندن یک پاسخ غیر شفاف به عنوان یک خطا تلقی می شود. با این حال، اگر مقدار رشته ای برابر با مبدأ کلاینت راه دور (که می تواند از طریقevent.origin
به دست آید) مشخص کنید، به صراحت انتخاب می کنید که پاسخی با CORS به مشتری ارائه دهد. -
headers
نیز اختیاری است و تنها در صورتی مفید است کهorigin
نیز مشخص کرده باشید و پاسخ CORS را برگردانید. به طور پیشفرض، فقط سرصفحههای موجود در فهرست سرصفحه پاسخ ایمنشده توسط CORS در پاسخ شما گنجانده میشود. اگر نیاز دارید آنچه را که برگردانده میشود فیلتر کنید، هدرها فهرستی از یک یا چند نام سرصفحه را میگیرند و از آن بهعنوان یک لیست مجاز از سرصفحهها برای نمایش در پاسخ استفاده میکند. این به شما امکان میدهد CORS را انتخاب کنید و در عین حال از قرار گرفتن مستقیم سرصفحههای پاسخ حساس با مشتری از راه دور جلوگیری کنید.
توجه به این نکته مهم است که هنگامی که کنترل کننده foreignfetch
اجرا می شود، به تمام اعتبارنامه ها و اختیارات محیطی مبدأ میزبان خدمات کارگر دسترسی دارد . بهعنوان توسعهدهندهای که یک کارمند خدمات خارجی با قابلیت واکشی را مستقر میکند، این مسئولیت شماست که اطمینان حاصل کنید که دادههای پاسخ ممتازی را که در غیر این صورت به موجب آن اعتبارنامهها در دسترس نبود، افشا نکنید. نیاز به انتخاب برای پاسخهای CORS یک مرحله برای محدود کردن قرار گرفتن در معرض ناخواسته است، اما بهعنوان یک توسعهدهنده میتوانید صراحتاً درخواستهای fetch()
را در handler foreignfetch
خود انجام دهید که از اعتبارنامههای ضمنی استفاده نمیکنند از طریق:
self.addEventListener('foreignfetch', event => {
// The new Request will have credentials omitted by default.
const noCredentialsRequest = new Request(event.request.url);
event.respondWith(
// Replace with your own request logic as appropriate.
fetch(noCredentialsRequest)
.catch(() => caches.match(noCredentialsRequest))
.then(response => ({response}))
);
});
ملاحظات مشتری
برخی ملاحظات اضافی وجود دارد که بر نحوه رسیدگی کارگر خدمات واکشی خارجی شما به درخواست های مشتریان خدمات شما تأثیر می گذارد.
مشتریانی که کارمند خدمات شخص اول خود را دارند
برخی از مشتریان سرویس شما ممکن است در حال حاضر کارمند خدمات شخص اول خود را داشته باشند که به درخواستهایی که از برنامه وب آنها نشات میگیرد رسیدگی میکند. این برای کارمند خدمات واکشی خارجی شخص ثالث شما چه معنایی دارد؟
کنترل کننده(های) fetch
در یک کارمند خدمات شخص اول، اولین فرصت را برای پاسخ به تمام درخواست های ارائه شده توسط برنامه وب، حتی اگر یک کارگر خدمات شخص ثالث با foreignfetch
با محدوده ای که درخواست را پوشش می دهد، وجود داشته باشد، دریافت می کند. اما مشتریان با کارگران خدمات شخص اول هنوز هم می توانند از مزایای کارگر خدمات واکشی خارجی شما استفاده کنند!
در داخل یک سرویسکار شخص اول، استفاده از fetch()
برای بازیابی منابع متقاطع، کارگر خدمات واکشی خارجی مناسب را راهاندازی میکند. این بدان معناست که کدهایی مانند زیر می توانند از کنترل کننده foreignfetch
شما استفاده کنند:
// Inside a client's first-party service-worker.js:
self.addEventListener('fetch', event => {
// If event.request is under your foreign fetch service worker's
// scope, this will trigger your foreignfetch handler.
event.respondWith(fetch(event.request));
});
به طور مشابه، اگر کنترلکنندههای واکشی شخص اول وجود داشته باشد، اما هنگام رسیدگی به درخواستهای منبع متقاطع شما، event.respondWith()
را فراخوانی نکنند، درخواست بهطور خودکار به کنترلکننده foreignfetch
شما میرسد:
// Inside a client's first-party service-worker.js:
self.addEventListener('fetch', event => {
if (event.request.mode === 'same-origin') {
event.respondWith(localRequestLogic(event.request));
}
// Since event.respondWith() isn't called for cross-origin requests,
// any foreignfetch handlers scoped to the request will get a chance
// to provide a response.
});
اگر یک کنترل کننده fetch
شخص اول event.respondWith()
را فراخوانی کند اما fetch()
برای درخواست منبعی تحت محدوده واکشی خارجی شما استفاده نکند ، در این صورت کارمند خدمات واکشی خارجی شما فرصتی برای رسیدگی به درخواست نخواهد داشت.
مشتریانی که کارگر خدماتی خود را ندارند
همه مشتریانی که از یک سرویس شخص ثالث درخواست میکنند، میتوانند زمانی که این سرویس یک کارگر خدمات واکشی خارجی را مستقر میکند، سود ببرند، حتی اگر قبلاً از سرویسکار خود استفاده نکرده باشند. تا زمانی که مشتریان از مرورگری که از آن پشتیبانی میکند استفاده میکنند، هیچ چیز خاصی برای انتخاب استفاده از یک کارمند خدمات واکشی خارجی انجام نمیدهند. این بدان معناست که با استقرار یک سرویس دهنده خارجی واکشی، منطق درخواست سفارشی و حافظه پنهان مشترک شما فوراً به بسیاری از مشتریان سرویس شما منتفع می شود، بدون اینکه آنها اقدامات بیشتری را انجام دهند.
کنار هم قرار دادن همه: جایی که مشتریان به دنبال پاسخ هستند
با در نظر گرفتن اطلاعات بالا، میتوانیم سلسله مراتبی از منابعی را که مشتری از آنها برای یافتن پاسخ برای یک درخواست متقاطع استفاده میکند، جمع آوری کنیم.
-
fetch
کارمند خدمات شخص اول (در صورت وجود) - کنترل کننده
foreignfetch
یک کارگر خدمات شخص ثالث (در صورت وجود، و فقط برای درخواست های متقابل) - حافظه پنهان HTTP مرورگر (در صورت وجود پاسخ جدید)
- شبکه
مرورگر از بالا شروع میشود و بسته به اجرای سرویسدهنده، تا زمانی که منبعی برای پاسخ پیدا کند به پایین لیست ادامه میدهد.
بیشتر بدانید
به روز باشید
با توجه به بازخورد برنامهنویسان، اجرای Chrome از واکشی خارجی آزمایشی مبدأ ممکن است تغییر کند. ما این پست را از طریق تغییرات درون خطی به روز نگه می داریم و تغییرات خاص زیر را در صورت وقوع یادداشت می کنیم. همچنین اطلاعات مربوط به تغییرات عمده را از طریق حساب توییتر chromiumdev@ به اشتراک خواهیم گذاشت.