Verileri istek yükü yerine form verisi olarak nasıl gönderebilirim?


523

Aşağıdaki kodda, AngularJS $httpyöntemi URL'yi çağırır ve xsrf nesnesini "Yük Taşıma İsteği" olarak gönderir (Chrome hata ayıklayıcı ağ sekmesinde açıklandığı gibi). JQuery $.ajaxyöntemi aynı çağrıyı yapar, ancak xsrf'yi "Form Verileri" olarak gönderir.

AngularJS'nin istek yükü yerine form verisi olarak xsrf göndermesini nasıl sağlayabilirim?

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
Bu çok faydalı bir soruydu. POST / GET öncesinde OPTIONS ile uğraşmamı engelleyen bir yükü dize olarak göndermemi sağlar (İçerik Türünü değiştirerek).
earthmeLon

Aynı sorum var, ben url talep sonra, ama ben göndermek parametre alamıyorum
黄伟杰

Yanıtlar:


614

Geçirilen $ http nesnesine aşağıdaki satırın eklenmesi gerekir:

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

Ve iletilen veriler URL kodlu bir dizeye dönüştürülmelidir:

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

Yani şöyle bir şey var:

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

Gönderen: https://groups.google.com/forum/#!msg/angular/5nAedJ1LyO0/4Vj_72EZcDsJ

GÜNCELLEME

AngularJS V1.4 ile eklenen yeni hizmetleri kullanmak için, bkz.


3
Verilerin json> url kodlamasının otomatik olarak gerçekleşmesinin veya her POST veya PUT yöntemi için bunun olmasını belirtmenin bir yolu var mı?
Dogoku

51
+1 @mjibson, Benim için üstbilgileri geçmek bile işe yaramadı, cevabınızı içeren cevabı görene kadar: var xsrf = $.param({fkey: "key"});Bu aptalca, neden açısal yapamıyorsunuz?
naikus

12
Daha önce $ .ajax varsayılan davranışını takip etmek için, charset içerik türü başlığında da belirtilmelidir -headers: {Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}
Imre

25
JQuery'nin param işlevini kullanmak yerine, $ http isteğinde params özelliğini ayarlamanız yeterlidir ve jQuery.param yönteminin Content-Type başlığı 'application / x-www-form-urlencoded' - stackoverflow
spig

13
@spig Evet, jQuery.param ne yaparsa yapsın, ancak params özelliğini kullanırsanız, özellikleriniz gövde / yerine istek URL'sinin bir parçası olarak kodlanır - / x-www- uygulamasını belirtmiş olsanız bile form urlen kodlu başlık.
stian

194

Çözümde jQuery kullanmak istemiyorsanız bunu deneyebilirsiniz. Çözüm buradan iptal edildi 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
Bu yöntem açısal 1.2.x benim için çalışıyor ve zarif olduğu için en iyi cevap olduğunu düşünüyorum, çekirdek açısal çalışır ve jQuery gibi herhangi bir dış kütüphanelere bağlı değildir.
gregtczap

2
Bir $ resource eylem içinde bu yöntemi kullanırken bir sorunla karşılaştım. Form verileri, $ get, $ save, vb. İşlevlerini de içeriyordu. Çözüm forifadeyi kullanmak için biraz değiştirmekti angular.forEach.
Anthony

10
$ .Param () yönteminin aksine, bu yöntemin diziler / nesneler üzerinde yinelemeli olarak çalışmadığını unutmayın.
MazeChaZer

1
Boş veya tanımsızobj[p] olup olmadığını kontrol ederdim . Aksi takdirde "null" veya "undefined" dizesini değer olarak gönderirsiniz.
tamir

1
Ben anlamadım transformRequest: function(obj)obj tanımsız olarak, biz xsrf geçmek varsayalım? BeğentransformRequest: function(xsrf)
Akshay Taru

92

Bu konuyla ilgili devam eden karışıklık, bu konuda bir blog yazısı yazmam için bana ilham verdi. Bu yazıda önerdiğim çözüm, mevcut en yüksek puanlı çözümünüzden daha iyidir çünkü sizi $ http servis çağrıları için veri nesnenizi parametrelendirmeyle sınırlamaz; yani benim çözümümle gerçek veri nesnelerini $ http.post (), vb. 'e geçirmeye devam edebilir ve yine de istenen sonucu elde edebilirsiniz.

