web-dev-qa-db-ja.com

JSONPおよびjQueryでPUT / POST / DELETEを使用する

クロスドメインリクエスト、JSON/JSONPサポート、およびメインHTTPメソッド(PUT/GET/POST/DELETE)をサポートするRESTful APIの作成に取り組んでいます。サーバー側のコードを介してこのAPIに簡単にアクセスできるようになりましたが、javascriptに公開するのは良いことです。私が知ることができることから、jQueryでJSONPリクエストを行うとき、それはGETメソッドのみをサポートします。 POST/PUT/DELETEを使用してJSONPリクエストを行う方法はありますか?

理想的には、jQuery内から(コアがこれをサポートしていない場合はプラグインを使用して)これを実行する方法が必要ですが、プレーンなJavaScriptソリューションも使用します。動作中のコードへのリンク、またはコーディング方法は参考になります。ありがとうございます。

38
ryanzec

実際には、POST要求をサポートする方法があります。PROXIサーバーには必要ありません。以下に説明する小さなユーティリティHTMLページだけです。

以下は、有効なPOSTクロスドメインコール(添付ファイル、マルチパート、すべてを含む):)

最初にunderstandアイデアのステップを示します。その後、実装サンプルを見つけます。

jQueryのJSONPの実装方法と、なぜPOSTリクエスト?をサポートしないのか

従来のJSONPは、スクリプト要素を作成してDOMに追加することで実装されますが、ブラウザがHTTPリクエストを実行してタグのソースを取得し、JavaScriptとして実行するように強制する結果、ブラウザが起動するHTTPリクエスト単純なGETです。

GETリクエストに限定されないものは何ですか?

フォーム。 actionクロスドメインサーバーを指定しながら、フォームを送信します。 FORMタグは、スクリプトを使用して完全に作成し、スクリプトを使用してすべてのフィールドを入力し、必要なすべての属性を設定し、DOMに挿入して送信します。すべてスクリプトを使用します。

しかし、ページを更新せずにフォームを送信するにはどうすればよいですか?

同じページのIFRAMEにtargetフォームを指定します。スクリプトを使用して、IFRAMEを作成、設定、名前付け、DOMに挿入することもできます。

しかし、この作業をユーザーから隠すにはどうすればよいですか?style="display:none"を使用して、非表示のDIVにFORMとIFRAMEの両方を含めます

(そして、ここがテクニックの最も複雑な部分です、我慢してください)

しかし、別のドメインからのIFRAMEは、そのトップレベルドキュメントでコールバックを呼び出すことができません。それを克服する方法は?

実際、FORMサブミットからの応答が別のドメインからのページである場合、トップレベルページとIFRAME内のページ間のスクリプト通信は「アクセス拒否」になります。そのため、サーバーはスクリプトを使用してコールバックできません。サーバーは何ができますか? リダイレクト。サーバーは、トップレベルドキュメントと同じドメイン内のページを含む任意のページにリダイレクトできます。このページは、コールバックを呼び出すことができます。

サーバーはどのようにリダイレクトできますか?

二通り:

  1. <Script>location.href = 'some-url'</script>などのクライアント側スクリプトを使用する
  2. HTTPヘッダーを使用します。参照: http://www.webconfs.com/how-to-redirect-a-webpage.php

だから私は別のページになりますか?どのように役立ちますか?

これは、すべてのクロスドメインコールから使用されるシンプルなユーティリティページです。実際、このページは実際には一種のプロキシですが、サーバーではなく、メモ帳とブラウザを使用する誰でも使用できるシンプルで静的なHTMLページです。

このページで行う必要があるのは、サーバーからの応答データを使用して、トップレベルドキュメントでコールバックを呼び出すことだけです。クライアント側のスクリプトはすべてのURL部分にアクセスでき、サーバーはその一部としてエンコードされた応答と、呼び出す必要のあるコールバックの名前をそこに置くことができます。意味-このページは静的なHTMLページにすることができ、動的なサーバー側ページである必要はありません:)

このユーティリティページは、実行するURLから(具体的には以下の実装で)Query-Stringパラメーターから情報を取得します(または、アンカーIDを使用して独自の実装を記述できます。つまり、URLの「#」符号)。また、このページは静的であるため、キャッシュすることも許可されます:)

POST DIV、SCRIPT、IFRAMEを要求すると、最終的にメモリリークが発生しますか?

あなたがページに残した場合-それはします。あなたの後に掃除する場合-それはしません。行う必要があるのは、サーバーから応答が到着するかタイムアウトになるたびに、DIVとその中のFORMおよびIFRAMEを整理するために使用できるIDをDIVに与えることです。

何が得られますか?

実質的にPOSTクロスドメインコール、添付ファイル、マルチパートおよびすべてを含む:)

