リクエストペイロードではなくフォームデータとしてデータを投稿するにはどうすればよいですか?


523

以下のコードでは、AngularJS $httpメソッドがURLを呼び出し、xsrfオブジェクトを「リクエストペイロード」として送信します(Chromeデバッガーのネットワークタブで説明)。jQuery $.ajaxメソッドは同じ呼び出しを行いますが、xsrfを「フォームデータ」として送信します。

AngularJSにリクエストペイロードではなくフォームデータとしてxsrfを送信させるにはどうすればよいですか?

var url = 'http://somewhere.com/';
var xsrf = {fkey: 'xsrf key'};

$http({
    method: 'POST',
    url: url,
    data: xsrf
}).success(function () {});

$.ajax({
    type: 'POST',
    url: url,
    data: xsrf,
    dataType: 'json',
    success: function() {}
});

1
これは非常に役立つ質問でした。(Content-Typeを変更することにより)ペイロードを文字列として送信できるため、POST / GETの前にOPTIONSを処理する必要がありません。
earthmeLon 14年

同じ質問があります
。URLを

回答:


614

渡される$ httpオブジェクトに次の行を追加する必要があります。

headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}

渡されたデータは、URLエンコードされた文字列に変換する必要があります。

> $.param({fkey: "key"})
'fkey=key'

だからあなたは次のようなものを持っています:

$http({
    method: 'POST',
    url: url,
    data: $.param({fkey: "key"}),
    headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}
})

送信元:https : //groups.google.com/forum/#!msg / angular / 5nAedJ1LyO0 / 4Vj_72EZcDsJ

更新

AngularJS V1.4で追加された新しいサービスを使用するには、を参照してください


3
json>データのurlエンコーディングを自動的に実行する方法、またはすべてのPOSTまたはPUTメソッドに対してこれを指定する方法はありますか?
道徳

51
+1 @mjibson、私がヘッダーを渡すことでさえ私がこれを含むあなたの答えを見るまでは機能しませんでした: var xsrf = $.param({fkey: "key"});それは愚かです、なぜ内部で角張ることができないのですか?
naikus 2013

12
$ .ajaxのデフォルトの動作をより厳密に追跡するには、コンテンツタイプヘッダーでも文字セットを指定する必要がありますheaders: {Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}
Imre

25
代わりにjQueryののparam機能を使用しての、ちょうど$ HTTPリクエストに応じてParamsプロパティを設定し、それはjQuery.param方法がある限りContent-Typeヘッダは、「アプリケーション/ x-www-form-urlencodedで」そのまま何でしょう- stackoverflowのは、 .com / questions / 18967307 /…
2013年

13
@spigはい、jQuery.paramと同じように機能しますが、paramsプロパティを使用すると、application / x-www-を指定した場合でも、本文ではなくリクエストURLの一部としてプロパティがエンコードされます。 form-urlencodedヘッダー。
stian 2014

194

ソリューションでjQueryを使用したくない場合は、これを試すことができます。ここからナブされたソリューションhttps://stackoverflow.com/a/1714899/1784301

$http({
    method: 'POST',
    url: url,
    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    transformRequest: function(obj) {
        var str = [];
        for(var p in obj)
        str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
        return str.join("&");
    },
    data: xsrf
}).success(function () {});

7
この方法はangular 1.2.xで機能します。エレガントでコアのAngleで機能し、jQueryなどの外部ライブラリに依存しないため、これが最良の答えだと思います。
gregtczap 2013

2
$ resourceアクション内でこのメソッドを使用すると問題が発生しました。フォームデータには、$ get、$ saveなどの関数も含まれていました。解決策は、代わりforに使用するステートメントを少し変更することでしたangular.forEach
Anthony

10
$ .param()とは対照的に、このメソッドは配列/オブジェクトに対して再帰的に機能しないことに注意してください。
MazeChaZer 2014年

1
nullまたはundefinedでobj[p]ないことを確認します。それ以外の場合は、値として「null」または「未定義」の文字列を送信することになります。
タミール2016年