Ayrıca, en yüksek puanlı cevap, tam jQuery'nin $ .param () işlevi için sayfaya dahil edilmesine dayanırken, benim çözümüm jQuery agnostic, pure AngularJS için hazırdır.

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

Bu yardımcı olur umarım.


10
Ayrıntılı blog için +1, ancak buna ihtiyaç olduğu gerçeği korkunç ...
iwein

4
Evet, belki iki düzeyde korkunçtur: 1) AngularJS, fiili (kabul edilen şekilde yanlış yönlendirilmiş olsa da) bir standardı yükseltmeye karar verdi ve 2) PHP'nin (ve diğer sunucu tarafındaki dilleri bilen) bir şekilde otomatik olarak application / json'u algılamadığını giriş. : P
Ezekiel Victor

Angularjs otomatik olarak içerik türüne uyum sağlamak ve buna göre kodlamak mümkün müdür? Öngörülüyor mu?
unludo

4
Ben (diğerleri gibi) arka ucumun ASP.NETbunu 'doğal olarak' desteklemediğini gördüm . AngularJS'in davranışını değiştirmek istemiyorsanız (ki API'm JSON döndürmediği için neden JSON'u kabul etmiyorsunuz, form verilerinden daha esnektir) Request.InputStreamve okuyabilir ve istediğiniz şekilde işleyebilirsiniz istiyorsun. ( dynamicKullanım kolaylığı için seriyi kaldırmayı seçtim .)
Aidiakapi

2
Bu, kendi kendine yeten bir cevap olmadığı için indirildi . İyi yanıtlar sadece başka bir yere bağlantı vermekle kalmaz. Gönderen Nasıl Cevap için : "Her zaman hedef sitesi erişilemiyor veya kalıcı çevrimdışı duruma durumunda önemli bir bağlantının en uygun kısmı, alıntı."
James

83

Diğer cevaplardan birkaçını aldım ve biraz daha temiz bir şey yaptım, bu .config()çağrıyı app.js'nizdeki açısal.modülünüzün sonuna koydum:

.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
Bir kaynak tanımına eklenmiş olsa bile bir cazibe gibi çalışır.
Kai Mattern

3
Ayrıca unshift()diğer dönüşümlerin bozulmadan kalmasını sağlamaya özen gösterdi . İyi iş.
Aditya MP

2
mükemmel! benim için iyi çalıştı! üzgün açısal bunu doğal olarak desteklemiyor.
spierala

2
Bu cevap en üstte doğru olmalı, diğerleri yanlış, teşekkürler dostum !!
Jose Ignacio Hita

2
Özyinelemeli kodlamaya ne dersiniz?
Petah

58

AngularJS v1.4.0 itibariyle $httpParamSerializer, dokümanlar sayfasında listelenen kurallara göre herhangi bir nesneyi HTTP isteğinin bir bölümüne dönüştüren yerleşik bir hizmet vardır .

Bu şekilde kullanılabilir:

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

