2012年3月8日木曜日

クロスドメインでXMLHttpRequestを送るときの逃げ道≡(;´Д`)ノ

クロスドメインでHTTPヘッダを送信するってこと
( ゚Д゚)<POSTでもGETでもなく、HTTPヘッダですよ!

おぅふ、こんなやっかいなこと言い出すなんて..._ノ乙(、ン、)_
後悔先に立たず。ありますよね?
( ・`д・´)<あるあるー!

今回はシステム間の遷移のお話です。
『ポートもドメイン名すらも違うWebシステムに任意のHTTPヘッダを送信する』
にはどうしましょうってことです。
状況として
振り返れば数週間前...( ´Д`)。oO(

(゚Д゚)<そちらのシステムから、うちのシステムに遷移させてもらえませんか?
はじまりはこの一言でした。
言い出したのは、うちの最大顧客の小会社でシステム開発しているS社。
無視するわけにはいかないし、
ただ遷移するだけなら簡単そうなので、
ヽ(´ー`)ノ<大丈夫ですよ!へっちゃらです。
って営業さんが返事してました。
(し、僕も大丈夫だと思ってました。)
やりたいことって?
要望を大雑把に言うと、
(1)うちのシステムにユーザがログイン
(2)あるボタンをクリック
(3)S社の開発したシステムに遷移。
ってことです。

普通にリンクはっただけでは、ユーザの識別ができないので、
ユーザを識別するコードを、
S社が用意してくれるアルゴリズムにしたがって暗号化して、
それをS社のシステムに送信するってことでした。

暗号化アルゴリズムもくれるっていうし、
POSTかGETで送信すれば全然たいしたことないっすよね。
って思ってました。
<a href="http://sss/xxx?user=0001">S社のシステムに遷移!</a>

とか、
みたくね。
直前になって...データはHTTPヘッダで送って
で、S社のシステムが本番反映をむかえる1日前、この一言がでたのです。

( ゚Д゚)<POSTでもGETでもなく、HTTPヘッダですよ!
正直。
なにいってんだ(゚Д゚)ゴルァ!!
って思いました。
S社はPOSTででもGETでもなく、
HTTPのヘッダに付加してリクエストを送信してほしかったのです。

[HTTPのヘッダ]
Accept-Language: ja
Referer: http://xxxxxxxx/ooooo.html?44
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible;...
USER: 0001

というのを期待しているようでした。

(゚Д゚≡゚Д゚)<素直にGETでおくれよー!
って。
しかしそこは最大顧客の小会社。どうにかしなければなりません。
『うちの方法と違うので、送信方法おしえてくれませんか?』
とS社にも協力を要請し、
どうしようかと調査開始。
制約~HTTPヘッダの付加とクロスドメイン~
とりあえず制約として、
(1)クライアントで任意のHTTPヘッダを付加する。
(2)ポートもドメインも違うWebシステムにリクエストを送信する。
の2点がありますが、
この二つが互いに足をひっぱりあいます。

任意のHTTPヘッダをクライアントで付加してリクエスト送るためには、
XMLHttpRequestを使う必要があります。
しかし、ポートもドメインもうちのシステムと違うシステムへリクエストを送信するため、
クロスドメイン制約にひっかかります。

一方、普通にGETで送れるならば、
anchorタグに href="http://xxxxx/oooo.jsp?user=0123" とかって指定すれば遷移します。
POSTでsubmitしてもいいですし。
でもそれだとHTTPヘッダは付加できません。

((ミ・♀・ミ)) くまった。

ところで、いいだしっぺのS社はというと、
『サーバでのHTTPヘッダの取得方法』とか、
『サーバでHTTPレスポンスにヘッダを設定する方法』とか、
ちょwwよくわかってないんじゃないのww
って返答ばかりが返って来ました...
いろいろな誤解~HTTPレスポンスでもなく、JSONPでもない~
誤解1
ヽ(´ー`)ノ<HTTPヘッダならいつもサーバで付加してるやん!

( ・`д・´) はいこれはS社と同じ誤解ですね。
HTTPにはリクエストとレスポンスがあります。
誤解1の場合はレスポンスのHTTPヘッダのお話です。

クライアント<-----【HTTP】----- サーバ

こんな感じですね。
たとえば、みんないつも

   Response.ContentType="application/octet-stream;name=file.pdf"
   Response.AddHeader "Content-Transfer-Encoding","base64"
みたいな感じでレスポンスのヘッダに付加してるから、
コレじゃない?って思うわけです。
でも、これではないんですね。

クライアント-----【HTTP】-----> サーバ

今回の場合はコレ。リクエストですね。
やり方としては、
具体的にはJavascriptでXMLHttpRequestを生成してヘッダを加えて、送信します。
xmlReq = new XMLHttpRequest();
xmlReq.open("POST","test.php", true);
xmlReq.setRequestHeader("user", inUser);
xmlReq.send(null);


このtest.phpの部分がhttp://xxxxxxになっていたら...
xmlReq.open("POST","http://xxxxxx", true);
とかになっていたら、クロスドメインとみなされて失敗します。もしくはエラーがでます。

誤解2
ヽ(´ー`)ノ<クロスドメインといえば、JSONPとかでできんじゃないの??

( ・`д・´) はいこれは僕が最初勘違いしていました。
これもおんなじ図で説明します。
JSONPは

クライアント<-----【データ】----- ドメインの違うサーバ

こんな感じでドメインの違うサーバからデータを取得する方法なんですね。
今回はこれ。

クライアント-----【データ】-----> ドメインの違うサーバ

しかもHTTPのリクエストに任意のヘッダを追加しないといけないおまけ付きです。
解決方法...ではなく逃げ道≡(;´Д`)ノ
さすがにここまで制約つくと一筋縄ではいかず、
どっちかの制約を緩和する方法が必要になりました。

で、その緩和する方法とは、
『そちらのドメイン名でアクセスできるHTMLファイルをそっちのサーバに置かせて!』
ってことでした。

つまり、S社のサーバに置かせてもらったHTMLから、
XMLHttpRequestでデータを送信しようって魂胆です。
ドメイン名がS社のシステムと同じならクロスドメインにひっかかりません。
図にするとこんな感じ。

ステップは3つ
(1)うちのサーバにあるリンクをクリックして、S社のサーバにうちが置いたHTMLファイルに遷移
(2)javascriptでURL(http://sss/http_header.html#0001)からユーザ情報を取得
(3)XMLHttpRequestを生成し、HTTPヘッダに取得したユーザ情報を付加、送信

(1)うちのサーバでやるのはリンクをはることだけです。URLのおしりにユーザ情報を付加して。
ちょうどこんな感じですね。 <a href=”http://sss/http_header.html#0001”>リンク</a> ユーザはこのリンクをクリックして、まんまとS社のサーバにとびます。

(2)つぎにうちがS社のサーバにおいた静的なファイルhttp_header.htmlに遷移します。
ここではonloadかなんかのイベントでユーザの情報を取得します。
location.hashを用いれば取得できます。
たとえばこんな感じで。
var user =  String(location.hash).substring(1) //#よりうしろが取得される。

(3)あとはこれをXMLHttpRequestで送信すればOKです。
このHTMLファイルはS社のサーバからクライアントに送られているので、
S社のサーバにあるシステムに存分にデータを送信できます。
クロスドメインなんかこわくないヽ(´ー`)ノわーい
雑ですが、http_header.htmlの中身です。
<html>
<head>
<script language='javascript'>
<!--

function send(){
  xmlHR = new XMLHttpRequest();
  
  xmlHR.onreadystatechange = function() {
    if (xmlHR.readyState == 4) {
      if (xmlHR.status == 200) {
        var page = window.open("", "", "");
        page.document.open();
        page.document.write(xmlHR.responseText);
        page.document.close();
        window.close();
        
      } else {
        var page = window.open("", "", "");
        page.document.open();
        page.document.write("false
<\/body><\/html>");        page.document.close();      }    }  }    xmlHR.open("POST","main.php", true);  xmlHR.setRequestHeader("USER", String(location.hash).substring(1));  xmlHR.send(null); } --> </script> </head> <body onload="send()"> </body> </html>
たしかに
当初の目的とはずれましたが、
どんな要望でもお手上げ状態ではなく、
何かしらの案を出せるもんだなって自信がつきましたっ( ・`д・´)

0 件のコメント:

コメントを投稿