1
私は理解していなかったtransformRequest: function(obj)OBJが定義されていないとして、我々はXSRFを渡すと思いますか?いいねtransformRequest: function(xsrf)
Akshay Taru

92

この問題を取り巻く混乱が続いたため、私はそれについてブログ投稿を書くようになりました。この投稿で提案するソリューションは、現在の最高評価のソリューションよりも優れています。これは、$ httpサービス呼び出しのデータオブジェクトのパラメーター化に制限されないためです。つまり、私のソリューションでは、実際のデータオブジェクトを$ http.post()などに渡し続けるだけで、希望する結果を得ることができます。

また、最高評価の回答は$ .param()関数のページに完全なjQueryを含めることに依存していますが、私の解決策はjQueryにとらわれず、純粋なAngularJSに対応しています。

http://victorblog.com/2012/12/20/make-angularjs-http-service-behave-like-jquery-ajax/

お役に立てれば。


10
詳細なブログの+1、しかしこれが必要であるという事実は恐ろしいです...
iwein

4
はい、おそらく2つのレベルで恐ろしいです:1)AngularJSが事実上の(誤解されているとはいえ)標準を更新することを決定したこと、および2)PHP(および他のサーバー側言語を知っている人)が何らかの方法でアプリケーション/ jsonを自動的に検出しないこと入力。:P
エゼキエルビクター

angularjsが自動的にコンテンツタイプに適応し、それに応じてエンコードすることは可能でしょうか?予見されていますか?
unludo 2013

4
私(他の多くの人と同様)は、私のバックエンドASP.NETがこれを「ネイティブに」サポートしていないことに遭遇しました。AngularJSの動作を変更したくない場合(私のAPIはJSONを返すので変更しませんでした。JSONも受け入れないようにしてください。フォームデータよりも柔軟性があるためです)、から読み取って、Request.InputStream任意の方法で処理できます。あなたはそれをしたい。(dynamic使いやすくするために逆シリアル化することを選択しました。)
アイディアカピ2013

2
これは自己完結型の回答ではないため反対票を投じました。良い答えは、どこかにリンクするだけではありません。どのようにして回答します:「常に標的部位に到達できないか、永続的にオフラインになった場合には、重要なリンクの最も関連性の高い部分を、引用します。」
James

83

私は他のいくつかの答えを取り、少しきれいにして、この.config()呼び出しをapp.jsのangular.moduleの最後に置きました:

.config(['$httpProvider', function ($httpProvider) {
  // Intercept POST requests, convert to standard form encoding
  $httpProvider.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";
  $httpProvider.defaults.transformRequest.unshift(function (data, headersGetter) {
    var key, result = [];

    if (typeof data === "string")
      return data;

    for (key in data) {
      if (data.hasOwnProperty(key))
        result.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]));
    }
    return result.join("&");
  });
}]);

1
リソース定義に追加されても、チャームのように機能します。
Kai Mattern '11 / 09/14

3
またunshift()、他の変換が影響を受けないように使用するように注意しました。よくできました。
Aditya MP 2015年

2
完璧!私にとってはうまくいきました!悲しい角度はこれをネイティブにサポートしていません。
spierala 2015年

2
この答えは一番上にあるはずです。他の答えは間違っています。
ホセ・イグナシオ・ヒタ

2
再帰的なエンコーディングはどうですか?
Petah、2015年

58

AngularJS v1.4.0以降$httpParamSerializerドキュメントページに記載されているルールに従って、任意のオブジェクトをHTTPリクエストの一部に変換する組み込みサービスがあります

次のように使用できます。

$http.post('http://example.com', $httpParamSerializer(formDataObj)).
    success(function(data){/* response status 200-299 */}).
    error(function(data){/* response status 400-999 */});

フォームを正しく投稿するには、Content-Typeヘッダーを変更する必要があることに注意してください。これをすべてのPOSTリクエストに対してグローバルに行うには、次のコード(Albireoのハーフアンサーから取得)を使用できます。

$http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";

現在の投稿に対してのみこれを行うにheadersは、リクエストオブジェクトのプロパティを変更する必要があります。

var req = {
 method: 'POST',
 url: 'http://example.com',
 headers: {
   'Content-Type': 'application/x-www-form-urlencoded'
 },
 data: $httpParamSerializer(formDataObj)
};

$http(req);

カスタム$ resourceファクトリーで同じようにするにはどうすればよいですか?
Stilllife

注:アプリをAngular 1.3から1.5にアップグレードします。これは、transformRequestのヘッダーを変更しました。何らかの理由で、上記の方法は私には機能しません。Angularは、URLエンコードされた文字列を二重引用符で囲みます。で解決transformRequest: $httpParamSerializer, data: formDataObj。解決策をありがとう。
PhiLho 2016年

24

動作をグローバルに定義できます。

$http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";

したがって、毎回再定義する必要はありません。

$http.post("/handle/post", {
    foo: "FOO",
    bar: "BAR"
}).success(function (data, status, headers, config) {
    // TODO
}).error(function (data, status, headers, config) {
    // TODO
});

46
あなたの例はとても間違っています...修正しているのはヘッダーだけです。データ自体は引き続きJSONエンコードされ、JSONを読み取ることができない古いサーバーでは読み取れません。
alexk 2013

victorblog.com/2012/12/20/…-これは、$ httpのデフォルトヘッダーをオーバーライドし、オブジェクトをシリアル化されたフォームデータに変換する良い例です。
フェデリコ

20

回避策として、POSTを受信するコードがアプリケーション/ jsonデータに応答するようにするだけです。PHPの場合、以下のコードを追加して、フォームエンコードまたはJSONでPOSTできるようにしました。

//handles JSON posted arguments and stuffs them into $_POST
//angular's $http makes JSON posts (not normal "form encoded")
$content_type_args = explode(';', $_SERVER['CONTENT_TYPE']); //parse content_type string
if ($content_type_args[0] == 'application/json')
  $_POST = json_decode(file_get_contents('php://input'),true);

//now continue to reference $_POST vars as usual

この問題の本当の問題は、サーバー側のAPIであるため、これは、サーバー側の修正の良い例の一つである...ブラボー
ビネーシュ

16

これらの答えは非常識なやり過ぎのように見えますが、時々、単純な方がちょうど良いです:

$http.post(loginUrl, "userName=" + encodeURIComponent(email) +
                     "&password=" + encodeURIComponent(password) +
                     "&grant_type=password"
).success(function (data) {
//...

1
私にとっては、ヘッダーを指定してにContent-Type設定する必要がありましたapplication/x-www-form-urlencoded
Victor Ramos

9

以下の解決策で試すことができます

$http({
        method: 'POST',
        url: url-post,
        data: data-post-object-json,
        headers: {'Content-Type': 'application/x-www-form-urlencoded'},
        transformRequest: function(obj) {
            var str = [];
            for (var key in obj) {
                if (obj[key] instanceof Array) {
                    for(var idx in obj[key]){
                        var subObj = obj[key][idx];
                        for(var subKey in subObj){
                            str.push(encodeURIComponent(key) + "[" + idx + "][" + encodeURIComponent(subKey) + "]=" + encodeURIComponent(subObj[subKey]));
                        }
                    }
                }
                else {
                    str.push(encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]));
                }
            }
            return str.join("&");
        }
    }).success(function(response) {
          /* Do something */
        });

8

postのアダプターサービスを作成します。

services.service('Http', function ($http) {

    var self = this

    this.post = function (url, data) {
        return $http({
            method: 'POST',
            url: url,
            data: $.param(data),
            headers: {'Content-Type': 'application/x-www-form-urlencoded'}
        })
    }

}) 

コントローラなどで使用してください:

ctrls.controller('PersonCtrl', function (Http /* our service */) {
    var self = this
    self.user = {name: "Ozgur", eMail: null}

    self.register = function () {
        Http.post('/user/register', self.user).then(function (r) {
            //response
            console.log(r)
        })
    }

})

$ .paramはjquery abiでのみ使用できます。jsfiddle.net/4n9fao9q/27 $ httpParamSerializerはAngularjsと同等です。
デクスター2017年

7

これと他の関連するもの-AJAXフォームの送信:AngularJS方法 -について説明する本当に素晴らしいチュートリアルがあります