Doğru bir form gönderisi için Content-Typebaşlığın değiştirilmesi gerektiğini unutmayın. Bunu tüm POST istekleri için global olarak yapmak için, bu kod (Albireo'nun yarı cevabından alınmıştır) kullanılabilir:

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

Bunu yalnızca geçerli gönderi için yapmak için headers, request nesnesinin özelliğinin değiştirilmesi gerekir:

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

$http(req);

Aynı şeyi özel bir $ kaynak fabrikasında nasıl yapabiliriz?
stilllife

Not: Bir uygulamayı Açısal 1.3'ten 1.5'e yükselttim. TransformRequest öğesindeki başlıkları değiştirdi. Nedense, yukarıdaki yöntem benim için çalışmıyor, Angular URL kodlu dizenin etrafına çift tırnak ekler. İle çözüldü transformRequest: $httpParamSerializer, data: formDataObj. Çözüm için teşekkürler.
PhiLho

24

Davranışı global olarak tanımlayabilirsiniz:

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

Yani her seferinde yeniden tanımlamanız gerekmez:

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

46
Örneğin çok yanlış ... Değiştirdiğiniz tek şey başlık. Verilerin kendileri hala JSON kodlu olacak ve JSON'u okuyamayan eski sunucular tarafından okunamayacak.
alexk

victorblog.com/2012/12/20/… - $ http varsayılan üstbilgisini geçersiz kıldığınız ve nesneyi serileştirilmiş form verilerine dönüştürdüğünüz iyi bir örnek.
Federico

20

Geçici bir çözüm olarak, POST alan kodun application / json verilerine yanıt vermesini sağlayabilirsiniz. PHP için aşağıdaki kodu ekledim, form kodlu veya JSON'da POST yapmamı sağladım.

//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

Bu sunucu tarafı düzeltme iyi bir örnektir, çünkü bu konuda gerçek sorun sunucu tarafı API üzerinde .. bravo
Vignesh

16

Bu cevaplar çılgınca aşırılık gibi görünüyor, bazen basit sadece daha iyi:

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

1
Benim için yine de üstbilgiyi belirtmem Content-Typeve ayarlamam gerekiyordu application/x-www-form-urlencoded.
Victor Ramos

9

Aşağıdaki çözümü deneyebilirsiniz

$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

Posta için bir adaptör hizmeti oluşturun:

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

}) 

Denetleyicilerinizde veya her neyse kullanın:

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 sadece jquery abi'de. jsfiddle.net/4n9fao9q/27 $ httpParamSerializer Angularjs eşdeğeri.
Dexter

7

Bu ve diğer ilgili şeyler üzerinde gerçekten güzel bir öğretici var - AJAX Formları Gönderme: AngularJS Yolu .

Temel olarak, form verilerini URL kodlamalı dize olarak gönderdiğinizi belirtmek için POST isteğinin başlığını ayarlamanız ve verilerin aynı biçimde gönderilecek şekilde ayarlanması gerekir

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

Verilerin bir dizeye serileştirilmesi için burada jQuery'nin param () yardımcı işlevinin kullanıldığını unutmayın, ancak jQuery kullanmıyorsanız bunu manuel olarak da yapabilirsiniz.


1
Moderatörler, önceki cevabımı sildiler çünkü bağlantıda belirtilen uygulamanın ayrıntılarını vermedim. Bunun yerine, bu cevapta görüldüğü gibi ayrıntıları sağlamak için cevabımı zaten düzenlediğim için, silmekten ziyade daha fazla ayrıntı vermemi isteselerdi daha iyi olurdu!
robinmitra

$.paramBüyü yapmak. jQuery + AngularJS tabanlı uygulaması olan kişiler için mükemmel bir çözüm.
dashtinejad


4

Symfony2 kullanıcıları için:

Bunun çalışması için javascriptinizde bir şey değiştirmek istemiyorsanız, symfony uygulamasında bu değişiklikleri yapabilirsiniz:

Symfony \ Component \ HttpFoundation \ Request sınıfını genişleten bir sınıf oluşturun:

<?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;

}

}

Şimdi sınıfınızı app_dev.php dosyasında (veya kullandığınız herhangi bir dizin dosyasında) kullanın

// 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);

bu benim için gerçekten yararlıydı, yeni createFromGlobals şimdi mükemmel çalışıyor. Neden bir aşağı oy aldığını bilmiyorum, ama kaldırdım.
Richard

3

Sadece İçerik Tipi ayarlamak yeterli değildir, url göndermeden önce form verilerini kodlar. $http.post(url, jQuery.param(data))


3

Şu anda AngularJS google grubunda bulduğum aşağıdaki çözümü kullanıyorum .

$ http
.post ('/ echo / json /', 'json =' + encodeURIComponent (angular.toJson (veri)), {
    başlıklar: {
        'Content-Type': 'application / x-www-form-urlencoded; karakter kümesi = UTF-8'
    }
}). başarı (işlev (veri)
    $ scope.data = veri;
});

