質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.34%

異なるドメインへjsonを送信する方法について

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,574

score 95

実現したいこと  

異なるドメインへjsonをPOSTリクエストしたいです。
具体的には、サーバ"A"からレスポンスとしてjsonをブラウザで受け取り、それをサーバ"B"へPOST送信したいです。
条件詳細については以降に記載しますが、このような場合受信側サーバBがjsonデータをどのようなパラメータ名で受け取るかわからない限り、実現することはできないのでしょうか?

 ブラウザ ====> 【サーバA】 ====> ブラウザ ====> 【サーバB】     

上記実現の為の条件は以下の通りです。
・サーバ間で直接やり取りは行わず、UA(ブラウザ)を介してPOST通信を行う。
・サーバBはjsonを受け取れることしかわからない。(受取るkey名は不明である)
・サーバBでは、access-control-allow-originは許可されていない。

背景

OAuth2.0を学んでいるのですが、アクセストークン取得までに行うパラメータのやり取りに1つ疑問が浮かびました。
OAuth2.0ではHTTPS通信が前提の為、GETでやり取りしている所が多く見受けられますが、POST通信でも同様にjsonのやり取り(実際はJWTでしょうが...)ができるのか疑問に思い実践しています。
そもそもOAuthにおけるJWTはHTTPヘッダのAuthenticationフィールドに付加するものであり、前述のような考え自体が仕様に反している等、OAUthに関する指摘でも構いませんので、ご教授頂けると幸いです。

試したこと

サーバA及び疑似リダイレクトで使用するjsを実装対象としています。

<!-- firstSend.html -->

<form action="/redirect" method="post">
    <input type="hidden" id="result" value=""/>
    <input type="submit"/>
</form>
<script>
    document.addEventListener("DOMContentLoaded",()=>{
        var data={name: "名前", age:100};
        let json = JSON.stringify(data);
        const xhr = new XMLHttpRequest();
        xhr.open("POST","/send");

        xhr.setRequestHeader('content-type','application/json;charset=UTF-8');
        xhr.onreadystatechange = function(){
            if(xhr.readyState === 4){
                if(xhr.status === 200){
                    console.log("OK");
                    let input = document.getElementById("result");
                    input.value = xhr.responseText;
                    //window.location.href = "localhots:8080/redirect";
                    document.forms[0].submit(); 
                }
            }else{
                console.log("通信中");
            }
        };
        xhr.send(encodeURIComponent(json));
    });
</script>
//サーバAに相当

@PostMapping(value="/send", consumes = "application/json")
@ResponseBody
public String ajax(HttpServletRequest req, HttpServletResponse res){
    String data = null;
    StringBuilder sb = new StringBuilder();
    try{
        BufferedReader br = req.getReader();
        while((data = br.readLine())!= null){
            sb.append(data);
        }
    }catch(IOException e){
        e.printStackTrace();
    }

    data = new URLDecoder().decode(sb.toString(),StandardCharsets.UTF_8);
    Gson gson = new Gson();
    Type type = new TypeToken<Map<String,Object>>(){}.getType();
    Map<String,Object> map = gson.fromJson(data,type);
    map.entrySet().stream().forEach(e->{
        System.out.print(e.getKey()+":");
        System.out.println(map.get(e.getKey()));
    });
    map.put("sex","male");
    String result = gson.toJson(map);

    return result;
}
//サーバBに相当

