ÐÑли Ð¼Ñ Ñделаем запÑÐ¾Ñ fetch на дÑÑгой веб-ÑайÑ, он, веÑоÑÑно, завеÑÑиÑÑÑ Ð½ÐµÑдаÑей.
ÐапÑимеÑ, давайÑе попÑобÑем запÑоÑиÑÑ http://example.com:
try {
await fetch('http://example.com');
} catch(err) {
alert(err); // Failed to fetch
}
ÐÑзов fetch не ÑдалÑÑ, как и ожидалоÑÑ.
ÐлÑÑевÑм понÑÑием здеÑÑ ÑвлÑеÑÑÑ Ð¸ÑÑоÑник (origin) â комбинаÑÐ¸Ñ Ð´Ð¾Ð¼ÐµÐ½/поÑÑ/пÑоÑокол.
ÐапÑоÑÑ Ð½Ð° дÑÑгой иÑÑоÑник â оÑпÑавленнÑе на дÑÑгой домен (или даже поддомен), или пÑоÑокол, или поÑÑ â ÑÑебÑÑÑ ÑпеÑиалÑнÑÑ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ¾Ð² Ð¾Ñ ÑдалÑнной ÑÑоÑонÑ.
ÐÑа полиÑика назÑваеÑÑÑ Â«CORS»: Cross-Origin Resource Sharing («ÑовмеÑÑное иÑполÑзование ÑеÑÑÑÑов Ð¼ÐµÐ¶Ð´Ñ ÑазнÑми иÑÑоÑниками»).
ÐаÑем нÑжен CORS? ÐкÑкÑÑÑ Ð² иÑÑоÑиÑ
CORS ÑÑÑеÑÑвÑÐµÑ Ð´Ð»Ñ Ð·Ð°ÑиÑÑ Ð¸Ð½ÑеÑнеÑа Ð¾Ñ Ð·Ð»ÑÑ Ñ Ð°ÐºÐµÑов.
СеÑÑÑзно. ÐавайÑе Ñделаем кÑаÑкое иÑÑоÑиÑеÑкое оÑÑÑÑпление.
Ðногие Ð³Ð¾Ð´Ñ ÑкÑÐ¸Ð¿Ñ Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ ÑайÑа не мог полÑÑиÑÑ Ð´Ð¾ÑÑÑп к ÑодеÑÐ¶Ð¸Ð¼Ð¾Ð¼Ñ Ð´ÑÑгого ÑайÑа.
ÐÑо пÑоÑÑое, но могÑÑее пÑавило бÑло оÑновой инÑеÑнеÑ-безопаÑноÑÑи. ÐапÑимеÑ, Ñ
акеÑÑкий ÑкÑÐ¸Ð¿Ñ Ñ ÑайÑа hacker.com не мог полÑÑиÑÑ Ð´Ð¾ÑÑÑп к поÑÑÐ¾Ð²Ð¾Ð¼Ñ ÑÑÐ¸ÐºÑ Ð¿Ð¾Ð»ÑзоваÑÐµÐ»Ñ Ð½Ð° ÑайÑе gmail.com. РлÑди ÑÑвÑÑвовали ÑÐµÐ±Ñ Ñпокойно.
Ð Ñо вÑÐµÐ¼Ñ Ð² JavaScript не бÑло меÑодов Ð´Ð»Ñ ÑеÑевÑÑ Ð·Ð°Ð¿ÑоÑов. ÐÑо бÑл «игÑÑÑеÑнÑй» ÑзÑк Ð´Ð»Ñ ÑкÑаÑÐµÐ½Ð¸Ñ Ð²ÐµÐ±-ÑÑÑаниÑ.
Ðо веб-ÑазÑабоÑÑики жаждали болÑÑей влаÑÑи. ЧÑÐ¾Ð±Ñ Ð¾Ð±Ð¾Ð¹Ñи ÑÑÐ¾Ñ Ð·Ð°Ð¿ÑÐµÑ Ð¸ вÑÑ Ð¶Ðµ полÑÑаÑÑ Ð´Ð°Ð½Ð½Ñе Ñ Ð´ÑÑÐ³Ð¸Ñ ÑайÑов, бÑли пÑидÑÐ¼Ð°Ð½Ñ ÑазнÑе Ñ Ð¸ÑÑоÑÑи.
ÐÑполÑзование ÑоÑм
Ðдним из ÑпоÑобов обÑÐµÐ½Ð¸Ñ Ñ Ð´ÑÑгим ÑеÑвеÑом бÑла оÑпÑавка ÑÑда ÑоÑÐ¼Ñ <form>. ÐÑди оÑпÑавлÑли ÐµÑ Ð² <iframe>, ÑÑÐ¾Ð±Ñ Ð¾ÑÑаваÑÑÑÑ Ð½Ð° ÑекÑÑей ÑÑÑаниÑе, Ð²Ð¾Ñ Ñак:
<!-- ÑÐµÐ»Ñ ÑоÑÐ¼Ñ -->
<iframe name="iframe"></iframe>
<!-- ÑоÑма могла бÑÑÑ Ð´Ð¸Ð½Ð°Ð¼Ð¸ÑеÑки ÑгенеÑиÑована и оÑпÑавлена Ñ Ð¿Ð¾Ð¼Ð¾ÑÑÑ JavaScript -->
<form target="iframe" method="POST" action="http://another.com/â¦">
...
</form>
Таким ÑпоÑобом бÑло возможно ÑделаÑÑ GET/POST запÑÐ¾Ñ Ðº дÑÑÐ³Ð¾Ð¼Ñ ÑайÑÑ Ð´Ð°Ð¶Ðµ без ÑеÑевÑÑ
меÑодов, Ñак как ÑоÑÐ¼Ñ Ð¼Ð¾Ð¶Ð½Ð¾ оÑпÑавлÑÑÑ ÐºÑда Ñгодно. Ðо Ñак как запÑеÑено полÑÑаÑÑ Ð´Ð¾ÑÑÑп к ÑодеÑÐ¶Ð¸Ð¼Ð¾Ð¼Ñ <iframe> Ñ Ð´ÑÑгого ÑайÑа, пÑоÑиÑаÑÑ Ð¾ÑÐ²ÐµÑ Ð±Ñло невозможно.
ÐÑли бÑÑÑ ÑоÑнÑм, бÑли ÑÑÑки и Ð´Ð»Ñ ÑÑого, ÑÑебÑÑÑие ÑпеÑиалÑного кода на ÑÑÑаниÑе и в иÑÑейме, Ñак ÑÑо обÑение Ñ Ð¸ÑÑеймом бÑло ÑÐµÑ Ð½Ð¸ÑеÑки возможно. СейÑÐ°Ñ Ð¼Ñ Ð½Ðµ бÑдем вдаваÑÑÑÑ Ð² подÑобноÑÑи, пÑÑÑÑ ÑÑи динозавÑÑ Ð¿Ð¾ÐºÐ¾ÑÑÑÑ Ñ Ð¼Ð¸Ñом.
ÐÑполÑзование ÑкÑипÑов
ÐÑÑ Ð¾Ð´Ð¸Ð½ ÑÑÑк заклÑÑалÑÑ Ð² иÑполÑзовании Ñега script. У него Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð»Ñбой src, Ñ Ð»ÑбÑм доменом, напÑÐ¸Ð¼ÐµÑ <script src="http://another.com/â¦">. ÐÑо даÑÑ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑÑ Ð·Ð°Ð³ÑÑзиÑÑ Ð¸ вÑполниÑÑ ÑкÑÐ¸Ð¿Ñ Ð¾ÑкÑда Ñгодно.
ÐÑли ÑайÑ, напÑÐ¸Ð¼ÐµÑ another.com, Ñ
оÑел пÑедоÑÑавиÑÑ Ð´Ð°Ð½Ð½Ñе Ð´Ð»Ñ Ñакого доÑÑÑпа, он пÑедоÑÑавлÑл Ñак назÑваемÑй «пÑоÑокол JSONP» (JSON with Padding)".
ÐÐ¾Ñ ÐºÐ°Ðº он ÑабоÑал.
ÐапÑимеÑ, нам на наÑем ÑайÑе нÑÐ¶Ð½Ñ Ð´Ð°Ð½Ð½Ñе Ñ ÑайÑа http://another.com, Ñкажем, погода:
-
СнаÑала, заÑанее, обÑÑвлÑем глобалÑнÑÑ ÑÑнкÑÐ¸Ñ Ð´Ð»Ñ Ð¾Ð±ÑабоÑки даннÑÑ , напÑимеÑ
gotWeather.// 1. ÐбÑÑвиÑÑ ÑÑнкÑÐ¸Ñ Ð´Ð»Ñ Ð¾Ð±ÑабоÑки погоднÑÑ Ð´Ð°Ð½Ð½ÑÑ function gotWeather({ temperature, humidity }) { alert(`ÑемпеÑаÑÑÑа: ${temperature}, влажноÑÑÑ: ${humidity}`); } -
ÐаÑем ÑоздаÑм Ñег
<script>Ñsrc="http://another.com/weather.json?callback=gotWeather", пÑи ÑÑом Ð¸Ð¼Ñ Ð½Ð°Ñей ÑÑнкÑии â в URL-паÑамеÑÑеcallback.let script = document.createElement('script'); script.src = `http://another.com/weather.json?callback=gotWeather`; document.body.append(script); -
УдалÑннÑй ÑеÑÐ²ÐµÑ Ñ
another.comдолжен в оÑÐ²ÐµÑ ÑгенеÑиÑоваÑÑ ÑкÑипÑ, коÑоÑÑй вÑзÑваеÑgotWeather(...)Ñ Ð´Ð°Ð½Ð½Ñми, коÑоÑÑе Ñ Ð¾ÑÐµÑ Ð¿ÐµÑедаÑÑ.// ÐжидаемÑй оÑÐ²ÐµÑ Ð¾Ñ ÑеÑвеÑа вÑглÑÐ´Ð¸Ñ Ñак: gotWeather({ temperature: 25, humidity: 78 }); -
Ðогда ÑÑÐ¾Ñ ÑкÑÐ¸Ð¿Ñ Ð·Ð°Ð³ÑÑзиÑÑÑ Ð¸ вÑполниÑÑÑ, наÑа ÑÑнкÑиÑ
gotWeatherполÑÑÐ°ÐµÑ Ð´Ð°Ð½Ð½Ñе.
ÐÑо ÑабоÑÐ°ÐµÑ Ð¸ не наÑÑÑÐ°ÐµÑ Ð±ÐµÐ·Ð¾Ð¿Ð°ÑноÑÑÑ, поÑÐ¾Ð¼Ñ ÑÑо обе ÑÑоÑÐ¾Ð½Ñ ÑоглаÑилиÑÑ Ð¿ÐµÑедаваÑÑ Ð´Ð°Ð½Ð½Ñе Ñаким обÑазом. Ркогда обе ÑÑоÑÐ¾Ð½Ñ ÑоглаÑнÑ, Ñо ÑÑо опÑеделÑнно не Ñ Ð°Ðº. ÐÑÑ ÐµÑÑ ÑÑÑеÑÑвÑÑÑ ÑеÑвиÑÑ, коÑоÑÑе пÑедоÑÑавлÑÑÑ Ñакой доÑÑÑп, Ñак как ÑÑо ÑабоÑÐ°ÐµÑ Ð´Ð°Ð¶Ðµ Ð´Ð»Ñ Ð¾ÑÐµÐ½Ñ ÑÑаÑÑÑ Ð±ÑаÑзеÑов.
СпÑÑÑÑ Ð½ÐµÐºÐ¾ÑоÑое вÑÐµÐ¼Ñ Ð² бÑаÑзеÑном JavaScript поÑвилиÑÑ Ð¼ÐµÑÐ¾Ð´Ñ Ð´Ð»Ñ ÑеÑевÑÑ Ð·Ð°Ð¿ÑоÑов.
ÐнаÑале запÑоÑÑ Ð½Ð° дÑÑгой иÑÑоÑник бÑли запÑеÑенÑ. Ðо в ÑезÑлÑÑаÑе Ð´Ð¾Ð»Ð³Ð¸Ñ Ð´Ð¸ÑкÑÑÑий бÑло ÑеÑено ÑазÑеÑиÑÑ Ð¸Ñ Ð´ÐµÐ»Ð°ÑÑ, но Ð´Ð»Ñ Ð¸ÑполÑÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð½Ð¾Ð²ÑÑ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑей ÑÑебоваÑÑ ÑазÑеÑение ÑеÑвеÑа, вÑÑаженное в ÑпеÑиалÑнÑÑ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ°Ñ .
ÐÑоÑÑÑе запÑоÑÑ
ÐÑÑÑ Ð´Ð²Ð° вида запÑоÑов на дÑÑгой иÑÑоÑник:
- ÐÑоÑÑÑе.
- ÐÑе оÑÑалÑнÑе.
ÐÑоÑÑÑе запÑоÑÑ Ð±ÑдÑÑ Ð¿Ð¾Ð¿ÑоÑе, поÑÑÐ¾Ð¼Ñ Ð´Ð°Ð²Ð°Ð¹Ñе наÑнÑм Ñ Ð½Ð¸Ñ .
ÐÑоÑÑой запÑÐ¾Ñ â ÑÑо запÑоÑ, ÑдовлеÑвоÑÑÑÑий ÑледÑÑÑим ÑÑловиÑм:
- ÐÑоÑÑой меÑод: GET, POST или HEAD
- ÐÑоÑÑÑе заголовки â ÑазÑеÑÐµÐ½Ñ ÑолÑко:
Accept,Accept-Language,Content-Language,Content-TypeÑо знаÑениемapplication/x-www-form-urlencoded,multipart/form-dataилиtext/plain.
ÐÑбой дÑÑгой запÑÐ¾Ñ ÑÑиÑаеÑÑÑ Â«Ð½ÐµÐ¿ÑоÑÑÑм». ÐапÑимеÑ, запÑÐ¾Ñ Ñ Ð¼ÐµÑодом PUT или Ñ HTTP-заголовком API-Key не ÑооÑвеÑÑÑвÑÐµÑ ÑÑловиÑм.
ÐÑинÑипиалÑное оÑлиÑие Ð¼ÐµÐ¶Ð´Ñ Ð½Ð¸Ð¼Ð¸ ÑоÑÑÐ¾Ð¸Ñ Ð² Ñом, ÑÑо «пÑоÑÑой запÑоÑ» Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ñделан ÑеÑез <form> или <script>, без какиÑ
-Ñо ÑпеÑиалÑнÑÑ
меÑодов.
Таким обÑазом, даже оÑÐµÐ½Ñ ÑÑаÑÑй ÑеÑÐ²ÐµÑ Ð´Ð¾Ð»Ð¶ÐµÐ½ бÑÑÑ ÑпоÑобен пÑинÑÑÑ Ð¿ÑоÑÑой запÑоÑ.
РпÑоÑивоположноÑÑÑ ÑÑомÑ, запÑоÑÑ Ñ Ð½ÐµÑÑандаÑÑнÑми заголовками или, напÑимеÑ, меÑодом DELETE нелÑÐ·Ñ ÑоздаÑÑ Ñаким ÑпоÑобом. Ðолгое вÑÐµÐ¼Ñ JavaScript не мог делаÑÑ Ñакие запÑоÑÑ. ÐоÑÑÐ¾Ð¼Ñ ÑÑаÑÑй ÑеÑÐ²ÐµÑ Ð¼Ð¾Ð¶ÐµÑ Ð¿ÑедположиÑÑ, ÑÑо Ñакие запÑоÑÑ Ð¿Ð¾ÑÑÑпаÑÑ Ð¾Ñ Ð¿ÑивилегиÑованного иÑÑоÑника, «пÑоÑÑо поÑомÑ, ÑÑо веб-ÑÑÑаниÑа неÑпоÑобна иÑ
поÑÑлаÑÑ».
Ðогда Ð¼Ñ Ð¿ÑÑаемÑÑ ÑделаÑÑ Ð½ÐµÐ¿ÑоÑÑой запÑоÑ, бÑаÑÐ·ÐµÑ Ð¿Ð¾ÑÑÐ»Ð°ÐµÑ ÑпеÑиалÑнÑй пÑедваÑиÑелÑнÑй запÑÐ¾Ñ («пÑедзапÑоÑ», по англ. «preflight»), коÑоÑÑй ÑпÑаÑÐ¸Ð²Ð°ÐµÑ Ñ ÑеÑвеÑа â ÑоглаÑен ли он пÑинÑÑÑ Ñакой непÑоÑÑой запÑÐ¾Ñ Ð¸Ð»Ð¸ неÑ?
Ð, еÑли ÑеÑÐ²ÐµÑ Ñвно не даÑÑ ÑоглаÑие в Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ°Ñ , непÑоÑÑой запÑÐ¾Ñ Ð½Ðµ поÑÑлаеÑÑÑ.
Ðалее Ð¼Ñ ÑазбеÑÑм конкÑеÑнÑе деÑали.
CORS Ð´Ð»Ñ Ð¿ÑоÑÑÑÑ Ð·Ð°Ð¿ÑоÑов
ÐÑи запÑоÑе на дÑÑгой иÑÑоÑник бÑаÑÐ·ÐµÑ Ð²Ñегда ÑÑÐ°Ð²Ð¸Ñ Â«Ð¾Ñ ÑебÑ» заголовок Origin.
ÐапÑимеÑ, еÑли Ð¼Ñ Ð·Ð°Ð¿ÑаÑиваем https://anywhere.com/request Ñо ÑÑÑаниÑÑ https://javascript.info/page, заголовки бÑдÑÑ Ñакими:
GET /request
Host: anywhere.com
Origin: https://javascript.info
...
Ðак Ð²Ñ Ð¼Ð¾Ð¶ÐµÑе видеÑÑ, заголовок Origin ÑодеÑÐ¶Ð¸Ñ Ð¸Ð¼ÐµÐ½Ð½Ð¾ иÑÑоÑник (домен/пÑоÑокол/поÑÑ), без пÑÑи.
СеÑÐ²ÐµÑ Ð¼Ð¾Ð¶ÐµÑ Ð¿ÑовеÑиÑÑ Origin и, еÑли он ÑоглаÑен пÑинÑÑÑ Ñакой запÑоÑ, добавиÑÑ Ð¾ÑобÑй заголовок Access-Control-Allow-Origin к оÑвеÑÑ. ÐÑÐ¾Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²Ð¾Ðº должен ÑодеÑжаÑÑ ÑазÑеÑÑннÑй иÑÑоÑник (в наÑем ÑлÑÑае https://javascript.info) или звÑздоÑÐºÑ *. Тогда оÑÐ²ÐµÑ ÑÑпеÑен, в пÑоÑивном ÑлÑÑае Ð²Ð¾Ð·Ð½Ð¸ÐºÐ°ÐµÑ Ð¾Ñибка.
ÐдеÑÑ Ð±ÑаÑÐ·ÐµÑ Ð¸Ð³ÑÐ°ÐµÑ ÑÐ¾Ð»Ñ Ð´Ð¾Ð²ÐµÑенного поÑÑедника:
- Ðн гаÑанÑиÑÑеÑ, ÑÑо к запÑоÑÑ Ð½Ð° дÑÑгой иÑÑоÑник добавлÑеÑÑÑ Ð¿ÑавилÑнÑй заголовок
Origin. - Ðн пÑовеÑÑÐµÑ Ð½Ð°Ð»Ð¸Ñие ÑазÑеÑаÑÑего заголовка
Access-Control-Allow-Originв оÑвеÑе и, еÑли вÑÑ Ñ Ð¾ÑоÑо, Ñо JavaScript полÑÑÐ°ÐµÑ Ð´Ð¾ÑÑÑп к оÑвеÑÑ ÑеÑвеÑа, в пÑоÑивном ÑлÑÑае â доÑÑÑп запÑеÑаеÑÑÑ Ñ Ð¾Ñибкой.
ÐÐ¾Ñ Ð¿ÑÐ¸Ð¼ÐµÑ Ð¾ÑвеÑа ÑеÑвеÑа, коÑоÑÑй ÑазÑеÑÐ°ÐµÑ Ð´Ð¾ÑÑÑп:
200 OK
Content-Type:text/html; charset=UTF-8
Access-Control-Allow-Origin: https://javascript.info
Ðаголовки оÑвеÑа
Ðо ÑмолÑÐ°Ð½Ð¸Ñ Ð¿Ñи запÑоÑе к дÑÑÐ³Ð¾Ð¼Ñ Ð¸ÑÑоÑÐ½Ð¸ÐºÑ JavaScript Ð¼Ð¾Ð¶ÐµÑ Ð¿Ð¾Ð»ÑÑиÑÑ Ð´Ð¾ÑÑÑп ÑолÑко к Ñак назÑваемÑм «пÑоÑÑÑм» заголовкам оÑвеÑа:
Cache-ControlContent-LanguageContent-LengthContent-TypeExpiresLast-ModifiedPragma
ÐÑи доÑÑÑпе к лÑÐ±Ð¾Ð¼Ñ Ð´ÑÑÐ³Ð¾Ð¼Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÑ Ð¾ÑвеÑа бÑÐ´ÐµÑ Ð¾Ñибка.
ЧÑÐ¾Ð±Ñ ÑазÑеÑиÑÑ JavaScript доÑÑÑп к лÑÐ±Ð¾Ð¼Ñ Ð´ÑÑÐ³Ð¾Ð¼Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÑ Ð¾ÑвеÑа, ÑеÑÐ²ÐµÑ Ð´Ð¾Ð»Ð¶ÐµÐ½ ÑказаÑÑ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²Ð¾Ðº Access-Control-Expose-Headers. Ðн ÑодеÑÐ¶Ð¸Ñ ÑпиÑок, ÑеÑез запÑÑÑÑ, заголовков, коÑоÑÑе не ÑвлÑÑÑÑÑ Ð¿ÑоÑÑÑми, но доÑÑÑп к коÑоÑÑм ÑазÑеÑÑн.
ÐапÑимеÑ:
200 OK
Content-Type:text/html; charset=UTF-8
Content-Length: 12345
Content-Encoding: gzip
API-Key: 2c9de507f2c54aa1
Access-Control-Allow-Origin: https://javascript.info
Access-Control-Expose-Headers: Content-Encoding,API-Key
ÐÑи Ñаком заголовке Access-Control-Expose-Headers, ÑкÑипÑÑ ÑазÑеÑено полÑÑиÑÑ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ¸ Content-Encoding и API-Key оÑвеÑа.
«ÐепÑоÑÑÑе» запÑоÑÑ
ÐÑ Ð¼Ð¾Ð¶ÐµÐ¼ иÑполÑзоваÑÑ Ð»Ñбой HTTP-меÑод: не ÑолÑко GET/POST, но и PATCH, DELETE и дÑÑгие.
ÐекоÑоÑое вÑÐµÐ¼Ñ Ð½Ð°Ð·Ð°Ð´ никÑо не мог даже пÑедположиÑÑ, ÑÑо веб-ÑÑÑаниÑа ÑпоÑобна делаÑÑ Ñакие запÑоÑÑ. Так ÑÑо могÑÑ ÑÑÑеÑÑвоваÑÑ Ð²ÐµÐ±-ÑеÑвиÑÑ, коÑоÑÑе ÑаÑÑмаÑÑиваÑÑ Ð½ÐµÑÑандаÑÑнÑй меÑод как Ñигнал: «ÐÑо не бÑаÑзеÑ». Ðни могÑÑ ÑÑиÑÑваÑÑ ÑÑо пÑи пÑовеÑке пÑав доÑÑÑпа.
ÐоÑÑомÑ, ÑÑÐ¾Ð±Ñ Ð¸Ð·Ð±ÐµÐ¶Ð°ÑÑ Ð½ÐµÐ´Ð¾Ð¿Ð¾Ð½Ð¸Ð¼Ð°Ð½Ð¸Ð¹, бÑаÑÐ·ÐµÑ Ð½Ðµ Ð´ÐµÐ»Ð°ÐµÑ Â«Ð½ÐµÐ¿ÑоÑÑÑе» запÑоÑÑ (коÑоÑÑе нелÑÐ·Ñ Ð±Ñло ÑделаÑÑ Ð² пÑоÑлом) ÑÑазÑ. ÐеÑед ÑÑим он поÑÑÐ»Ð°ÐµÑ Ð¿ÑедваÑиÑелÑнÑй запÑоÑ, ÑпÑаÑÐ¸Ð²Ð°Ñ ÑазÑеÑениÑ.
ÐÑедваÑиÑелÑнÑй запÑÐ¾Ñ Ð¸ÑполÑзÑÐµÑ Ð¼ÐµÑод OPTIONS, Ñ Ð½ÐµÐ³Ð¾ Ð½ÐµÑ Ñела, но еÑÑÑ ÑÑи заголовка:
OriginÑодеÑÐ¶Ð¸Ñ Ð¸Ð¼ÐµÐ½Ð½Ð¾ иÑÑоÑник (домен/пÑоÑокол/поÑÑ), без пÑÑи.Access-Control-Request-MethodÑодеÑÐ¶Ð¸Ñ HTTP-меÑод «непÑоÑÑого» запÑоÑа.Access-Control-Request-HeadersпÑедоÑÑавлÑÐµÑ ÑазделÑннÑй запÑÑÑми ÑпиÑок его «непÑоÑÑÑÑ Â» HTTP-заголовков.
ÐÑли ÑеÑÐ²ÐµÑ ÑоглаÑен пÑинимаÑÑ Ñакие запÑоÑÑ, Ñо он должен оÑвеÑиÑÑ Ð±ÐµÐ· Ñела, Ñо ÑÑаÑÑÑом 200 и Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ°Ð¼Ð¸:
Access-Control-Allow-Originдолжен ÑодеÑжаÑÑ ÑазÑеÑÑннÑй иÑÑоÑник.Access-Control-Allow-Methodsдолжен ÑодеÑжаÑÑ ÑазÑеÑÑннÑе меÑодÑ.Access-Control-Allow-Headersдолжен ÑодеÑжаÑÑ ÑпиÑок ÑазÑеÑÑннÑÑ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ¾Ð².- ÐÑоме Ñого, заголовок
Access-Control-Max-AgeÐ¼Ð¾Ð¶ÐµÑ ÑказÑваÑÑ ÐºÐ¾Ð»Ð¸ÑеÑÑво ÑекÑнд, на коÑоÑое нÑжно кеÑиÑоваÑÑ ÑазÑеÑениÑ. Так ÑÑо бÑаÑзеÑÑ Ð½Ðµ пÑидÑÑÑÑ Ð¿Ð¾ÑÑлаÑÑ Ð¿ÑедзапÑÐ¾Ñ Ð´Ð»Ñ Ð¿Ð¾ÑледÑÑÑÐ¸Ñ Ð·Ð°Ð¿ÑоÑов, ÑдовлеÑвоÑÑÑÑÐ¸Ñ Ð´Ð°Ð½Ð½Ñм ÑазÑеÑениÑм.
ÐавайÑе поÑагово поÑмоÑÑим, как ÑÑо ÑабоÑаеÑ, на пÑимеÑе PATCH запÑоÑа (ÑÑÐ¾Ñ Ð¼ÐµÑод ÑаÑÑо иÑполÑзÑеÑÑÑ Ð´Ð»Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð´Ð°Ð½Ð½ÑÑ
) на дÑÑгой иÑÑоÑник:
let response = await fetch('https://site.com/service.json', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'API-Key': 'secret'
}
});
ÐÑÐ¾Ñ Ð·Ð°Ð¿ÑÐ¾Ñ Ð½Ðµ ÑвлÑеÑÑÑ Ð¿ÑоÑÑÑм по ÑÑÑм пÑиÑинам (доÑÑаÑоÑно одной):
- ÐеÑод
PATCH Content-Typeне один из:application/x-www-form-urlencoded,multipart/form-data,text/plain.- СодеÑÐ¶Ð¸Ñ Â«Ð½ÐµÐ¿ÑоÑÑой» заголовок
API-Key.
Шаг 1 (пÑедзапÑоÑ)
ÐеÑед Ñем, как поÑлаÑÑ Ñакой запÑоÑ, бÑаÑÐ·ÐµÑ ÑамоÑÑоÑÑелÑно генеÑиÑÑÐµÑ Ð¸ поÑÑÐ»Ð°ÐµÑ Ð¿ÑедзапÑоÑ, коÑоÑÑй вÑглÑÐ´Ð¸Ñ ÑледÑÑÑим обÑазом:
OPTIONS /service.json
Host: site.com
Origin: https://javascript.info
Access-Control-Request-Method: PATCH
Access-Control-Request-Headers: Content-Type,API-Key
- ÐеÑод:
OPTIONS. - ÐÑÑÑ â ÑоÑно Ñакой же, как в оÑновном запÑоÑе:
/service.json. - ÐÑобÑе заголовки:
Originâ иÑÑоÑник.Access-Control-Request-Methodâ запÑаÑиваемÑй меÑод.Access-Control-Request-Headersâ ÑазделÑннÑй запÑÑÑми ÑпиÑок «непÑоÑÑÑÑ Â» заголовков запÑоÑа.
Шаг 2 (оÑÐ²ÐµÑ ÑеÑвеÑа на пÑедзапÑоÑ)
СеÑÐ²ÐµÑ Ð´Ð¾Ð»Ð¶ÐµÐ½ оÑвеÑиÑÑ Ñо ÑÑаÑÑÑом 200 и заголовками:
Access-Control-Allow-Methods: PATCHAccess-Control-Allow-Headers: Content-Type,API-Key.
ÐÑо ÑазÑеÑÐ¸Ñ Ð±ÑдÑÑÑÑ ÐºÐ¾Ð¼Ð¼ÑникаÑиÑ, в пÑоÑивном ÑлÑÑае Ð²Ð¾Ð·Ð½Ð¸ÐºÐ°ÐµÑ Ð¾Ñибка.
ÐÑли ÑеÑÐ²ÐµÑ Ð¾Ð¶Ð¸Ð´Ð°ÐµÑ Ð² бÑдÑÑем дÑÑгие меÑÐ¾Ð´Ñ Ð¸ заголовки, Ñо он Ð¼Ð¾Ð¶ÐµÑ Ð² оÑвеÑе пеÑеÑиÑлиÑÑ Ð¸Ñ Ð²Ñе ÑÑазÑ, ÑазÑеÑиÑÑ Ð·Ð°Ñанее, напÑимеÑ:
200 OK
Access-Control-Allow-Methods: PUT,PATCH,DELETE
Access-Control-Allow-Headers: API-Key,Content-Type,If-Modified-Since,Cache-Control
Access-Control-Max-Age: 86400
ТепеÑÑ, когда бÑаÑÐ·ÐµÑ Ð²Ð¸Ð´Ð¸Ñ, ÑÑо PATCH еÑÑÑ Ð² Access-Control-Allow-Methods, а Content-Type,API-Key â в ÑпиÑке Access-Control-Allow-Headers, он поÑÑÐ»Ð°ÐµÑ Ð½Ð°Ñ Ð¾Ñновной запÑоÑ.
ÐÑоме Ñого, оÑÐ²ÐµÑ Ð½Ð° пÑедзапÑÐ¾Ñ ÐºÐµÑиÑÑеÑÑÑ Ð½Ð° вÑемÑ, Ñказанное в заголовке Access-Control-Max-Age (86400 ÑекÑнд, один денÑ), Ñак ÑÑо поÑледÑÑÑие запÑоÑÑ Ð½Ðµ вÑзовÑÑ Ð¿ÑедзапÑоÑ. Ðни бÑдÑÑ Ð¾ÑоÑÐ»Ð°Ð½Ñ ÑÑÐ°Ð·Ñ Ð¿Ñи ÑÑловии, ÑÑо ÑооÑвеÑÑÑвÑÑÑ Ð·Ð°ÐºÐµÑиÑованнÑм ÑазÑеÑениÑм.
Шаг 3 (оÑновной запÑоÑ)
ÐÑли пÑедзапÑÐ¾Ñ ÑÑпеÑен, бÑаÑÐ·ÐµÑ Ð´ÐµÐ»Ð°ÐµÑ Ð¾Ñновной запÑоÑ. ÐлгоÑиÑм здеÑÑ Ñакой же, ÑÑо и Ð´Ð»Ñ Ð¿ÑоÑÑÑÑ Ð·Ð°Ð¿ÑоÑов.
ÐÑновной запÑÐ¾Ñ Ð¸Ð¼ÐµÐµÑ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²Ð¾Ðº Origin (поÑÐ¾Ð¼Ñ ÑÑо он идÑÑ Ð½Ð° дÑÑгой иÑÑоÑник):
PATCH /service.json
Host: site.com
Content-Type: application/json
API-Key: secret
Origin: https://javascript.info
Шаг 4 (оÑновной оÑвеÑ)
СеÑÐ²ÐµÑ Ð½Ðµ должен забÑваÑÑ Ð¾ добавлении Access-Control-Allow-Origin к оÑвеÑÑ Ð½Ð° оÑновной запÑоÑ. УÑпеÑнÑй пÑедзапÑÐ¾Ñ Ð½Ðµ оÑÐ²Ð¾Ð±Ð¾Ð¶Ð´Ð°ÐµÑ Ð¾Ñ ÑÑого:
Access-Control-Allow-Origin: https://javascript.info
ÐоÑле ÑÑого JavaScript Ð¼Ð¾Ð¶ÐµÑ Ð¿ÑоÑиÑаÑÑ Ð¾ÑÐ²ÐµÑ ÑеÑвеÑа.
ÐÑедзапÑÐ¾Ñ Ð¾ÑÑÑеÑÑвлÑеÑÑÑ Â«Ð·Ð° кÑлиÑами», невидимо Ð´Ð»Ñ JavaScript.
JavaScript полÑÑÐ°ÐµÑ ÑолÑко оÑÐ²ÐµÑ Ð½Ð° оÑновной запÑÐ¾Ñ Ð¸Ð»Ð¸ оÑибкÑ, еÑли Ñо ÑÑоÑÐ¾Ð½Ñ ÑеÑвеÑа Ð½ÐµÑ ÑазÑеÑениÑ.
ÐвÑоÑизаÑионнÑе даннÑе
ÐапÑÐ¾Ñ Ð½Ð° дÑÑгой иÑÑоÑник по ÑмолÑÐ°Ð½Ð¸Ñ Ð½Ðµ ÑодеÑÐ¶Ð¸Ñ Ð°Ð²ÑоÑизаÑионнÑÑ Ð´Ð°Ð½Ð½ÑÑ (credentials), под коÑоÑÑми здеÑÑ Ð¿Ð¾Ð½Ð¸Ð¼Ð°ÑÑÑÑ ÐºÑки и заголовки HTTP-аÑÑенÑиÑикаÑии.
ÐÑо неÑипиÑно Ð´Ð»Ñ HTTP-запÑоÑов. ÐбÑÑно запÑÐ¾Ñ Ðº http://site.com ÑопÑовождаеÑÑÑ Ð²Ñеми кÑки Ñ ÑÑого домена. Ðо запÑоÑÑ Ð½Ð° дÑÑгой иÑÑоÑник, ÑделаннÑе меÑодами JavaScript â иÑклÑÑение.
ÐапÑимеÑ, fetch('http://another.com') не поÑÑÐ»Ð°ÐµÑ Ð½Ð¸ÐºÐ°ÐºÐ¸Ñ
кÑки, даже ÑеÑ
(!), коÑоÑÑе пÑÐ¸Ð½Ð°Ð´Ð»ÐµÐ¶Ð°Ñ Ð´Ð¾Ð¼ÐµÐ½Ñ another.com.
ÐоÑемÑ?
ÐоÑÐ¾Ð¼Ñ ÑÑо запÑÐ¾Ñ Ñ Ð°Ð²ÑоÑизаÑионнÑми даннÑми даÑÑ Ð½Ð°Ð¼Ð½Ð¾Ð³Ð¾ болÑÑе возможноÑÑей, Ñем без Ð½Ð¸Ñ . ÐÑли он ÑазÑеÑÑн, Ñо ÑÑо позволÑÐµÑ JavaScript дейÑÑвоваÑÑ Ð¾Ñ Ð¸Ð¼ÐµÐ½Ð¸ полÑзоваÑÐµÐ»Ñ Ð¸ полÑÑаÑÑ Ð¸Ð½ÑоÑмаÑиÑ, иÑполÑзÑÑ ÐµÐ³Ð¾ авÑоÑизаÑионнÑе даннÑе.
ÐейÑÑвиÑелÑно ли ÑеÑÐ²ÐµÑ Ð½Ð°ÑÑолÑко довеÑÑÐµÑ ÑкÑипÑÑ? Тогда он должен Ñвно ÑазÑеÑиÑÑ Ñакие запÑоÑÑ Ð¿Ñи помоÑи дополниÑелÑного заголовка.
ЧÑÐ¾Ð±Ñ Ð²ÐºÐ»ÑÑиÑÑ Ð¾ÑпÑÐ°Ð²ÐºÑ Ð°Ð²ÑоÑизаÑионнÑÑ
даннÑÑ
в fetch, нам нÑжно добавиÑÑ Ð¾Ð¿ÑÐ¸Ñ credentials: "include", Ð²Ð¾Ñ Ñак:
fetch('http://another.com', {
credentials: "include"
});
ТепеÑÑ fetch поÑлÑÑ ÐºÑки Ñ Ð´Ð¾Ð¼ÐµÐ½Ð° another.com вмеÑÑе Ñ Ð½Ð°Ñим запÑоÑом на ÑÑÐ¾Ñ ÑайÑ.
ÐÑли ÑеÑÐ²ÐµÑ ÑоглаÑен пÑинÑÑÑ Ð·Ð°Ð¿ÑÐ¾Ñ Ñ Ð°Ð²ÑоÑизаÑионнÑми даннÑми, он должен добавиÑÑ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²Ð¾Ðº Access-Control-Allow-Credentials: true к оÑвеÑÑ, в дополнение к Access-Control-Allow-Origin.
ÐапÑимеÑ:
200 OK
Access-Control-Allow-Origin: https://javascript.info
Access-Control-Allow-Credentials: true
ÐожалÑйÑÑа, обÑаÑиÑе внимание: в Access-Control-Allow-Origin запÑеÑено иÑполÑзоваÑÑ Ð·Ð²ÑздоÑÐºÑ * Ð´Ð»Ñ Ð·Ð°Ð¿ÑоÑов Ñ Ð°Ð²ÑоÑизаÑионнÑми даннÑми. Там должен бÑÑÑ Ð¸Ð¼ÐµÐ½Ð½Ð¾ иÑÑоÑник, как показано вÑÑе. ÐÑо дополниÑелÑÐ½Ð°Ñ Ð¼ÐµÑа безопаÑноÑÑи, ÑÑÐ¾Ð±Ñ Ð³Ð°ÑанÑиÑоваÑÑ, ÑÑо ÑеÑÐ²ÐµÑ Ð´ÐµÐ¹ÑÑвиÑелÑно знаеÑ, ÐºÐ¾Ð¼Ñ Ð¾Ð½ довеÑÑÐµÑ Ð´ÐµÐ»Ð°ÑÑ Ñакие запÑоÑÑ.
ÐÑого
С ÑоÑки зÑÐµÐ½Ð¸Ñ Ð±ÑаÑзеÑа запÑоÑÑ Ðº дÑÑÐ³Ð¾Ð¼Ñ Ð¸ÑÑоÑÐ½Ð¸ÐºÑ Ð±ÑваÑÑ Ð´Ð²ÑÑ Ð²Ð¸Ð´Ð¾Ð²: «пÑоÑÑÑе» и вÑе оÑÑалÑнÑе.
ÐÑоÑÑÑе запÑоÑÑ Ð´Ð¾Ð»Ð¶Ð½Ñ ÑдовлеÑвоÑÑÑÑ ÑледÑÑÑим ÑÑловиÑм:
- ÐеÑод: GET, POST или HEAD.
- Ðаголовки â Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ ÑÑÑановиÑÑ ÑолÑко:
AcceptAccept-LanguageContent-LanguageContent-TypeÑо знаÑениемapplication/x-www-form-urlencoded,multipart/form-dataилиtext/plain.
ÐÑновное иÑ
оÑлиÑие заклÑÑаеÑÑÑ Ð² Ñом, ÑÑо пÑоÑÑÑе запÑоÑÑ Ñ Ð´Ð°Ð²Ð½Ð¸Ñ
вÑемÑн вÑполнÑлиÑÑ Ñ Ð¸ÑполÑзованием Ñегов <form> или <script>, в Ñо вÑÐµÐ¼Ñ ÐºÐ°Ðº непÑоÑÑÑе долгое вÑÐµÐ¼Ñ Ð±Ñли Ð½ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ñ Ð´Ð»Ñ Ð±ÑаÑзеÑов.
ÐÑакÑиÑеÑÐºÐ°Ñ ÑазниÑа ÑоÑÑÐ¾Ð¸Ñ Ð² Ñом, ÑÑо пÑоÑÑÑе запÑоÑÑ Ð¾ÑпÑавлÑÑÑÑÑ ÑÑÐ°Ð·Ñ Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ¾Ð¼ Origin, а Ð´Ð»Ñ Ð´ÑÑгиÑ
бÑаÑÐ·ÐµÑ Ð´ÐµÐ»Ð°ÐµÑ Ð¿ÑедваÑиÑелÑнÑй запÑоÑ, ÑпÑаÑÐ¸Ð²Ð°Ñ ÑазÑеÑениÑ.
ÐÐ»Ñ Ð¿ÑоÑÑÑÑ Ð·Ð°Ð¿ÑоÑов:
- â ÐÑаÑÐ·ÐµÑ Ð¿Ð¾ÑÑÐ»Ð°ÐµÑ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²Ð¾Ðº
OriginÑ Ð¸ÑÑоÑником. - â ÐÐ»Ñ Ð·Ð°Ð¿ÑоÑов без авÑоÑизаÑионнÑÑ
даннÑÑ
(не оÑпÑавлÑÑÑÑÑ Ð¿Ð¾ ÑмолÑаниÑ) ÑеÑÐ²ÐµÑ Ð´Ð¾Ð»Ð¶ÐµÐ½ ÑÑÑановиÑÑ:
Access-Control-Allow-Originв*или Ñо же знаÑение, ÑÑо иOrigin
- â ÐÐ»Ñ Ð·Ð°Ð¿ÑоÑов Ñ Ð°Ð²ÑоÑизаÑионнÑми даннÑми ÑеÑÐ²ÐµÑ Ð´Ð¾Ð»Ð¶ÐµÐ½ ÑÑÑановиÑÑ:
Access-Control-Allow-Originв Ñо же знаÑение, ÑÑо иOriginAccess-Control-Allow-Credentialsвtrue
ÐополниÑелÑно, ÑÑÐ¾Ð±Ñ ÑазÑеÑиÑÑ JavaScript доÑÑÑп к лÑбÑм заголовкам оÑвеÑа, кÑоме Cache-Control, Content-Language, Content-Type, Expires, Last-Modified или Pragma, ÑеÑÐ²ÐµÑ Ð´Ð¾Ð»Ð¶ÐµÐ½ пеÑеÑиÑлиÑÑ ÑазÑеÑÑннÑе в заголовке Access-Control-Expose-Headers.
ÐÐ»Ñ Ð½ÐµÐ¿ÑоÑÑÑÑ Ð·Ð°Ð¿ÑоÑов пеÑед оÑновнÑм запÑоÑом оÑпÑавлÑеÑÑÑ Ð¿ÑедзапÑоÑ:
- â ÐÑаÑÐ·ÐµÑ Ð¿Ð¾ÑÑÐ»Ð°ÐµÑ Ð·Ð°Ð¿ÑоÑ
OPTIONSна ÑÐ¾Ñ Ð¶Ðµ адÑÐµÑ Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ°Ð¼Ð¸:Access-Control-Request-Methodâ ÑодеÑÐ¶Ð¸Ñ Ð·Ð°Ð¿ÑаÑиваемÑй меÑод,Access-Control-Request-Headersâ пеÑеÑиÑлÑÐµÑ Ð½ÐµÐ¿ÑоÑÑÑе запÑаÑиваемÑе заголовки.
- â СеÑÐ²ÐµÑ Ð´Ð¾Ð»Ð¶ÐµÐ½ оÑвеÑиÑÑ Ñо ÑÑаÑÑÑом 200 и заголовками:
Access-Control-Allow-MethodsÑо ÑпиÑком ÑазÑеÑÑннÑÑ Ð¼ÐµÑодов,Access-Control-Allow-HeadersÑо ÑпиÑком ÑазÑеÑÑннÑÑ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ¾Ð²,Access-Control-Max-AgeÑ ÐºÐ¾Ð»Ð¸ÑеÑÑвом ÑекÑнд Ð´Ð»Ñ ÐºÐµÑиÑÐ¾Ð²Ð°Ð½Ð¸Ñ ÑазÑеÑений
- â ÐаÑем оÑпÑавлÑеÑÑÑ Ð¾Ñновной запÑоÑ, пÑименÑеÑÑÑ Ð¿ÑедÑдÑÑÐ°Ñ Â«Ð¿ÑоÑÑаÑ» ÑÑ ÐµÐ¼Ð°.
ÐомменÑаÑии
<code>, Ð´Ð»Ñ Ð½ÐµÑколÑÐºÐ¸Ñ ÑÑÑок кода — Ñег<pre>, еÑли болÑÑе 10 ÑÑÑок — ÑÑÑÐ»ÐºÑ Ð½Ð° пеÑоÑниÑÑ (plnkr, JSBin, codepenâ¦)