制限は何ですか?

  • サーバーの応答は、リダイレクトに適合するものに制限されます。
  • サーバーは常にPOSTリクエストにリダイレクトを返す必要があります。404および500エラーを含みます。または、リクエストを実行する直前にクライアントでタイムアウトを作成します。返されていないリクエストを検出します。
  • 全員がこのすべてと関連するすべての段階を理解できるわけではありません。それは一種のインフラストラクチャレベルの作業ですが、一度実行すると、揺れます:)

PUTおよびDELETE呼び出しに使用できますか?

FORMタグはPUTおよびDELETEしません。しかし、それは何よりも良いです:)

OK、コンセプトを得た。技術的にはどうですか?

私がやることは:

DIVを作成し、非表示としてスタイルを設定し、DOMに追加します。また、サーバーの応答が到着した後にDOMからクリーンアップできるIDを提供します(JQueryがJSONP SCRIPT tasgs-DIVをクリーンアップするのと同じ方法)。

次に、I compose IFRAMEとFORMの両方を含む文字列-すべての属性、プロパティ、および入力フィールドを使用して、非表示のDIVに挿入します。 divがDOMに入った後にのみ、この文字列をDIVに挿入することが重要です。そうでない場合-すべてのブラウザで動作しません。

その後-フォームへの参照を取得して送信します。その前の1行を覚えておいてください-サーバーが応答しない場合、または間違った方法で応答する場合にTimeoutコールバックを設定します。

コールバック関数には、クリーンアップコードが含まれています。また、応答タイムアウトの場合にタイマーによって呼び出されます(サーバー応答が到着したときにタイムアウトタイマーを消去します)。

コードを見せてください!

以下のコードスニペットは、「純粋な」JavaScriptで完全に「中立」であり、必要なユーティリティを宣言します。アイデアの説明を簡単にするために-それはすべてグローバルスコープで実行されますが、もう少し洗練されたものでなければなりません...

必要に応じて関数でそれを整理し、必要なものをパラメータ化します-しかし、お互いを見る必要があるすべての部分が同じスコープで実行されるようにしてください:)

この例では、クライアントが http://samedomain.com で実行され、サーバーが http://crossdomain.com で実行されると仮定します。

最上位ドキュメントのスクリプトコード

//declare the Async-call callback function on the global scope
function myAsyncJSONPCallback(data){
    //clean up 
    var e = document.getElementById(id);
    if (e) e.parentNode.removeChild(e);
    clearTimeout(timeout);

    if (data && data.error){
        //handle errors & TIMEOUTS
        //...
        return;
    }

    //use data
    //...
}

var serverUrl          = "http://crossdomain.com/server/page"
  , params = { param1  : "value of param 1"      //I assume this value to be passed
             , param2  : "value of param 2"      //here I just declare it...
             , callback: "myAsyncJSONPCallback" 
             }
  , clientUtilityUrl   = "http://samedomain.com/utils/postResponse.html"
  , id     = "some-unique-id"// unique Request ID. You can generate it your own way
  , div    = document.createElement("DIV")       //this is where the actual work start!
  , HTML   = [ "<IFRAME name='ifr_",id,"'></IFRAME>"  
             , "<form target='ifr_",id,"' method='POST' action='",serverUrl 
             , "' id='frm_",id,"' enctype='multipart/form-data'>"
             ]
  , each, pval, timeout;

//augment utility func to make the array a "StringBuffer" - see usage bellow
HTML.add = function(){
              for (var i =0; i < arguments.length; i++) 
                  this[this.length] = arguments[i];
           }

//add rurl to the params object - part of infrastructure work
params.rurl = clientUtilityUrl //ABSOLUTE URL to the utility page must be on
                               //the SAME DOMAIN as page that makes the request

