Jak OPUBLIKOWAĆ żądanie x-www-form-urlencoded przy użyciu funkcji Fetch?

111

Mam kilka parametrów, które chcę POST zakodować na moim serwerze:

{
    'userName': '[email protected]',
    'password': 'Password!',
    'grant_type': 'password'
}

Wysyłam moje żądanie (obecnie bez parametrów) w ten sposób

var obj = {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
  },
};
fetch('https://example.com/login', obj)
  .then(function(res) {
    // Do stuff with result
  }); 

Jak mogę dołączyć parametry zakodowane w formularzu do żądania?

texas697
źródło
3
Zaktualizuj wybraną odpowiedź, podając właściwą odpowiedź.
Albert Renshaw

Odpowiedzi:

-60

Do przesyłania żądań POST zakodowanych w formularzu polecam użycie obiektu FormData .

Przykładowy kod:

var params = {
    userName: '[email protected]',
    password: 'Password!',
    grant_type: 'password'
};

var formData = new FormData();

for (var k in params) {
    formData.append(k, params[k]);
}

var request = {
    method: 'POST',
    headers: headers,
    body: formData
};

fetch(url, request);
David Kay
źródło
86
To nie jest application / x-www-form-urlencoded, ale multipart / form-data
Haha TTpro
Zgadzam się, to żądanie nie będzie miało „application / x-www-form-urlencoded” jako Content-Type, ale „multipart / form-data”.
b4stien
2
@Mzn - Na przykład, jeśli korzystasz z usługi takiej jak Google Closure Compiler API , serwer akceptuje tylko application/x-www-form-urlencoded, a nie multipart/form-data.
Sphinxxx
12
Jak to może być akceptowana odpowiedź? To jest po prostu błędne, jeśli chodzi o rzeczywiste pytanie ...
Żabojad
1
Podczas przesyłania obiektów FormData będziesz musiał wykonać dodatkowe przetwarzanie na serwerze. Zasadniczo przetwarzaj zwykły formularz tak, jakby był przesyłanym plikiem. Jaka jest zaleta obiektów FormData w przypadku zwykłych formularzy?
MarsAndBack
272

Musisz samodzielnie złożyć ładunek x-www-form-urlencoded, na przykład:

var details = {
    'userName': '[email protected]',
    'password': 'Password!',
    'grant_type': 'password'
};

var formBody = [];
for (var property in details) {
  var encodedKey = encodeURIComponent(property);
  var encodedValue = encodeURIComponent(details[property]);
  formBody.push(encodedKey + "=" + encodedValue);
}
formBody = formBody.join("&");

fetch('https://example.com/login', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
  },
  body: formBody
})

Zauważ, że jeśli używasz fetchw (wystarczająco nowoczesnej) przeglądarce, zamiast React Native, możesz zamiast tego utworzyć URLSearchParamsobiekt i użyć go jako ciała, ponieważ Fetch Standard stwierdza, że jeśli bodyjest URLSearchParamsobiektem, powinien być serializowany jako application/x-www-form-urlencoded. Jednak nie możesz tego zrobić w React Native, ponieważ React Native nie implementujeURLSearchParams .

