cross origin な ajax 通信したい
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ webサーバを立ててそこを通す
◆ クライアントサイドで fetch を使う場合は web サーバから Access-Control-Allow-Origin の http header で接続元を許可しないとだめ
◆ credentials 送るときは Access-Control-Allow-Origin に "*" が使えない
◆ referrer からオリジン作ればどこからでも許可できる
◆ credentials 送るときは Access-Control-Allow-Credentials に true の設定必要
◆ chrome の拡張機能で ajax 通信を任意の URL へリダイレクト
◆ クライアントサイドで fetch を使う場合は web サーバから Access-Control-Allow-Origin の http header で接続元を許可しないとだめ
◆ credentials 送るときは Access-Control-Allow-Origin に "*" が使えない
◆ referrer からオリジン作ればどこからでも許可できる
◆ credentials 送るときは Access-Control-Allow-Credentials に true の設定必要
◆ chrome の拡張機能で ajax 通信を任意の URL へリダイレクト
xhr や fetch の ajax 通信で別オリジンのデータがほしいことってありますよね
特に xxx へのリクエストを yyy に置き換えて別のデータを取得して使うとか
もちろん通常は クロスオリジンはだめと怒られるわけです
なのでいつもどおり 拡張機能に頼ります
拡張機能部分は簡単に
manifest.json では background スクリプトを実行するようにします
それと permission に webRequest と webRequestBlocking を入れます
background.js の方では onBeforeRequest にリスナをつけて xmlhttprequest なら URL を変換してそこにリダイレクトするようにします
fetch でも xmlhttprequest になります
↑だと <all_urls> なので全部の URL に対して実行されます
必要なところだけに絞ることができます
ここに書けるパターンの書き方はちょっと複雑なので 関数の中で if 文で書くほうが楽ですし 自由度高いです
これで 特定の URL へのアクセスを好きなところへリダイレクトすることができるようになりました
ですが あくまで リダイレクトなんです
xxx への ajax 通信を yyy に書き換えられますが クロスオリジンの制限は解除できていません
apache の mod_rewrite みたいに アクセス先は xxx だとして扱ってるけど中身だけ yyy に置き換わってる となるといいのですけどそうはいかないです
なので いったん自分で立てたサーバへリダイレクトして 自分のサーバで目的のデータを取ってくるようにします
とりあえずサーバは Node.js で
これを実行しておいて var.blog.jp のページから
これだと 普通にクロスドメイン通信しているので fetch のエラーです
エラーにもあるように Access-Control-Allow-Origin を使って クロスオリジンを許可します
目的は拡張機能使ってどこの URL から来てもいいようにすることなので "*" を指定しました
すると
credentials を送る場合は "*" が使えないと言われました
今回の例では普通に全公開されてるブログなので credentials はいらないですが クッキーを保持しないといけないログインが関係する場合は credentials が必要です
credentials がいるものとして 仕方なく "*" をやめて オリジンの "http://var.blog/jp" にします
今度は
Access-Control-Allow-Credentials を true にしろという指令
ここまでやると
やっとでました
ちょっと面倒ですが 拡張機能で URL を置き換えたいときにはこのサーバも起動しておけば ajax のレスポンスを置き換えることができます
ただ やっぱり Access-Control-Allow-Origin に文字列で書いておくのは決まったページのみになりますし どこからでもできるようにしたいです
最終的に header にアクセス元のオリジンが出力できればいいので referrer からオリジンを取得します
これで referrer が送られていればどこから来てもクロスドメイン許可できます
ところで Node.js は JavaScript なのですが ブラウザサイドと違い http header の "referrer" は "referer" となっています
規格の方に合わせているようです
ブラウザサイドでは fetch でも "referrer" に統一してくれていていいのですけどね
スペルミスなわけですし "referrer" とユーザは書くようにして内部の変換で最終的に送るデータで "referer" に書き換えればいいのにと思います
ところで XHR では
"Access-Control-Allow-Origin": "*" だけで OK でした
なんと簡単!
fetch は便利ですが 面倒な制限も増えているのですね
特に xxx へのリクエストを yyy に置き換えて別のデータを取得して使うとか
もちろん通常は クロスオリジンはだめと怒られるわけです
なのでいつもどおり 拡張機能に頼ります
拡張機能部分は簡単に
manifest.json では background スクリプトを実行するようにします
それと permission に webRequest と webRequestBlocking を入れます
"background":{
"scripts": ["background.js"]
},
"permissions": [ "<all_urls>", "webRequest", "webRequestBlocking" ]
"scripts": ["background.js"]
},
"permissions": [ "<all_urls>", "webRequest", "webRequestBlocking" ]
background.js の方では onBeforeRequest にリスナをつけて xmlhttprequest なら URL を変換してそこにリダイレクトするようにします
fetch でも xmlhttprequest になります
chrome.webRequest.onBeforeRequest.addListener(function(details){
if(details.type === "xmlhttprequest"){
const url = convertURL(details.url)
return {redirectUrl: url}
}
}, {urls: ["<all_urls>"]}, ["blocking"])
if(details.type === "xmlhttprequest"){
const url = convertURL(details.url)
return {redirectUrl: url}
}
}, {urls: ["<all_urls>"]}, ["blocking"])
↑だと <all_urls> なので全部の URL に対して実行されます
必要なところだけに絞ることができます
ここに書けるパターンの書き方はちょっと複雑なので 関数の中で if 文で書くほうが楽ですし 自由度高いです
これで 特定の URL へのアクセスを好きなところへリダイレクトすることができるようになりました
ですが あくまで リダイレクトなんです
xxx への ajax 通信を yyy に書き換えられますが クロスオリジンの制限は解除できていません
apache の mod_rewrite みたいに アクセス先は xxx だとして扱ってるけど中身だけ yyy に置き換わってる となるといいのですけどそうはいかないです
なので いったん自分で立てたサーバへリダイレクトして 自分のサーバで目的のデータを取ってくるようにします
とりあえずサーバは Node.js で
> npm install node-fetch
const fetch = require("node-fetch")
require("http").createServer((req, res) => {
res.writeHead(200, {
"Content-Type": "application/json"
})
fetch(req.url.substr(1))
.then(res => res.text())
.then(res.end.bind(res))
}).listen(4567)
require("http").createServer((req, res) => {
res.writeHead(200, {
"Content-Type": "application/json"
})
fetch(req.url.substr(1))
.then(res => res.text())
.then(res.end.bind(res))
}).listen(4567)
これを実行しておいて var.blog.jp のページから
fetch("http://localhost:4567/http://var.blog.jp", {credentials:"include"})
.then(e => e.text())
.then(e => console.log(e.substr(0,100)))
を実行してみます.then(e => e.text())
.then(e => console.log(e.substr(0,100)))
Fetch API cannot load http://localhost:4567/http://var.blog.jp.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://var.blog.jp' is therefore not allowed access.
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://var.blog.jp' is therefore not allowed access.
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
これだと 普通にクロスドメイン通信しているので fetch のエラーです
エラーにもあるように Access-Control-Allow-Origin を使って クロスオリジンを許可します
const fetch = require("node-fetch")
require("http").createServer((req, res) => {
res.writeHead(200, {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*"
})
fetch(req.url.substr(1))
.then(res => res.text())
.then(res.end.bind(res))
}).listen(4567)
require("http").createServer((req, res) => {
res.writeHead(200, {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*"
})
fetch(req.url.substr(1))
.then(res => res.text())
.then(res.end.bind(res))
}).listen(4567)
目的は拡張機能使ってどこの URL から来てもいいようにすることなので "*" を指定しました
すると
Fetch API cannot load http://localhost:4567/http://var.blog.jp.
A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true.
Origin 'http://var.blog.jp' is therefore not allowed access.
A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true.
Origin 'http://var.blog.jp' is therefore not allowed access.
credentials を送る場合は "*" が使えないと言われました
今回の例では普通に全公開されてるブログなので credentials はいらないですが クッキーを保持しないといけないログインが関係する場合は credentials が必要です
credentials がいるものとして 仕方なく "*" をやめて オリジンの "http://var.blog/jp" にします
const fetch = require("node-fetch")
require("http").createServer((req, res) => {
res.writeHead(200, {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "http://var.blog.jp"
})
fetch(req.url.substr(1))
.then(res => res.text())
.then(res.end.bind(res))
}).listen(4567)
require("http").createServer((req, res) => {
res.writeHead(200, {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "http://var.blog.jp"
})
fetch(req.url.substr(1))
.then(res => res.text())
.then(res.end.bind(res))
}).listen(4567)
今度は
Fetch API cannot load http://localhost:4567/http://var.blog.jp.
Credentials flag is 'true', but the 'Access-Control-Allow-Credentials' header is ''.
It must be 'true' to allow credentials.
Origin 'http://var.blog.jp' is therefore not allowed access.
Credentials flag is 'true', but the 'Access-Control-Allow-Credentials' header is ''.
It must be 'true' to allow credentials.
Origin 'http://var.blog.jp' is therefore not allowed access.
Access-Control-Allow-Credentials を true にしろという指令
const fetch = require("node-fetch")
require("http").createServer((req, res) => {
res.writeHead(200, {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "http://var.blog.jp",
"Access-Control-Allow-Credentials": true
})
fetch(req.url.substr(1))
.then(res => res.text())
.then(res.end.bind(res))
}).listen(4567)
require("http").createServer((req, res) => {
res.writeHead(200, {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "http://var.blog.jp",
"Access-Control-Allow-Credentials": true
})
fetch(req.url.substr(1))
.then(res => res.text())
.then(res.end.bind(res))
}).listen(4567)
ここまでやると
<!DOCTYPE html>
<html lang="ja" itemscope itemtype="http://schema.org/Blog">
<head>
<meta http-equiv
<html lang="ja" itemscope itemtype="http://schema.org/Blog">
<head>
<meta http-equiv
やっとでました
ちょっと面倒ですが 拡張機能で URL を置き換えたいときにはこのサーバも起動しておけば ajax のレスポンスを置き換えることができます
ただ やっぱり Access-Control-Allow-Origin に文字列で書いておくのは決まったページのみになりますし どこからでもできるようにしたいです
最終的に header にアクセス元のオリジンが出力できればいいので referrer からオリジンを取得します
const fetch = require("node-fetch")
const url = require("url")
require("http").createServer((req, res) => {
const referrer = url.parse(req.headers.referer)
res.writeHead(200, {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": referrer.protocol + "//" + referrer.host,
"Access-Control-Allow-Credentials": true
})
fetch(req.url.substr(1))
.then(res => res.text())
.then(res.end.bind(res))
}).listen(4567)
const url = require("url")
require("http").createServer((req, res) => {
const referrer = url.parse(req.headers.referer)
res.writeHead(200, {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": referrer.protocol + "//" + referrer.host,
"Access-Control-Allow-Credentials": true
})
fetch(req.url.substr(1))
.then(res => res.text())
.then(res.end.bind(res))
}).listen(4567)
これで referrer が送られていればどこから来てもクロスドメイン許可できます
ところで Node.js は JavaScript なのですが ブラウザサイドと違い http header の "referrer" は "referer" となっています
規格の方に合わせているようです
ブラウザサイドでは fetch でも "referrer" に統一してくれていていいのですけどね
スペルミスなわけですし "referrer" とユーザは書くようにして内部の変換で最終的に送るデータで "referer" に書き換えればいいのにと思います
ところで XHR では
const fetch = require("node-fetch")
const url = require("url")
require("http").createServer((req, res) => {
const referrer = url.parse(req.headers.referer)
res.writeHead(200, {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
})
fetch(req.url.substr(1))
.then(res => res.text())
.then(res.end.bind(res))
}).listen(4567)
const url = require("url")
require("http").createServer((req, res) => {
const referrer = url.parse(req.headers.referer)
res.writeHead(200, {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
})
fetch(req.url.substr(1))
.then(res => res.text())
.then(res.end.bind(res))
}).listen(4567)
const x = new XMLHttpRequest()
x.open("GET", "http://localhost:4567/http://var.blog.jp")
x.send(null)
x.onload = eve => console.log(x.responseText.substr(0,100))
x.open("GET", "http://localhost:4567/http://var.blog.jp")
x.send(null)
x.onload = eve => console.log(x.responseText.substr(0,100))
<!DOCTYPE html>
<html lang="ja" itemscope itemtype="http://schema.org/Blog">
<head>
<meta http-equiv
<html lang="ja" itemscope itemtype="http://schema.org/Blog">
<head>
<meta http-equiv
"Access-Control-Allow-Origin": "*" だけで OK でした
なんと簡単!
fetch は便利ですが 面倒な制限も増えているのですね