基本的に、POSTリクエストのヘッダーを設定して、フォームデータをURLエンコードされた文字列として送信することを示し、同じ形式で送信されるようにデータを設定する必要があります

$http({
  method  : 'POST',
  url     : 'url',
  data    : $.param(xsrf),  // pass in data as strings
  headers : { 'Content-Type': 'application/x-www-form-urlencoded' }  // set the headers so angular passing info as form data (not request payload)
});

ここでは、jQueryのparam()ヘルパー関数がデータを文字列にシリアル化するために使用されていますが、jQueryを使用しない場合は手動で行うこともできます。


1
リンクに記載されている実際の実装の詳細を提供しなかったため、モデレーターは私の以前の回答を単に削除しました。この回答にある詳細を提供するためにすでに回答を編集していたため、削除するのではなく、最初に詳細を提供するように依頼された方がよかったのです。
robinmitra 14

$.param魔法を行います。jQuery + AngularJSベースのアプリを持っている人のための完璧なソリューション。
dashtinejad


4

Symfony2ユーザーの場合:

これを機能させるためにJavaScriptの何も変更したくない場合は、symfonyアプリでこれらの変更を行うことができます:

Symfony \ Component \ HttpFoundation \ Requestクラスを拡張するクラスを作成します。

<?php

namespace Acme\Test\MyRequest;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\ParameterBag;

class MyRequest extends Request{


/**
* Override and extend the createFromGlobals function.
* 
* 
*
* @return Request A new request
*
* @api
*/
public static function createFromGlobals()
{
  // Get what we would get from the parent
  $request = parent::createFromGlobals();

  // Add the handling for 'application/json' content type.
  if(0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/json')){

    // The json is in the content
    $cont = $request->getContent();

    $json = json_decode($cont);

    // ParameterBag must be an Array.
    if(is_object($json)) {
      $json = (array) $json;
  }
  $request->request = new ParameterBag($json);

}

return $request;

}

}

ここでapp_dev.php(または使用するインデックスファイル)でクラスを使用します

// web/app_dev.php

$kernel = new AppKernel('dev', true);
// $kernel->loadClassCache();
$request = ForumBundleRequest::createFromGlobals();

// use your class instead
// $request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);

これは私にとって本当に便利でした。新しいcreateFromGlobalsは現在完全に機能しています。なぜあなたが反対票を獲得したのかはわかりませんが、削除しました。
リチャード

3

Content-Typeを設定するだけでは不十分で、送信前にフォームデータをURLエンコードします。 $http.post(url, jQuery.param(data))


3

私は現在、AngularJSのgoogleグループで見つけた次のソリューションを使用しています。

$ http
.post( '/ echo / json /'、 'json =' + encodeURIComponent(angular.toJson(data))、{
    ヘッダー:{
        'Content-Type': 'application / x-www-form-urlencoded; charset = UTF-8 '
    }
})。success(function(data){
    $ scope.data = data;
});

PHPを使用しているRequest::createFromGlobals()場合、$ _ POSTは自動的に読み込まれないため、これを読み取るにはSymfony 2 HTTPコンポーネントのようなものを使用する必要があることに注意してください。


2

AngularJSは、http-requestヘッダー内で次のcontent-typeを実行しているため、正しく実行しています。

Content-Type: application/json

私のようなphp、またはSymfony2を使用している場合は、http://silex.sensiolabs.org/doc/cookbook/json_request_body.htmlのように、サーバーの互換性をjson標準に拡張するだけです。

Symfony2の方法(例:DefaultController内):

$request = $this->getRequest();
if (0 === strpos($request->headers->get('Content-Type'), 'application/json')) {
    $data = json_decode($request->getContent(), true);
    $request->request->replace(is_array($data) ? $data : array());
}
var_dump($request->request->all());

利点は、jQueryパラメータを使用する必要がなく、AngularJSをそのようなリクエストを実行するネイティブな方法で使用できることです。


2

完全な答え(角度1.4以降)。依存関係$ httpParamSerializerを含める必要があります