Narongdej Sarnsuwan
źródło
52
Droga ES6:const formBody = Object.keys(details).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(details[key])).join('&');
Eric Burel
Ten polyfill dla URLSearchParams github.com/WebReflection/url-search-params może działać dla React Native lub starszych przeglądarek.
bucabay
7
Inny podobny sposób:const formBody = Object.entries(details).map(([key, value]) => encodeURIComponent(key) + '=' + encodeURIComponent(value)).join('&')
Flynn Hou
1
Konwertuje parametr tablicy json na łańcuch
atulkhatri
Wypróbowałem wszystkie sugerowane metody. Bez względu na to, co robię, funkcja fetch wprowadza niechciane cudzysłowy w całym ciele, bezpośrednio do ciągu znaków - otwierające i zamykające cudzysłowy. Powoduje to parsowanie parametrów, np. W ten sposób: '"mykey': 'myvalue"'. Co sprawia, że ​​wywoływanie API jest niemożliwe, ponieważ te oczywiście powodują po prostu 400 błędów (serwer rozpoznaje mykey, a nie "mykey). Czy ktoś inny ma ten problem? Zaskakujące.
Dave Munger
49

Posługiwać się URLSearchParams

https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams

var data = new URLSearchParams();
data.append('userName', '[email protected]');
data.append('password', 'Password');
data.append('grant_type', 'password');
Nicu Criste
źródło
to robi to, co zamierzałem, ponieważ php7 nie analizował poprawnie kodowania FormData. Mam nadzieję, że dostanie więcej głosów dla chłopców i dziewcząt z PHP
cav_dan
6
-1; URLSearchParamsnie istnieje w React Native. (Zobacz github.com/facebook/react-native/issues/9596. )
Mark Amery
3
Jest teraz częścią React Native. Pamiętaj, aby wywołać toString()dane przed przekazaniem żądania body.
phatmann
Nawet po tym, jak RN powiedział, że wdrożyli URLSearchParams, nadal mam problemy. Nie sądzę, że jest zaimplementowany zgodnie ze specyfikacją i nie jest to tylko kropla w rozwiązaniu. Proszę rozważyć przeczytanie URLSearchParams „Error: not Implemented”, jeśli próbujesz wpaść URLSearchParamsi nadal masz problemy.
zero298
14

Właśnie to zrobiłem i UrlSearchParams załatwił sprawę Oto mój kod, jeśli komuś pomoże

import 'url-search-params-polyfill';
const userLogsInOptions = (username, password) => {



// const formData = new FormData();
  const formData = new URLSearchParams();
  formData.append('grant_type', 'password');
  formData.append('client_id', 'entrance-app');
  formData.append('username', username);
  formData.append('password', password);
  return (
    {
      method: 'POST',
      headers: {
        // "Content-Type": "application/json; charset=utf-8",
        "Content-Type": "application/x-www-form-urlencoded",
    },
      body: formData.toString(),
    json: true,
  }
  );
};


const getUserUnlockToken = async (username, password) => {
  const userLoginUri = `${scheme}://${host}/auth/realms/${realm}/protocol/openid-connect/token`;
  const response = await fetch(
    userLoginUri,
    userLogsInOptions(username, password),
  );
  const responseJson = await response.json();
  console.log('acces_token ', responseJson.access_token);
  if (responseJson.error) {
    console.error('error ', responseJson.error);
  }
  console.log('json ', responseJson);
  return responseJson.access_token;
};
ROCZNIE
źródło
5
*/ import this statement */
import qs from 'querystring'

fetch("*your url*", {
            method: 'POST',
            headers: {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'},
            body: qs.stringify({ 
                username: "akshita",
                password: "123456",
            })
    }).then((response) => response.json())
      .then((responseData) => {
         alert(JSON.stringify(responseData))
    })

Po użyciu npm i querystring - zapisz, że działa dobrze.

Akshita Agarwal
źródło
5
var details = {
    'userName': '[email protected]',
    'password': 'Password!',
    'grant_type': 'password'
};

var formBody = [];
for (var property in details) {
  var encodedKey = encodeURIComponent(property);
  var encodedValue = encodeURIComponent(details[property]);
  formBody.push(encodedKey + "=" + encodedValue);
}
formBody = formBody.join("&");

fetch('http://identity.azurewebsites.net' + '/token', {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  body: formBody
})

jest to dla mnie bardzo pomocne i działa bez żadnego błędu

odniesienie: https://gist.github.com/milon87/f391e54e64e32e1626235d4dc4d16dc8

mahsa k
źródło
3

Po prostu użyj

import  qs from "qs";
 let data = {
        'profileId': this.props.screenProps[0],
        'accountId': this.props.screenProps[1],
        'accessToken': this.props.screenProps[2],
        'itemId': this.itemId
    };
    return axios.post(METHOD_WALL_GET, qs.stringify(data))
mojTaba Shayegh
źródło
1
Powinno to być oznaczone jako poprawna odpowiedź. Jest tak łatwy w użyciu i nie ma żadnych dziwnych rzeczy do zrobienia.
Augusto Gonzalez
3

Nie ma potrzeby używania jQuery querystringani ręcznego składania ładunku. URLSearchParamsjest do zrobienia, a oto jedna z najbardziej zwięzłych odpowiedzi z przykładem pełnego zapytania:

fetch('https://example.com/login', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  body: new URLSearchParams({
    'param': 'Some value',
    'another_param': 'Another value'
  })
})
  .then(res => {
    // Do stuff with the result
  });

Tak, zamiast tego możesz użyć Axios lub czego tylko chcesz fetch.

PS URLSearchParamsnie jest obsługiwany w IE.

Neurotransmitter
źródło
2

Po prostu ustaw ciało w następujący sposób

var reqBody = "username="+username+"&password="+password+"&grant_type=password";

następnie

fetch('url', {
      method: 'POST',
      headers: {
          //'Authorization': 'Bearer token',
          'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
      },
      body: reqBody
  }).then((response) => response.json())
      .then((responseData) => {
          console.log(JSON.stringify(responseData));
      }).catch(err=>{console.log(err)})
Hazem AbdelHamid
źródło
1

W oryginalnym przykładzie masz plik transformRequest funkcję, która konwertuje obiekt na dane zakodowane w formacie Form.

W poprawionym przykładzie zastąpiłeś to JSON.stringify który konwertuje obiekt na JSON.

W obu przypadkach masz 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'więc roszczenie być wysyłania danych zakodowanej formie w obu przypadkach.

Zamiast tego użyj funkcji kodowania formularza JSON.stringify.


Ponowna aktualizacja:

W pierwszym fetchprzykładzie ustawiasz bodywartość JSON.

Teraz utworzyłeś wersję Form Encoded, ale zamiast ustawiania bodytej wartości, utworzyłeś nowy obiekt i ustawiłeś dane Form Encoded jako właściwość tego obiektu.

Nie twórz dodatkowego obiektu. Po prostu przypisz swoją wartość do body.

Quentin
źródło
Cześć @Quentin. Po prostu radykalnie zredukowałem to pytanie, aby spróbować uczynić je bardziej użytecznym odniesieniem dla przyszłych czytelników; robiąc to, całkowicie unieważniłem twoją odpowiedź, która odnosi się do szczegółów i błędów oryginalnego kodu pytającego. Przypuszczam, że masz prawo cofnąć moją zmianę, jeśli chcesz - teoretycznie nie mamy na celu wprowadzania zmian unieważniających odpowiedź, co jest tym, co zrobiłem - ale jeśli chcesz, myślę, że tak lepiej byłoby po prostu usunąć tę odpowiedź; IMO pytanie jest o wiele ładniejsze bez kodu Angulara lub poprzedniej nieudanej próby.
Mark Amery,
1

Jeśli używasz JQuery, to też działa.

fetch(url, {
      method: 'POST', 
      body: $.param(data),
      headers:{
        'Content-Type': 'application/x-www-form-urlencoded'
      }
})
życzliwy
źródło
0

Zgodnie ze specyfikacją użycie encodeURIComponentnie daje zgodnego ciągu zapytania. W Stanach:

  1. Nazwy i wartości kontrolek są chronione. Znaki spacji są zastępowane+ , a następnie zarezerwowane znaki są usuwane, jak opisano w [RFC1738], sekcja 2.2: Znaki niealfanumeryczne są zastępowane %HHznakiem procentu i dwiema cyframi szesnastkowymi reprezentującymi kod ASCII znaku. Podziały wiersza są przedstawiane jako pary „CR LF” (tj %0D%0A.).
  2. Nazwy / wartości kontrolek są wymienione w kolejności, w jakiej pojawiają się w dokumencie. Nazwa jest oddzielana od wartości przez, =a pary nazwa / wartość są oddzielane od siebie &.

Problem w tym, że encodeURIComponentzakoduje przestrzenie, które mają być %20, a nie +.

Treść formularza powinna być zakodowana przy użyciu różnych encodeURIComponentmetod przedstawionych w innych odpowiedziach.

const formUrlEncode = str => {
  return str.replace(/[^\d\w]/g, char => {
    return char === " " 
      ? "+" 
      : encodeURIComponent(char);
  })
}

const data = {foo: "bar߃©˙∑  baz", boom: "pow"};

const dataPairs = Object.keys(data).map( key => {
  const val = data[key];
  return (formUrlEncode(key) + "=" + formUrlEncode(val));
}).join("&");

// dataPairs is "foo=bar%C3%9F%C6%92%C2%A9%CB%99%E2%88%91++baz&boom=pow"
papiro
źródło
0

Możesz użyć aplikacji React-Native-Easy, która ułatwia wysyłanie żądań http i formułowanie żądań przechwycenia.

import { XHttp } from 'react-native-easy-app';

* Synchronous request
const params = {name:'rufeng',age:20}
const response = await XHttp().url(url).param(params).formEncoded().execute('GET');
const {success, json, message, status} = response;


* Asynchronous requests
XHttp().url(url).param(params).formEncoded().get((success, json, message, status)=>{
    if (success){
       this.setState({content: JSON.stringify(json)});
    } else {
       showToast(msg);
    }
});
rufeng
źródło