AngularJS - Czy jest jakiś sposób, aby $ http.post wysyłać parametry żądania zamiast JSON?

116

Mam jakiś stary kod, który wykonuje żądanie AJAX POST za pośrednictwem metody postu jQuery i wygląda mniej więcej tak:

$.post("/foo/bar", requestData,
    function(responseData)
    {
        //do stuff with response
    }

requestData jest po prostu obiektem javascript z kilkoma podstawowymi właściwościami ciągu.

Jestem w trakcie przenoszenia naszych rzeczy do użycia Angulara i chcę zastąpić to wywołanie $ http.post. Wymyśliłem co następuje:

$http.post("/foo/bar", requestData).success(
    function(responseData) {
        //do stuff with response
    }
});

Kiedy to zrobiłem, otrzymałem odpowiedź o błędzie 500 z serwera. Korzystając z Firebug, odkryłem, że wysłało to treść żądania w następujący sposób:

{"param1":"value1","param2":"value2","param3":"value3"}

Pomyślne jQuery $.postwysyła treść w następujący sposób:

param1=value1&param2=value2&param3=value3

Punkt końcowy, do którego trafiam, oczekuje parametrów żądania, a nie formatu JSON. Tak więc moje pytanie brzmi: czy mimo to $http.postnależy wysłać obiekt javascript jako parametry żądania zamiast JSON? Tak, wiem, że mógłbym sam zbudować ciąg z obiektu, ale chcę wiedzieć, czy Angular zapewnia coś takiego po wyjęciu z pudełka.

dnc253
źródło

Odpowiedzi:

140

Myślę, że paramsparametr config nie zadziała tutaj, ponieważ dodaje ciąg do adresu URL zamiast treści, ale aby dodać do tego, co sugeruje tutaj Infeligo, jest przykładem globalnego nadpisania domyślnej transformacji (używając parametru jQuery jako przykładu do konwersji dane do ciągu parametrów).

Skonfiguruj globalną funkcję transformRequest:

var app = angular.module('myApp');

app.config(function ($httpProvider) {
    $httpProvider.defaults.transformRequest = function(data){
        if (data === undefined) {
            return data;
        }
        return $.param(data);
    }
});

W ten sposób wszystkie wywołania $ http.post automatycznie przekształcą treść do tego samego formatu parametrów, który jest używany przez $.postwywołanie jQuery .

Zauważ, że możesz również chcieć ustawić nagłówek Content-Type dla połączenia lub globalnie w następujący sposób:

$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';

Przykładowe nieglobalne żądanie transformRequest na wywołanie:

    var transform = function(data){
        return $.param(data);
    }

    $http.post("/foo/bar", requestData, {
        headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
        transformRequest: transform
    }).success(function(responseData) {
        //do stuff with response
    });
Gloopy
źródło
Zastanawiałem się, czy istnieje coś innego niż posiadanie funkcji transformRequest, ale wygląda na to, że nie ma. Dzięki za ostrzeżenia o funkcji parametru jQuery.
dnc253
Nieglobalna metoda na wywołanie działa dobrze dla mnie, ale kiedy próbuję skonfigurować globalną za pośrednictwem $httpProvider.defaults, to nie działa, jakieś wskazówki na ten temat?
Dfr
1
WRT konfiguruje go globalnie, ja też mam problemy. Kiedy próbuję to zrobić za pomocą podanego tutaj fragmentu kodu, pojawia się błąd. Cannot read property "jquery" of undefined.Jak to naprawić? PS. Przekształcenia na żądanie działają.
kshep92
@ kshep92 To, co się dzieje, polega na tym, że funkcja transformRequest jest wywoływana na żądanie bez danych, więc „data” jest niezdefiniowana. Dodałem strażnika przed 'return $ .param (data);'. Wstaw to jako pierwszy wiersz do funkcji transformRequest: 'if (data === undefined) return data;' Zobacz edycję, którą wprowadziłem do odpowiedzi.
Jere.Jones,
1
od Angular 1.4 możesz używać $ httpParamSerializer zamiast jQuery docs.angularjs.org/api/ng/service/$httpParamSerializer
theRemix
21

Jeśli używasz Angular> = 1.4 , oto najczystsze rozwiązanie, które znalazłem, które nie opiera się na niczym niestandardowym ani zewnętrznym:

angular.module('yourModule')
  .config(function ($httpProvider, $httpParamSerializerJQLikeProvider){
    $httpProvider.defaults.transformRequest.unshift($httpParamSerializerJQLikeProvider.$get());
    $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
});

Następnie możesz to zrobić w dowolnym miejscu aplikacji:

$http({
  method: 'POST',
  url: '/requesturl',
  data: {
    param1: 'value1',
    param2: 'value2'
  }
});

I będzie prawidłowo szeregować dane jak param1=value1&param2=value2i wysłać go do /requesturlz application/x-www-form-urlencoded; charset=utf-8nagłówka Content-Type, jak to zwykle oczekuje wniosków post na punktach końcowych.

Saeb Amini
źródło
17

Z dokumentacji AngularJS:

params - {Object.} - Mapa ciągów lub obiektów, które zostaną zamienione na? key1 = value1 & key2 = value2 po adresie URL. Jeśli wartość nie jest ciągiem znaków , będzie to JSONified.

Więc podaj ciąg jako parametry. Jeśli tego nie chcesz, użyj transformacji. Ponownie z dokumentacji:

Aby nadpisać te transformacje lokalnie, określ funkcje transformacji jako właściwości transformRequest i / lub transformResponse obiektu config. Aby globalnie przesłonić domyślne przekształcenia, przesłoń właściwości $ httpProvider.defaults.transformRequest i $ httpProvider.defaults.transformResponse elementu $ httpProvider.

Więcej informacji można znaleźć w dokumentacji .

Infeligo
źródło
Widziałem parametry w dokumentacji i tak jak wspomina Gloopy, potrzebuję ich w treści, a nie w adresie URL. Zastanawiałem się, czy jest jakaś opcja lub coś, czego mi brakuje, aby wykonać parametry zamiast JSON, ale wygląda na to, że muszę po prostu użyć właściwości transformRequest.
dnc253
15

Użyj $.paramfunkcji jQuery, aby serializować dane JSON w requestData.

Krótko mówiąc, używając podobnego kodu jak Twój:

$http.post("/foo/bar",
$.param(requestData),
{
    headers:
    {
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
    }
}
).success(
    function(responseData) {
        //do stuff with response
    }
});

Aby z tego skorzystać, musisz dołączyć jQuery na swojej stronie wraz z AngularJS.

Sagar Bhosale
źródło
7

Zwróć uwagę, że od wersji Angular 1.4 możesz serializować dane formularza bez używania jQuery.

W pliku app.js:

module.run(function($http, $httpParamSerializerJQLike) {
  $http.defaults.transformRequest.unshift($httpParamSerializerJQLike);
});

Następnie w kontrolerze:

$http({
    method: 'POST',
    url: myUrl',
    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    data: myData
});
Thomas Graziani
źródło
Ta odpowiedź jest świetna. Rozwiązuje 2 główne problemy związane z Post from Angular. Nagłówek musi być poprawnie ustawiony i musisz serializować dane JSON. Jeśli nie potrzebujesz obsługi IE8, użyj wersji 1.4 lub nowszej.
mbokil
Właśnie to zaimplementowałem i rozwiązuje to problemy, które miałem z postem, ale zmienia to również sposób działania łatki i wydaje się, że zepsuło wszystkie moje zastosowania $ http.patch ().
Mike Feltman,
5

To może być trochę hack, ale uniknąłem problemu i przekonwertowałem json na tablicę POST PHP po stronie serwera:

$_POST = json_decode(file_get_contents('php://input'), true);
TimoSolo
źródło
Użyłem tej metody, ale jej nienawidzę; i zajęło mi dużo czasu, zanim zrozumiałem, dlaczego musiałem tego użyć.
meconroy
jak powiedziałem - czuje się hacky. Jak większość php;)
TimoSolo
5

