2012-07-11 37 views
495

在下面的代碼中,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() {} 
}); 
+0

這是一個非常有用的問題。它允許我將一個有效載荷作爲一個字符串發送(通過更改Content-Type),這使我無法在POST/GET之前處理OPTIONS。 – earthmeLon 2014-06-04 15:52:10

+0

我有同樣的問題,它是在我請求URL後,但我無法獲得我提交的參數 – 2016-07-31 15:50:17

回答

588

下面的行需要被添加到被傳遞的$ HTTP對象:

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

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

並通過應該被轉換爲一個URL編碼的字符串的數據

所以你有這樣的東西:

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

來源:https://groups.google.com/forum/#!msg/angular/5nAedJ1LyO0/4Vj_72EZcDsJ

+3

有沒有一種方法可以自動發生數據的json> url編碼或爲每個POST指定這種情況或PUT方法? – Dogoku 2012-10-31 13:25:52

+0

@Dogoku,看到我的答案如下:http://stackoverflow.com/a/13490145/91696 – Albireo 2012-11-21 09:33:11

+50

+1 @mjibson,對於我甚至傳遞標題是行不通的,直到我看到你的回答包含這個: 'var xsrf = $ .param({fkey:「key」});'這很愚蠢,爲什麼不能在內部做角? – naikus 2013-02-04 06:44:55

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 
}); 
+44

你的例子是錯誤的...你所修改的只是頭。數據本身仍然是JSON編碼的,並且無法被無法讀取JSON的舊服務器讀取。 – alexk 2013-09-10 13:38:15

+0

http://victorblog.com/2012/12/20/make-angularjs-http-service-behave-like-jquery-ajax/ - 這是一個很好的例子,您可以覆蓋$ http默認標題,以及將對象轉換爲序列化的表單數據。 – Federico 2014-04-24 21:36:40

91

圍繞這一問題的持續混亂啓發了我寫一篇關於它的博客文章。我在這篇文章中提出的解決方案比您當前評分最高的解決方案要好,因爲它不限制您爲$ http服務調用設置數據對象的參數;即使用我的解決方案,您可以簡單地繼續將實際的數據對象傳遞給$ http.post()等,並仍然達到期望的結果。

此外,評分最高的答案依賴於在$ .param()函數的頁面中包含完整的jQuery,而我的解決方案是jQuery不可知的,純粹的AngularJS就緒。

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

希望這有助於。

+10

+1爲詳細的博客,但事實上,這是需要這個是可怕的... – iwein 2013-04-05 07:55:20

+4

是的,也許在兩個層面上可怕:1)AngularJS決定顛倒_de facto_(雖然被公認是錯誤的)標準,並且2)PHP(以及誰知道任何其他服務器端語言)不會自動檢測應用程序/ json輸入。 :P – 2013-04-29 03:20:36

+0

有可能angularjs自動適應內容類型並進行相應的編碼?預見到了嗎? – unludo 2013-07-10 08:28:09

189

如果你不想在解決方案中使用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

這種方法適用於角1.2.x,我認爲這是最好的答案,因爲它是優雅的,它在覈心角度工作,並不依賴於任何外部庫,如jQuery。 – Egg 2013-12-09 23:19:58

+2

在$ resource操作中使用此方法時遇到問題。表單數據還包括$ get,$ save等函數。解決方法是稍微改變'for'語句以代替使用'angular.forEach'。 – Anthony 2014-01-08 06:57:18

+10

請注意,與$ .param()不同,此方法不能在數組/對象上遞歸地工作。 – MazeChaZer 2014-11-07 15:56:26

2

AngularJS抓獲這樣做是正確的,因爲它做的HTTP請求頭中,以下內容類型:

Content-Type: application/json 

如果您正在使用PHP會像我一樣,甚至與Symfony2中,你可以簡單地擴展服務器兼容性JSON標準喜歡這裏描述:http://silex.sensiolabs.org/doc/cookbook/json_request_body.html

的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以原生方式執行此類請求。

3

只需設置Content-Type是不夠的,url發送前先對錶單數據進行編碼。 $http.post(url, jQuery.param(data))

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 */ 
     }); 
+0

謝謝!這個對我來說非常完美。 – charliepage88 2013-10-27 15:33:52

3

我目前使用下面的解決方案,我的AngularJS谷歌組found嘗試。

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