//add all params to composed string of FORM and IFRAME inside the FORM tag  
for(each in params){
    pval = params[each].toString().replace(/\"/g,"&quot;");//assure: that " mark will not break
    HTML.add("<input name='",each,"' value='",pval,"'/>"); //        the composed string      
}
//close FORM tag in composed string and put all parts together
HTML.add("</form>");
HTML = HTML.join("");   //Now the composed HTML string ready :)

//prepare the DIV
div.id = id; // this ID is used to clean-up once the response has come, or timeout is detected
div.style.display = "none"; //assure the DIV will not influence UI

//TRICKY: append the DIV to the DOM and *ONLY THEN* inject the HTML in it
//        for some reason it works in all browsers only this way. Injecting the DIV as part 
//        of a composed string did not always work for me
document.body.appendChild(div);
div.innerHTML = HTML;

//TRICKY: note that myAsyncJSONPCallback must see the 'timeout' variable
timeout = setTimeout("myAsyncJSONPCallback({error:'TIMEOUT'})",4000);
document.getElementById("frm_"+id+).submit();

クロスドメイン上のサーバーサーバーからの応答は、HTTP-HeaderまたはSCRIPTタグの作成によるリダイレクトであると予想されます。 (リダイレクトが優れている、SCRIPTタグはJSブレークポイントを使用してデバッグする方が簡単です)。上記のrurl値を想定したヘッダーの例は次のとおりです。

Location: http://samedomain.com/HTML/page?callback=myAsyncJSONPCallback&data=whatever_the_server_has_to_return

ご了承ください

  • data引数の値はJavaScript Object-LiteralまたはJSON式にすることができますが、URLエンコードする方が適切です。
  • サーバーの応答の長さは、ブラウザーが処理できるURLの長さに制限されます。

また、私のシステムでは、サーバーにはrurlのデフォルト値があるため、このパラメーターはオプションです。ただし、これは、クライアントアプリケーションとサーバーアプリケーションが結合されている場合にのみ可能です。

リダイレクトヘッダーを発行するAPI:

http://www.webconfs.com/how-to-redirect-a-webpage.php

または、サーバーに応答として次のように書き込ませることもできます。

<script>
   location.href="http://samedomain.com/HTML/page?callback=myAsyncJSONPCallback&data=whatever_the_server_has_to_return"
</script>

ただし、HTTPヘッダーはよりクリーンであると見なされます;)

トップレベルドキュメントと同じドメインのユーティリティページ

すべての投稿要求にrurlと同じユーティリティページを使用します。クライアント側コードを使用してQuery-Stringからコールバックの名前とパラメーターを取得し、親ドキュメントで呼び出します。このページがリクエストを実行したページと同じドメインで実行されている場合にのみ、それを行うことができます[〜#〜] only [〜#〜]重要:Cookieとは異なり、サブドメインはカウントされません!!彼にはまったく同じドメインが必要です。

また、このユーティリティページに、JSライブラリを含む他のリソースへの参照が含まれていない場合は、より効率的になります。したがって、このページは単純なJavaScriptです。ただし、好きなように実装できます。

私が使用するレスポンダーページは次のとおりです。URLはPOSTリクエストのrurlにあります(例では http://samedomain.com/ utils/postResponse.html

<html><head>
<script type="text/javascript">
//parse and organize all QS parameters in a more comfortable way
var params = {};
if (location.search.length > 1) {
    var i, arr = location.search.substr(1).split("&");
    for (i = 0; i < arr.length; i++) {
        arr[i] = arr[i].split("=");
        params[arr[i][0]] = unescape(arr[i][1]);
    }
}

//support server answer as JavaScript Object-Literals or JSON:
//  evaluate the data expression
try { 
    eval("params.data = " + params.data); 
} catch (e) { 
    params.data = {error: "server response failed with evaluation error: " + e.message
                  ,data : params.data
                  }
}

//invoke the callback on the parent
try{
     window.parent[ params.callback ](params.data || "no-data-returned");
}catch(e){
     //if something went wrong - at least let's learn about it in the
     //      console (in addition to the timeout)
     throw "Problem in passing POST response to Host page: \n\n" + e.message;
}
</script>
</head><body></body></html>

それはあまり自動化されておらず、jQueryのような「既製の」ライブラリーであり、「手動」の作業を伴いますが、魅力があります:)

既製のライブラリの熱心なファンなら-Dojo Toolkitもチェックできます。最後にチェックしたとき(約1年前)-同じメカニズムの独自の実装がありました。 http://dojotoolkit.org/

幸運の相棒、私はそれが役立つことを願っています...

70

POST/PUT/DELETEを使用してJSONPリクエストを行う方法はありますか?

ありません。

13
Darin Dimitrov

いいえ。JSONPとは何かを検討してください。新しい<script>タグがドキュメントにあります。ブラウザはGETリクエストを実行して、src属性が指すスクリプトをプルします。これを行うときに他のHTTP動詞を指定する方法はありません。

8
Wayne Burkett
  • JSONPメソッドで頭を叩くのではなく、実際にはPOSTメソッドをデフォルトでサポートしません)、 [〜#〜] cors [〜#〜] =。これにより、従来のプログラミング方法に大きな変更はありません単純なJquery Ajax呼び出しにより、クロスドメインを使用できます。
  • CORSメソッドでは、このアクセスを有効にするために、サーバー側のスクリプトファイルまたはサーバー自体(リモートドメイン内)にヘッダーを追加する必要があります。不要な呼び出しを行うドメインを防止/制限できるため、これは非常に信頼できます。
  • 詳細は wikipedia ページにあります。
2
Ganesh Babu