Mam również problemy z ustawieniem niestandardowego uwierzytelniania HTTP, ponieważ $ zasób buforuje żądanie.

Aby to zadziałało, musisz w ten sposób nadpisać istniejące nagłówki

var transformRequest = function(data, headersGetter){
  var headers = headersGetter();
  headers['Authorization'] = 'WSSE profile="UsernameToken"';
  headers['X-WSSE'] = 'UsernameToken ' + nonce
  headers['Content-Type'] = 'application/json';
};

return $resource(
  url,
    {
    },
    {
      query: {
        method: 'POST',
        url: apiURL + '/profile',
        transformRequest: transformRequest,
        params: {userId: '@userId'}
      },
    }
);

Mam nadzieję, że udało mi się komuś pomóc. Zrozumienie tego zajęło mi 3 dni.

Frank Marcelo
źródło
Chyba właśnie zaoszczędziłeś mi 3 dni pracy. Dzięki!!! Nadal próbuję dowiedzieć się, czy mogę w jakiś sposób przechwycić wywołanie żądania, aby móc wstrzyknąć niestandardowy nagłówek dla każdego połączenia.
marcoseu
4

Zmodyfikuj domyślne nagłówki:

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

Następnie użyj $.parammetody JQuery :

var payload = $.param({key: value});
$http.post(targetURL, payload);
Zags
źródło
3
   .controller('pieChartController', ['$scope', '$http', '$httpParamSerializerJQLike', function($scope, $http, $httpParamSerializerJQLike) {
        var data = {
                TimeStamp : "2016-04-25 12:50:00"
        };
        $http({
            method: 'POST',
            url: 'serverutilizationreport',
            headers: {'Content-Type': 'application/x-www-form-urlencoded'},
            data: $httpParamSerializerJQLike(data),
        }).success(function () {});
    }
  ]);
Rohit Luthra
źródło
Według mnie to najprostsze i najłatwiejsze ...
Sposobów
2

Szybka regulacja - dla tych z Was, którzy mają problem z globalną konfiguracją funkcji transformRequest, oto fragment kodu, którego używam, aby pozbyć się Cannot read property 'jquery' of undefinedbłędu:

$httpProvider.defaults.transformRequest = function(data) {
        return data != undefined ? $.param(data) : null;
    }
kshep92
źródło
0

Wielokrotnie znajdowałem problematyczne zachowanie tej całości. Użyłem go z express (bez typów) i bodyParser (z typami dt ~ body-parser).

Nie próbowałem przesyłać pliku, zamiast tego po prostu zinterpretowałem JSON podany w ciągu postu.

request.bodyPo prostu pusty json ( {}).

Po wielu badaniach w końcu to zadziałało:

import { json } from 'body-parser';
...
app.use(json()); <-- should be defined before the first POST handler!

Ważne może być również podanie application/jsontypu zawartości w ciągu żądania po stronie klienta.

peterh - Przywróć Monikę
źródło
Przepraszam za odpowiedź w stylu "i poświęcić czarną kurę", co niestety jest powszechne na obecnym etapie środowiska maszynopisu / węzła / kątowego.
peterh - Przywróć Monikę
0

Składnia dla AngularJS w wersji 1.4.8 + (w wersji 1.5.0)

       $http.post(url, data, config)
            .then(
                    function (response) {
                        // success callback
                    },
                    function (response) {
                        // failure callback
                    }
            );

Na przykład:

    var url = "http://example.com";

    var data = {
        "param1": "value1",
        "param2": "value2",
        "param3": "value3"
    };

    var config = {
        headers: {
            'Content-Type': "application/json"
        }
    };

    $http.post(url, data, config)
            .then(
                    function (response) {
                        // success callback
                    },
                    function (response) {
                        // failure callback
                    }
            );
Pranav VR
źródło