var res = $resource(serverUrl + 'Token', { }, {
                save: { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
            });

            res.save({ }, $httpParamSerializer({ param1: 'sdsd', param2: 'sdsd' }), function (response) {

            }, function (error) { 

            });

1

アプリの設定で-

$httpProvider.defaults.transformRequest = function (data) {
        if (data === undefined)
            return data;
        var clonedData = $.extend(true, {}, data);
        for (var property in clonedData)
            if (property.substr(0, 1) == '$')
                delete clonedData[property];

        return $.param(clonedData);
    };

あなたのリソース要求で-

 headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            }

0

これは直接的な回答ではなく、少し異なる設計の方向性です。

データをフォームとして投稿するのではなく、サーバー側オブジェクトに直接マッピングされるJSONオブジェクトとして投稿するか、RESTスタイルのパス変数を使用します

XSRFキーを渡そうとしているので、どちらのオプションもあなたのケースに適していないかもしれません。このようなパス変数へのマッピングはひどい設計です:

http://www.someexample.com/xsrf/{xsrfKey}

自然にあなたは、あまりにも他のパスにXSRFの鍵を渡したいと思うので/login/book-appointmentなど、あなたが混乱にあなたのきれいなURLを望んでいません

興味深いことに、オブジェクトフィールドとして追加することも適切ではありません。サーバーに渡す各jsonオブジェクトで、フィールドを追加する必要があるためです。

{
  appointmentId : 23,
  name : 'Joe Citizen',
  xsrf : '...'
}

ドメインオブジェクトとの直接のセマンティックアソシエーションを持たない別のフィールドをサーバー側クラスに追加したくないのは確かです。

私の意見では、xsrfキーを渡す最善の方法は、HTTPヘッダーを使用することです。多くのxsrf保護サーバー側Webフレームワークライブラリがこれをサポートしています。たとえば、Java Springでは、X-CSRF-TOKENheader を使用して渡すことができます

AngularのJSオブジェクトをUIオブジェクトにバインドする優れた機能は、フォームをすべて一緒に投稿するという習慣をなくし、代わりにJSONを投稿できることを意味します。JSONはサーバー側オブジェクトに簡単にデシリアライズでき、マップ、配列、ネストされたオブジェクトなどの複雑なデータ構造をサポートします。

配列をフォームペイロードにどのように投稿しますか?多分このように:

shopLocation=downtown&daysOpen=Monday&daysOpen=Tuesday&daysOpen=Wednesday

またはこれ:

shopLocation=downtwon&daysOpen=Monday,Tuesday,Wednesday

どちらもデザインが悪いです。


0

これは私が私の必要性のためにやっていることです、ここでログインデータをフォームデータとしてAPIに送信する必要があり、Javascriptオブジェクト(userData)が自動的にURLエンコードされたデータに変換されています

        var deferred = $q.defer();
        $http({
            method: 'POST',
            url: apiserver + '/authenticate',
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
            transformRequest: function (obj) {
                var str = [];
                for (var p in obj)
                    str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
                return str.join("&");
            },
            data: userData
        }).success(function (response) {
            //logics
            deferred.resolve(response);
        }).error(function (err, status) {
           deferred.reject(err);
        });

これは私のユーザーデータです

var userData = {
                grant_type: 'password',
                username: loginData.userName,
                password: loginData.password
            }

-1

変更する必要がある唯一の方法は、$ httpオブジェクトを作成するときに「data」ではなく「params」プロパティを使用することです。

$http({
   method: 'POST',
   url: serviceUrl + '/ClientUpdate',
   params: { LangUserId: userId, clientJSON: clients[i] },
})

上記の例では、clients [i]は単なるJSONオブジェクトです(決してシリアル化されていません)。:あなたが「のparams」ではなく「データ」を使用した場合の角度は$ httpParamSerializerあなたが使用するためのオブジェクトをシリアル化しますhttps://docs.angularjs.org/api/ng/service/ $ httpParamSerializer


2
データの代わりにparamsを使用することにより、Angularはデータをリクエスト本文の代わりにURLパラメーターに配置します。これはフォーム投稿から期待されるものではありません。
2015

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.