@PostMapping(value="/redirect")
public String receive(HttpServletRequest req, HttpServletResponse res){
    //jsonを取得する処理。サーバBではあくまでjsonを取得していることしかわからない。

環境

java OpenJDK-12
Spring Boot 2.1.8

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • takasima20

    2019/09/16 22:05

    データ量が少ないなら適当な input タグにつっこんで、そのまま post すればいいんじゃないスかね。

    キャンセル

  • kanetugu_70e

    2019/09/16 22:23

    「inputタグに突っ込んでpost」となるとform送信になりますが、この場合受け取り側(今回でいうサーバB)はパラメータを指定しないとjsonを取り出せないですよね?
    記載通りの繰り返しになりますが、送信するjsonに名前付けはできないという条件があります。
    あくまでjsonのみを送りたいです。

    キャンセル

  • takasima20

    2019/09/16 22:59

    POST はしたいが FORM タグは使いたくない、と。ムリじゃね?

    キャンセル

回答 1

checkベストアンサー

+1

XHRでの他ドメインへのアクセス禁止はブラウザ側の仕様です。
これを素直にクリアするにはサーバ側でAccess-Control-Allow-Originを正しく設定する他ありません。

よって

このような場合受信側サーバBがjsonデータをどのようなパラメータ名で受け取るかわからない限り、実現することはできないのでしょうか?

POSTされるかどうかはサーバ側でAccess-Control-Allow-Originが設定されているかどうかが条件であって、jsonの形式は関係ありません。
サーバ側アプリケーションからすればjsonを含むHTTPリクエストはただの文字列なので、受け取りさえ出来ればどうにでもなります。

Javaでの実装については知りませんが、どんな言語でも(普通はそこまでする必要は無いと思いますが)最悪でも生のHTTPリクエストを自力でパースすればデータを取得出来ます。

ブラウザの開発者ツールを使って、実際に送信されているデータを確認してみると原因と対策を把握しやすいかと思いますよ。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/09/17 01:22 編集

    ご回答頂きありがとうございます。

    条件の3番目の通り、サーバBは`Access-Control-Allow-Origin`を設定していない為、ajaxによるPOSTは使うことができません。
    その為、先の回答者様及び私の実装記載例の様にFormによるPOST通信が考えられます。しかしこれまた条件の通りですが、jsonが紐づいているkey名をサーバBが知る事はできません。
    このような状況で、json文字列だけを受け取る方法はあるのでしょうか?(key名などは含めないでjsonだけを受け取れるか)

    もしくは、tanat様がおっしゃる自力パースとは、「他のform要素やkey名が混じった文字列をパースして、json部分だけを抽出する」といった意味合いでしょうか?

    お手数ですがご確認の程宜しくお願い致します。

    キャンセル

  • 2019/09/17 01:50 編集

    どういった状況でそういった制限下での開発が必要になるかは想像がつきませんが、(背景として記載されている部分ともどうつながるかわかりません)

    > もしくは、tanat様がおっしゃる自力パースとは、「他のform要素やkey名が混じった文字列をパースして、json部分だけを抽出する」といった意味合いでしょうか?

    というのはその通りです。
    POSTとして受け取ったすべてのキーの値をJSONとしてパースしてみて、サーバ側が望むJSONが取得出来たらそれが正しいとみなす くらいの方法しかないでしょうね。
    *もし、制約が実は「サーバ側のソースを修正できない」というということであれば方法は存在しません。(質問の条件下では。)

    HTML formでのJSONの送信については
    この辺 https://www.w3.org/TR/html-json-forms/ を見ると検討された形跡はありますが、その後廃止されているので、この仕様を実装しているブラウザが存在するかは不明です。(多分無いと思います)

    * 開発時にクロスドメインになってしまうのでそれをどうにかしたいというだけであれば、
    https://teratail.com/questions/210879
    この辺の方法でなんとかなります。

    キャンセル

  • 2019/09/17 03:06

    質問中の条件を変えていいのであれば、サーバAで一旦POSTを受け取ってからサーバ間通信でサーバBにJSONを送信すれば可能です(Access-Control-Allow-Originが設定されないAPIはそういった利用方法を想定しています)

    キャンセル

  • 2019/09/17 07:38 編集

    私の経験が浅く、今回のような条件で通信可能であるかが分からない(何か私が知らない方法があるかもしれない)為、質問させて頂きました。
    その為、"実現不可"も私が求めていた回答の一つになります。

    サーバ間通信についても、サーバAに余計な穴を開けたくない為、今回は考えておりませんでした。

    ともあれ、頂いた回答及びリンクで私自身納得する事が出来ました。
    背景、目的共に分り辛い質問にも関わらずお付き合い頂きありがとうございました!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.34%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る