請注意,如果你使用PHP,你需要使用像Symfony的2 HTTP組件的Request::createFromGlobals()閱讀這一點,因爲$ _ POST不會自動加載它。

80

我花了幾個其他的答案和做的東西有點更乾淨,把這個.config()請撥打angular.module結束在app.js:

.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("&"); 
    }); 
}]); 
+2

你是老闆。這是一個很好的代碼snippit。 – whitehat101 2014-08-28 01:49:28

+1

像魅力一樣工作 - 即使附加到資源定義。 – 2014-09-11 07:45:34

+3

還要小心使用'unshift()',這樣其他轉換不會受到干擾。好工作。 – 2015-01-04 15:57:49

4

對於Symfony2的用戶:

如果你不想在你的JavaScript來改變什麼這個工作,你可以在你做這些修改symfony的應用:

創建擴展的Symfony \分量\ HttpFoundation \請求類的類:

<?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); 
+0

這對我來說真的很有用,新的createFromGlobals現在正在完美地工作。我不知道你爲什麼得到一個downvote,但我刪除它。 – Richard 2014-05-22 08:44:17

20

作爲一種變通方法,您可以簡單地使代碼收到POST應用/ JSON數據作出反應。對於PHP,我添加了下面的代碼,允許我以POST形式編碼或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 
+0

這是服務器端修復的一個很好的例子,因爲在這個問題上的真正問題是在服務器端API .. bravo – Vignesh 2015-04-21 07:08:25

7

有一個非常好的教程,可以查看這個和其他相關的東西 - Submitting AJAX Forms: The AngularJS Way

基本上,你需要設置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 2014-07-02 12:36:29

+0

'$ .param'表現神奇。誰擁有基於jQuery + AngularJS的應用程序的完美解決方案。 – dashtinejad 2016-07-02 09:55:30

-3

使用AngularJS $http服務並使用其post方法或配置$http函數。

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框架庫支持這一點。 For example in Java Spring, you can pass it using X-CSRF-TOKEN header

角的JS的對象綁定到UI對象優異的性能意味着我們可以擺脫發佈形式的做法都在一起,和後JSON代替。 JSON可以容易地解序列化到服務器側對象和支持複雜的數據結構,例如地圖,數組,嵌套對象等

如何在一個形式有效載荷張貼數組?也許是這樣的:

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

或本:

shopLocation=downtwon&daysOpen=Monday,Tuesday,Wednesday 

兩者都是設計不良..

15

這些答案看起來像瘋了矯枉過正,有時,簡單的就是更好:

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

對我來說,我仍然需要指定頭文件'Content-Type'並將其設置爲'application/x-www-form-urlencoded'。 – 2016-05-25 09:13:42

55

作爲AngularJS V1.4.0的,有一個內置的$httpParamSerializer服務的任何對象轉換爲根據要在docs page列出的規則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); 
+0

我們如何在定製的$資源工廠上做同樣的事情? – stilllife 2015-10-09 10:14:00

+0

注意:我將一個應用程序從Angular 1.3升級到1.5。它更改了transformRequest中的標題。出於某種原因,上面的方法對我不起作用,Angular圍繞URL編碼的字符串添加雙引號。用'transformRequest:$ httpParamSerializer,data:formDataObj'解決。感謝您的解決方案。 – PhiLho 2016-06-24 14:14:01

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' 
      } 
-1

你必須改變的僅僅是薄層是使用屬性「PARAMS」,而不是「數據」當你創建你的$ HTTP對象:

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

在上面的客戶端的例子[i]是隻是JSON對象(不以任何方式序列化)。https://docs.angularjs.org/api/ng/service/ $ httpParamSerializer

+2

通過使用參數而不是數據,Angular將數據放在URL參數中而不是請求主體中。這不是表單文章所期望的。 – cmenning 2015-07-22 16:18:34

2

完整的答案(因爲角1.4):如果用「PARAMS」而不是「數據」的角度將使用$ httpParamSerializer序列化你的對象。您需要包括去依賴$ 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) { 

      }); 
7

爲崗位創建一個適配器服務:

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) 
     }) 
    } 

}) 
+0

要點... :) – 2017-04-16 16:09:32

+0

$ .param只在jquery abi。 http://jsfiddle.net/4n9fao9q/27/ $ httpParamSerializer是Angularjs的等價物。 – Dexter 2017-05-17 01:15:04

相關問題