PHP kullanıyorsanız, bunu Request::createFromGlobals()okumak için Symfony 2 HTTP bileşeni gibi bir şey kullanmanız gerekeceğini unutmayın , çünkü $ _POST otomatik olarak yüklenmeyecektir.


2

AngularJS bunu http-request üstbilgisinde aşağıdaki içerik türünü yaptığı gibi yapıyor:

Content-Type: application/json

Benim gibi php ile, hatta Symfony2 ile gidiyorsanız, sunucu uyumluluğunuzu burada açıklandığı gibi json standardı için genişletebilirsiniz: http://silex.sensiolabs.org/doc/cookbook/json_request_body.html

Symfony2 yolu (örneğin DefaultController'ınızın içinde):

$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());

Avantajı, jQuery parametresini kullanmanız gerekmemesi ve AngularJS'yi bu tür istekleri yapmanın yerel yolu olarak kullanabilmenizdir.


2

Tam cevap (açısal 1.4'ten beri). De bağımlılığı dahil etmeniz gerekir $ 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

Uygulama yapılandırmanızda -

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

Kaynak talebinizle -

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

0

Bu doğrudan bir cevap değil, biraz farklı bir tasarım yönü:

Verileri form olarak değil, doğrudan sunucu tarafı nesnesiyle eşlenecek bir JSON nesnesi olarak gönderin veya REST stili yol değişkenini kullanın

Bir XSRF anahtarını geçmeye çalıştığınız için artık her iki seçeneğin de sizin durumunuzda uygun olmadığını biliyorum. Böyle bir yol değişkenine eşlemek korkunç bir tasarımdır:

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

Çünkü doğası gereği, xsrf anahtarını başka bir yola /login, /book-appointmentvb. De geçirmek istersiniz ve güzel URL'nizi karıştırmak istemezsiniz.

Bir nesne alanı olarak ilginç bir şekilde eklemek de uygun değil, çünkü şimdi sunucuya geçirdiğiniz json nesnesinin her birinde alanı eklemeniz gerekiyor

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

Kesinlikle, sunucu tarafı sınıfınıza etki alanı nesnesiyle doğrudan anlamsal bir ilişkisi olmayan başka bir alan eklemek istemezsiniz.

Bence xsrf anahtarınızı iletmenin en iyi yolu bir HTTP üstbilgisidir. Birçok xsrf koruması sunucu tarafı web çerçevesi kitaplığı bunu desteklemektedir. Örneğin Java Spring'de X-CSRF-TOKENüstbilgiyi kullanarak iletebilirsiniz .

Angular'ın JS nesnesini UI nesnesine mükemmel bir şekilde bağlayabilme özelliği, formun birlikte gönderilmesi ve bunun yerine JSON'un gönderilmesi uygulamasından kurtulabileceğimiz anlamına gelir. JSON, sunucu tarafındaki nesneye kolayca serileştirilebilir ve harita, diziler, iç içe nesneler gibi karmaşık veri yapılarını destekleyebilir.

Diziyi bir form yükünde nasıl gönderiyorsunuz? Belki şöyle:

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

veya bu:

shopLocation=downtwon&daysOpen=Monday,Tuesday,Wednesday

Her ikisi de kötü tasarım.


0

Bu benim ihtiyacım için yapıyorum, nerede form veri olarak API veri giriş gerekiyor ve Javascript Nesne (userData) otomatik olarak URL kodlanmış verilere dönüştürülüyor

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

Kullanıcı verilerim bu şekilde

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

-1

Değiştirmeniz gereken tek şey, $ http nesnenizi oluştururken "data" yerine "params" özelliğini kullanmaktır:

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

Yukarıdaki örnekte istemciler [i] sadece JSON nesnesidir (hiçbir şekilde serileştirilmemiş). "Data" yerine "params" kullanırsanız açısal $ httpParamSerializer kullanarak sizin için serileştirir: https://docs.angularjs.org/api/ng/service/ $ httpParamSerializer


2
Angular, veri yerine params kullanarak verileri istek gövdesi yerine URL parametrelerine yerleştirir. Bir form gönderisinden beklenen bu değildir.
2015'te

-3

AngularJS $httphizmetini kullanın ve postyöntemini veya yapılandırma $httpişlevini kullanın.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.