- How to send JSON request using XMLHttpRequest (XHR)
- You might also like.
- System Overlord
- A blog about security engineering, research, and general hacking.
- Posting JSON with an HTML Form
- The Application
- Exploring Options
- JSON in Forms
- How to use fetch to POST form data as JSON to your API
- Example HTML form
- Step 1. Listen for when a user submits the form
- Step 2. Read the values of all the form fields with FormData
- Step 3. Format the data as JSON and POST it to a URL with fetch
- Full code example without comments
- Handling JSON request bodies in an Express based API
- Share this
- Html post запрос json
- Отправка json
How to send JSON request using XMLHttpRequest (XHR)
In my previous article, we looked at how to make an HTTP POST request using XMLHttpRequest (XHR) in vanilla JavaScript. Since the most common use of XHR is for sending an asynchronous request with JSON payload, it’s good to know how to do it.
JSON stands for JavaScript Object Notation and is a popular format for sharing data with the server and displaying the result to the client.
The following example shows how you can use the XHR to make a JSON POST request in JavaScript:
const xhr = new XMLHttpRequest() // listen for `load` event xhr.onload = () => // print JSON response if (xhr.status >= 200 && xhr.status 300) // parse JSON const response = JSON.parse(xhr.responseText) console.log(response) > > // create a JSON object const json = email: 'eve.holt@reqres.in', password: 'cityslicka' > // open request xhr.open('POST', 'https://reqres.in/api/login') // set `Content-Type` header xhr.setRequestHeader('Content-Type', 'application/json') // send rquest with JSON payload xhr.send(JSON.stringify(json))
Take a look at the making HTTP requests using XHR tutorial to learn about all available options.
If you work with modern browsers only, I’d suggest using the Fetch API instead of XHR. It has clear and concise syntax and also supports promises:
// create a JSON object const json = email: 'hi@attacomsian.com', password: '123abc' > // request options const options = method: 'POST', body: JSON.stringify(json), headers: 'Content-Type': 'application/json' > > // send post request fetch('/login', options) .then(res => res.json()) .then(res => console.log(res)) .catch(err => console.error(err))
You might also like.
System Overlord
A blog about security engineering, research, and general hacking.
Posting JSON with an HTML Form
A coworker and I were looking at an application today that, like so many other modern web applications, offers a RESTful API with JSON being used for serialization of requests/responses. She noted that the application didn’t include any sort of CSRF token and didn’t seem to use any of the headers (X-Requested-With, Referer, Origin, etc.) as a “poor man’s CSRF token”, but since it was posting JSON, was it really vulnerable to CSRF? Yes, yes, definitely yes!
Interestingly, this is reminiscent of many of the confusions between server and browser that are described in Michal Zalewski’s The Tangled Web.
The idea that the use of a particular encoding is a security boundary is, at worst, a completely wrong notion of security, and at best, a stopgap until W3C, browser vendors, or a clever attacker gets hold of your API. Let’s examine JSON encoding as a protection against CSRF and demonstrate a mini-PoC.
The Application
We have a basic application written in Go. Authentication checking is elided for post size, but this is not just an unauthenticated endpoint.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25package main import ( "encoding/json" "fmt" "net/http" ) type Secrets struct Secret int > var storage Secrets func handler(w http.ResponseWriter, r *http.Request) if r.Method == "POST" json.NewDecoder(r.Body).Decode(&storage) > fmt.Fprintf(w, "The secret is %d", storage.Secret) > func main() http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) >As you can see, it basically serves a secret number that can be updated via HTTP POST of a JSON object. If we attempt a URL-encoded or multipart POST, the JSON decoding fails miserably and the secret remains unchanged. We must POST JSON in order to get the secret value changed.
Exploring Options
So let’s explore our options here. The site can locally use AJAX via the XMLHTTPRequest API, but due to the Same-Origin Policy, an attacker’s site cannot use this. For most CSRF, the way to get around this is plain HTML forms, since form submission is not subject to the Same-Origin Policy. The W3C had a draft specification for JSON forms, but that has been abandoned since late 2015, and isn’t supported in any browsers. There are probably some techniques that can make use of Flash or other browser plugins (aren’t there always?) but it can even be done with basic forms, it just takes a little work.
JSON in Forms
Normally, if we try to POST JSON as, say, a form value, it ends up being URL encoded, not to mention including the field name.
method='POST'> name='json' value=''> type='submit'>
Results in a POST body of:
How to use fetch to POST form data as JSON to your API
If you’re using the browser Fetch API to make requests from a web page in your front end through to your back end API, at some point you might need to make requests that send data from a form on that page. The good news is that fetch supports this, but if you want to send the data to your API as JSON, you’ll need to do a little bit of extra work. Let’s take a look at what’s involved.
Example HTML form
In the following steps we’re going to create three small chunks of JavaScript that will take any data entered by a user into these form fields and POST it to our API as JSON.
First Name: Last Name:
👀 It might look like a lot of code in the steps below, but most of it is comments to help you understand what’s happening and why.
Step 1. Listen for when a user submits the form
A submit event is dispatched by the browser when the user clicks the form’s submit button or when they are focused on a form field and press the return key on their keyboard.
const exampleForm = document.getElementById("example-form"); /** * We'll define the `handleFormSubmit()` event handler function in the next step. */ exampleForm.addEventListener("submit", handleFormSubmit);
Step 2. Read the values of all the form fields with FormData
The FormData API is natively supported by most modern browsers and provides a straightforward way of accessing the values for all the fields in a an HTML form: you pass it a reference to a form element and it will do the rest for you.
/** * Event handler for a form submit event. * * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event * * @param event */ async function handleFormSubmit(event) < /** * This prevents the default behaviour of the browser submitting * the form so that we can handle things instead. */ event.preventDefault(); /** * This gets the element which the event handler was attached to. * * @see https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget */ const form = event.currentTarget; /** * This takes the API URL from the form's `action` attribute. */ const url = form.action; try < /** * This takes all the fields in the form and makes their values * available through a `FormData` instance. * * @see https://developer.mozilla.org/en-US/docs/Web/API/FormData */ const formData = new FormData(form); /** * We'll define the `postFormDataAsJson()` function in the next step. */ const responseData = await postFormDataAsJson(< url, formData >); /** * Normally you'd want to do something with the response data, * but for this example we'll just log it to the console. */ console.log(< responseData >); > catch (error) < console.error(error); >>
Step 3. Format the data as JSON and POST it to a URL with fetch
/** * Helper function for POSTing data as JSON with fetch. * * @param options * @param options.url - URL to POST data to * @param options.formData - `FormData` instance * @return - Response body from URL that was POSTed to */ async function postFormDataAsJson(< url, formData >) < /** * We can't pass the `FormData` instance directly to `fetch` * as that will cause it to automatically format the request * body as "multipart" and set the `Content-Type` request header * to `multipart/form-data`. We want to send the request body * as JSON, so we're converting it to a plain object and then * into a JSON string. * * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify */ const plainFormData = Object.fromEntries(formData.entries()); const formDataJsonString = JSON.stringify(plainFormData); const fetchOptions = < /** * The default method for a request with fetch is GET, * so we must tell it to use the POST HTTP method. */ method: "POST", /** * These headers will be added to the request and tell * the API that the request body is JSON and that we can * accept JSON responses. */ headers: < "Content-Type": "application/json", "Accept": "application/json" >, /** * The body of our POST request is the JSON string that * we created above. */ body: formDataJsonString, >; const response = await fetch(url, fetchOptions); if (!response.ok) < const errorMessage = await response.text(); throw new Error(errorMessage); >return response.json(); >
Full code example without comments
Here’s all of the JavaScript code from the steps above pulled together without inline comments:
/** * Helper function for POSTing data as JSON with fetch. * * @param options * @param options.url - URL to POST data to * @param options.formData - `FormData` instance * @return - Response body from URL that was POSTed to */ async function postFormDataAsJson(< url, formData >) < const plainFormData = Object.fromEntries(formData.entries()); const formDataJsonString = JSON.stringify(plainFormData); const fetchOptions = < method: "POST", headers: < "Content-Type": "application/json", Accept: "application/json", >, body: formDataJsonString, >; const response = await fetch(url, fetchOptions); if (!response.ok) < const errorMessage = await response.text(); throw new Error(errorMessage); >return response.json(); > /** * Event handler for a form submit event. * * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event * * @param event */ async function handleFormSubmit(event) < event.preventDefault(); const form = event.currentTarget; const url = form.action; try < const formData = new FormData(form); const responseData = await postFormDataAsJson(< url, formData >); console.log(< responseData >); > catch (error) < console.error(error); >> const exampleForm = document.getElementById("example-form"); exampleForm.addEventListener("submit", handleFormSubmit);
Handling JSON request bodies in an Express based API
If your API is built with Express you’ll want to configure your routes to be able to accept JSON request bodies. You can use the body-parser middleware to handle this for you. It can be configured to parse the JSON request body for POST/PUT/PATCH requests and it will then add it as an object under a body property in the request object. This means it’s then available to any middleware or route handlers that are run after the body-parser middleware as request.body .
const bodyParser = require("body-parser"); app.use(bodyParser.json()); app.post("/user", (request, response) => < /** * In a real application, the data in `request.body` should * be validated before its used for anything e.g. inserting * into a database. */ const newUser = < first_name: request.body.first_name, last_name: request.body.last_name >; response.status(201).json(newUser); >);
This blog post was inspired by a question that I received from one of my new subscribers, Stéphanie. She had built a static front end with Eleventy, and an API with Express and a MongoDB database. Stéphanie wanted to know how she could have the JavaScript in her front end send data from a form on the page to her API, which would then save it to the database. Thanks for the question, Stéphanie!
Share this
Html post запрос json
Для отправки данных в JavaScript с помощью объекта XMLHttpRequest в его метод send() передаются отправляемые данные.
Для тестирования отправки определим простейший сервер на node.js, который принимает данные:
const http = require("http"); const fs = require("fs"); http.createServer(async (request, response) => < if(request.url == "/user")< const buffers = []; // буфер для получаемых данных // получаем данные из запроса в буфер for await (const chunk of request) < buffers.push(chunk); >// получаем строковое представление ответа let userName = Buffer.concat(buffers).toString(); userName = userName + " Smith"; response.end(userName); > else < fs.readFile("index.html", (error, data) =>response.end(data)); > >).listen(3000, ()=>console.log("Сервер запущен по адресу http://localhost:3000"));В данном случае при обращении по адресу "/user" сервер получает все отправленные данные. Объект запроса предоставляет итератор для извлечения данные. И в коде сервера эти данные передаются в специальный массив-буфер:
for await (const chunk of request)
Затем с помощью метода Buffer.concat() объединяем все полученные данные и формируем из них строку:
let userName = Buffer.concat(buffers).toString();В данном случае мы предполагаем, что на сервер отправляется простая строка с текстом. Пусть она представляет некоторое имя пользователя. И для примера к этому имени добавляется фамилия и измененное имя отправляется обратно клиенту:
userName = userName + " Smith"; response.end(userName);Теперь определим на странице index.html код для отправки данных на этот сервер:
Для отправки применяется метод POST. А в качестве отправляемых данных выступает простая строка "Tom". То есть на сервер отправляется простой текст. И поскольку сервер в ответ также отправляет текст, то для получения ответа здесь применяется свойство xhr.responseText . И при запуске данной веб-страницы будет выполняться отправка данных на сервер, и в консоли браузера можно будет увидеть полученный от сервера ответ:
Отправка json
Подобным образом можно отправлять более сложные по структуре данные. Например, рассмотрим отправку json. Для этого на строне node.js определим следующий сервер:
const http = require("http"); const fs = require("fs"); http.createServer(async (request, response) => < if(request.url == "/user")< const buffers = []; for await (const chunk of request) < buffers.push(chunk); >const data = Buffer.concat(buffers).toString(); const user = JSON.parse(data); // парсим строку в json // изменяем данные полученного объекта user.name = user.name + " Smith"; user.age += 1; // отправляем измененый объект обратно клиенту response.end(JSON.stringify(user)); > else < fs.readFile("index.html", (error, data) =>response.end(data)); > >).listen(3000, ()=>console.log("Сервер запущен по адресу http://localhost:3000"));В данном случае на сервера ожидаем, что мы получим объект в формате json, который имеет два свойства - name и age. Для примера сервер меняет значения этих свойств и отправляет измененный объект обратно клиенту.
На веб-странице установим объект json для отправки и получим обратно данные:
Здесь на сервер отправляется объект tom, который имеет два свойства: name и age. Перед отправкой он кодируется в формат json с помощью функции JSON.stringify() .
const data = JSON.stringify(tom);При отправке с помощью метода setRequestHeader() для заголовка "Content-Type" устанавливаем значение "application/json", тем самым указывая, что мы отправляем данные в формате json:
xhr.setRequestHeader("Content-Type", "application/json");В обработчике события load сначала парсим текст ответа из формата json в стандартный объект JavaScript:
const user = JSON.parse(xhr.responseText)Затем выводим данные полученного объекта на консоль браузера: