Ajax con Vanilla Node.js / Javascript - Post JSON sin un formulario -- javascript campo con node.js campo con reinventing-the-wheel campo con json campo con ajax camp codereview Relacionados El problema

AJAX with Vanilla Node.js/JavaScript – POST JSON without a form


5
vote

problema

Español

Soy nuevo en el código NODE.JS, AJAX y asíncrono. He reconsiderado una forma de trabajo de intercambiar JSON entre el navegador y el servidor y me pregunto si alguien con más experiencia considera que el enfoque sea razonable para mis necesidades.

Contexto de requisito: esta técnica se utilizará para la actualización de datos de back-office. Inicialmente, habrá un usuario en la misma computadora que la base de datos. Prefiero no descartar que permitiera a algunos otros usuarios actualizar de forma remota el backend en el futuro, pero si eso sucede, usarían pantallas más simples diseñadas con la ubicación remota en mente. Dado que esta es la oficina de regreso, puedo insistir en el último navegador de Chrome.

Mis limitaciones: Soy una tienda de un solo hombre en desarrollo / apoyando un sitio web en semi-jubilación. Soy a tiempo parcial y mis responsabilidades se extienden más allá de la codificación, así que, en el mejor de los casos, puedo ser un gato de algunos oficios, pero maestro de ninguno.

Puede saltar directamente a "requisito" a continuación , o si desea más contexto, siga leyendo:

¿Por qué no usar una biblioteca para hacer esto?: Estoy abierto a que me dicen que debería, pero prefiero evitar las bibliotecas a menos que haya una razón convincente para usarlas. Soy una tienda de 1 hombres que necesita averiguar cómo hacer un puñado de cosas y luego automatizar, volver a hacerlos una y otra vez. Es una situación muy diferente a la que una gran empresa haciendo una multitud de cosas. Por ejemplo, la parte frontal de esta aplicación tiene medio millón de páginas web estáticas escritas por el back-end. Solo hay media docena de tipos de páginas y cada carga con un solo golpe al servidor. Están escritos con html de vainilla, CSS y JavaScript. Los menús se verían mejor si se usara una biblioteca (o cuando paso más tiempo en ellos), pero hay valor en la simplicidad. Las bibliotecas complican el control de la versión, generalmente proporcionan una enorme capacidad que nunca usaré, y arrastraré por el soporte legado que no necesito. Mi experiencia personal es que en relación con muchos otros, estoy mejor para ir un poco más profundo en un producto que en recordar cómo se conectan múltiples productos.

Justificación de la herramienta: No hay planes para que el sitio genere ingresos, por lo que una de las razones por las que elegí MySQL y Nede.js es que no hay costos de licencia para un hobby, espero continuar durante veinte años. Las herramientas parecen a la altura de mis requisitos y son lo suficientemente populares para poder encontrar formas de hacer las cosas en Internet. Además, como una tienda a tiempo parcial, 1 hombre, poder usar el mismo idioma en el servidor y el navegador es una ventaja enorme.

Requisito: con el nodo de vainilla liso.js, mueva archivos JSON grandes entre el navegador y el servidor para admitir el mantenimiento de datos de backend para un sitio web. He probado la solución actual y mueve más datos de los que necesito moverme más rápidamente de lo que necesito para moverlo. (Probé 100,000 objetos que requieren archivos de 8meg JSON en ambas formas). Pero no tengo experiencia en AJAX y temo que haya problemas, no estoy prevén o formas fáciles de hacer este tipo de Ajax de una manera mejor.

problemas que consideré:

  • Cors requiere que el servidor que responda a una solicitud AJAX, primero debe haber servido al archivo HTML que realiza la solicitud.
  • Las publicaciones de
  • Ajax no son ideanpotentes
  • El navegador solicitará automáticamente un favicon

En el servidor, la instrucción Switch se volverá demasiado tiempo si sigo agregando casos para cada página de mantenimiento. Lo limpiaré y daré la bienvenida a cualquier consejo general que esté dispuesto a ofrecer, ¡pero mi pregunta aquí es si la técnica AJAX es razonable para mis necesidades?

Incluyendo imágenes colorizadas-VSCODE del código para que lean más fácil, así como el código real, puede copiar a una computadora y ejecutar. El código de muestra se puede probar, al igual que colocar el archivo .js y .html en el mismo directorio y nombrarlos "test_ajax_post_json_sans_form". Prueba ejecutando ejecutando "nombre de archivo de nodo" en la consola de comandos, y luego cargando http: // localhosthost : 8000 / nombre de archivo en su navegador. Usé Chrome.

HTML con código de código cliente original (consulte el fragmento para actualizar): ingrese la descripción de la imagen aquí

JS Imagen: ingrese la descripción de la imagen aquí

Incluí el código en fragmentos, pero se requiere que Node.js se ejecute, por lo que tendrá que copiarlo a una computadora con Node.js. (HTML actualizado por sugerencias 10/11):

  'use strict'; const host = 'localhost'; const http = require('http'); // VSCode shows 3 dots under "require" and says something about NodeRequire??? const fs   = require('fs').promises; const port = 8000;  const requestListener = function (req, res) {     switch (req.url) {         case "/test_ajax_post_json_sans_form.html": // serve an intial html file             fs.readFile(__dirname + '/test_ajax_post_json_sans_form.html')             .then(contents => {                 res.writeHead(200, {'Content-Type': 'text/html'});                 res.end(contents);             })             .catch(err => {                 res.writeHead(500);                 res.end(err);                 return;             });             break         case '/test_ajax_post_json_sans_form.html/ajaxTest1': // receive json, process, then return other json             let body = '';             req.on('data', (data) => {                 body += data;             });             req.on('end', () => {                 console.log(body);  // to show all data has arrived                 // here we will check or errors, create a complex return-object, stringify it, and send it back                 const objToReturn = {data1: 'Test message from server', data2: 'could be a complex json object'};                 res.writeHead(200, {'Content-Type': 'application/json'});                 res.end(JSON.stringify(objToReturn));             });                      break         case '/favicon.ico':             // browser will call favicon automatically. This satisfies the request (though its failing             //   won't keep the ajax from working).             fs.readFile(__dirname + '/favicon.ico')             .then(contents => {                 res.writeHead(200, {'Content-Type': 'image/x-icon'});                 res.end(contents);             })             .catch(err => { // lacking favicon will not impact test                 res.writeHead(200, {'Content-Type': 'image/x-icon'});                 res.end();             });             break         default:             res.writeHead(404);             res.end(JSON.stringify({error:'Resource not found'})); // in production, might load a not-found page here     } }  const server = http.createServer(requestListener); server.listen(port,host, () => { // binds the server object to a newtwork address     console.log(`Server is running on http://${host}:${port}. (^c to cancel)`); });  
  <!DOCTYPE html><body>     <button type="button" id="submitButton">Send and then Receive JSON</button>     <br>After clicking the button, the JSON string sent from the browser to the server will show in the console.     <br>Then the JSON response string from the server will replace what is below.     <br><br>     <div id='messageArea'>         Message to replace via ajax.     </div>      <script>         'use strict';         const submitButton = document.getElementById('submitButton');         const messageArea = document.getElementById('messageArea');         async function exchangeJSON() {             try {                 submitButton.disabled = true;                 // here can build a complex object to send                 const objToSend = {message1: 'Test message from browser', message2: 'could be a complex JSON string'};                 const response = await fetch('test_ajax_post_json_sans_form.html/ajaxTest1', {                         method: 'POST',                         body: JSON.stringify(objToSend)                 });                 if (response.ok) {                     const jsonResponse = await response.json();                     // here can parse, update screen, etc.                     messageArea.textContent = JSON.stringify(jsonResponse);                 }                 //throw new Error('Test error in ExchangeJSON'); // uncomment for testing             } catch (error) {                 messageArea.textContent = error;             } finally {                 submitButton.disabled = false;             }         }          submitButton.addEventListener('click',exchangeJSON);     </script> </body></html>  

Original en ingles

I am new to node.js, ajax, and asynchronous code. Ixe2x80x99ve pieced together a working way to exchange json between browser and server and am wondering if someone with more experience considers the approach reasonable for my needs.

Requirement Context: This technique will be used for back-office update of data. Initially there will be one user on the same computer as the database. Ixe2x80x99d rather not rule out allowing a few other users to remotely update the backend in the future, but if that happens, they would use simpler screens designed with the remote location in mind. Since this is back-office, I can insist on the latest Chrome browser.

My limitations: Ixe2x80x99m a one-man shop developing/supporting a website in semi-retirement. I am part-time and my responsibilities extend beyond coding, so at best I can be a jack of some trades, but master of none.

You can skip straight to xe2x80x9cRequirementxe2x80x9d below, or if you want more context, read on:

Why Not Use a Library to Do This?: I am open to being told that I should, but prefer to avoid libraries unless there is compelling reason to use them. Ixe2x80x99m a 1-man shop who needs to figure out how to do a handful of things and then automate doing them again and again. Itxe2x80x99s a very different situation than a large company doing a multitude of things. For example, the front-end of this application has half a million static web pages written by the back-end. There are only half a dozen page types and each loads with a single hit to the server. They are written with plain vanilla html, css, and javascript. The menus would look nicer if a library were used (or when I spend more time on them), but there is value in the simplicity. Libraries complicate version control, typically provide enormous capability I will never use, and drag along legacy support I donxe2x80x99t need. My personal experience is that relative to many others, I am better at going a little deeper on one product than I am at remembering how multiple products interface.

Tool Rationale: There are no plans for the site to generate revenue, so one of the reasons I chose MySQL and node.js is that there is no licensing costs for a hobby I hope to continue for twenty years. The tools seem up to my requirements and are popular enough that I can find ways of doing things on the internet. Also, as a part-time, 1-man shop, being able to use the same language on the server and browser is an enormous advantage.

Requirement: With plain vanilla node.js, move large json files between browser and server to support backend data maintenance for a website. I have tested the current solution and it moves more data than I need to move more quickly than I need to move it. (I tested 100,000 objects requiring 8Meg json files both ways.) But I have no ajax experience and fear there might be problems Ixe2x80x99m not foreseeing or easy ways to do this kind of ajax in a better way.

Issues I considered:

  • CORS requires that the server that responds to an ajax request, must first have served the html file making the request.
  • Ajax POSTs are not idempotent
  • The browser will automatically call for a favicon

On the server, the switch statement is going to get too long if I keep adding cases for each maintenance page. Ixe2x80x99ll clean that up and welcome any general advice youxe2x80x99re willing to offer, but my question here is if the ajax technique is reasonable for my needs?

Ixe2x80x99m including colorized-VSCode images of the code to make reading it easier, as well as actual code you can copy to a computer and run. The sample code can be tested as is by putting the .js and .html file in the same directory and naming them "test_ajax_post_json_sans_formxe2x80x9d. You would test by executing "node filename" in the command console, and then loading http://localhost:8000/filename in your browser. I used Chrome.

html with client code image ORIGINAL (SEE snippet for update): enter image description here

js image: enter image description here

I included the code in snippets, but it requires node.js to run, so you'd have to copy it to a computer with node.js. (HTML UPDATED per suggestions 10/11):

'use strict'; const host = 'localhost'; const http = require('http'); // VSCode shows 3 dots under "require" and says something about NodeRequire??? const fs   = require('fs').promises; const port = 8000;  const requestListener = function (req, res) {     switch (req.url) {         case "/test_ajax_post_json_sans_form.html": // serve an intial html file             fs.readFile(__dirname + '/test_ajax_post_json_sans_form.html')             .then(contents => {                 res.writeHead(200, {'Content-Type': 'text/html'});                 res.end(contents);             })             .catch(err => {                 res.writeHead(500);                 res.end(err);                 return;             });             break         case '/test_ajax_post_json_sans_form.html/ajaxTest1': // receive json, process, then return other json             let body = '';             req.on('data', (data) => {                 body += data;             });             req.on('end', () => {                 console.log(body);  // to show all data has arrived                 // here we will check or errors, create a complex return-object, stringify it, and send it back                 const objToReturn = {data1: 'Test message from server', data2: 'could be a complex json object'};                 res.writeHead(200, {'Content-Type': 'application/json'});                 res.end(JSON.stringify(objToReturn));             });                      break         case '/favicon.ico':             // browser will call favicon automatically. This satisfies the request (though its failing             //   won't keep the ajax from working).             fs.readFile(__dirname + '/favicon.ico')             .then(contents => {                 res.writeHead(200, {'Content-Type': 'image/x-icon'});                 res.end(contents);             })             .catch(err => { // lacking favicon will not impact test                 res.writeHead(200, {'Content-Type': 'image/x-icon'});                 res.end();             });             break         default:             res.writeHead(404);             res.end(JSON.stringify({error:'Resource not found'})); // in production, might load a not-found page here     } }  const server = http.createServer(requestListener); server.listen(port,host, () => { // binds the server object to a newtwork address     console.log(`Server is running on http://${host}:${port}. (^c to cancel)`); });
<!DOCTYPE html><body>     <button type="button" id="submitButton">Send and then Receive JSON</button>     <br>After clicking the button, the JSON string sent from the browser to the server will show in the console.     <br>Then the JSON response string from the server will replace what is below.     <br><br>     <div id='messageArea'>         Message to replace via ajax.     </div>      <script>         'use strict';         const submitButton = document.getElementById('submitButton');         const messageArea = document.getElementById('messageArea');         async function exchangeJSON() {             try {                 submitButton.disabled = true;                 // here can build a complex object to send                 const objToSend = {message1: 'Test message from browser', message2: 'could be a complex JSON string'};                 const response = await fetch('test_ajax_post_json_sans_form.html/ajaxTest1', {                         method: 'POST',                         body: JSON.stringify(objToSend)                 });                 if (response.ok) {                     const jsonResponse = await response.json();                     // here can parse, update screen, etc.                     messageArea.textContent = JSON.stringify(jsonResponse);                 }                 //throw new Error('Test error in ExchangeJSON'); // uncomment for testing             } catch (error) {                 messageArea.textContent = error;             } finally {                 submitButton.disabled = false;             }         }          submitButton.addEventListener('click',exchangeJSON);     </script> </body></html>
              

Lista de respuestas

3
 
vote
vote
La mejor respuesta
 

Considere fetch En los navegadores modernos, que se garantiza que su código se está ejecutando, 99887776655544331 suele ser una mejor opción que XMLHttpRequest2 - fetch se basa en la promesa (las promesas suelen ser un poco mejor para trabajar que las devoluciones de llamada), su API es un poco más limpia para leer y escribir, y es un poco más conciso.

Manipulación de errores El front-end no tiene un manejo de errores. Si la solicitud falla por cualquier motivo, no habrá indicio de eso al usuario, después de presionar el botón, parecerá procesarse para siempre, sin volver a discapacitarse. Considerar

  • mostrando el mensaje de error si hay un error, y
  • re-habilitando el botón si hay un error

Respuesta Si desea mostrar el JSON de la respuesta al usuario:

  • No establece la respuesta como innerHTML de un elemento. Esto puede resultar en la ejecución de código arbitraria, elementos HTML adicionales inesperados y cosas extrañas relacionadas con las entidades HTML. Use .textContent en su lugar.
  • Dado que la respuesta es JSON, tal vez use un elemento más similar a un código, como un <pre> ?

Callbacks Cuando agrega un oyente, si desea que se ejecute una devolución de llamada, si la devolución de llamada no toma ningún argumento, puede pasar la devolución de llamada directamente a < Código> addEventListener en lugar de envolverlo en otra función.

Semicolones Algunas de tus líneas faltan como punto y coma. Para ser estilísticamente consistente, ya sea que los use o no, y si no elige, con suerte, usted sea un experto, de lo contrario, puede encontrar problemas con Inserción de punto y coma automática . Elija un estilo, luego haga cumplir con un linter .

idsubmitbutton? El nombre de la variable del botón seleccionado debe probaly ser algo así como submitButton - El ID no es relevante después de que se haya seleccionado. Tener id Como prefijo en el atributo de identificación también es extraño, tal vez solo use fetch0 .

  fetch1  

en el backend:

Servir archivos estáticos con código seco El controlador de solicitud y respuesta para el archivo HTML y el favicon se codifica actualmente en el servidor HTTP. Si bien eso puede trabajo , se necesita una cantidad molesta de código de placa de calderas y es un poco feo. Considere si tuvo 4 o 5 archivos estáticos para servir en su lugar; Su método actual no es escalable.

Prefiero evitar las bibliotecas a menos que haya una razón convincente para usarlos

Esta es una razón muy convincente para usarlos. Aunque es cierto, a menudo vienen con muchas características, no le importa, vale la pena por una o dos o tres características no triviales que, de lo contrario, tendría que implementarse tediosamente.

¿Cuál? Recomendaría expreso, su uso está muy extendido, y está bien documentado en su sitio, en el desbordamiento de la pila, y en muchos otros lugares alrededor de Internet.

rutas separadas en diferentes archivos Aunque me refactoré a todos menos uno a continuación, para el caso general cuando tiene múltiples puntos finales no estáticos en un servidor que necesita manejar la lógica diferente, considere separar la separación del Diferentes rutas en diferentes archivos. Por ejemplo, podría tener un archivo que exporta una función que maneja las solicitudes fetch2 y otro archivo que exporta una función que maneja fetch3 solicitudes (al igual que un ejemplo). A medida que crece su solicitud, esto es mucho más mantenido que poner todo en un solo archivo.

  fetch4  
 

Consider fetch In modern browsers, which your code is guaranteed to be running in, fetch is usually a better choice than XMLHttpRequest - fetch is Promise-based (Promises are usually a bit nicer to work with than callbacks), its API is a bit cleaner to read and write, and it's a bit more concise.

Error handling The front-end has no error handling. If the request fails for whatever reason, there will be no indication of that to the user - after the button is pressed, it'll appear to be processing forever, without becoming un-disabled again. Consider

  • Displaying the error message if there's an error, and
  • Re-enabling the button if there's an error

Response If you want to show the response's JSON to the user:

  • Don't set the response as innerHTML of an element. This can result in arbitrary code execution, unexpected additional HTML elements, and weird things related to HTML entities. Use .textContent instead.
  • Since the response is JSON, maybe use a more code-like element, like a <pre>?

Callbacks When you add a listener, if you want a callback to run, if the callback doesn't take any arguments, you can pass the callback directly to addEventListener instead of wrapping it in another function.

Semicolons Some of your lines are missing semicolons. To be stylistically consistent, either use them or don't - and if you choose not to, hopefully you're an expert, else you may well run into problems with automatic semicolon insertion. Choose a style, then enforce it with a linter.

idSubmitButton? The selected button's variable name should probbaly be something like submitButton - the ID isn't relevant after it's been selected. Having id as a prefix in the id attribute is strange too, maybe just use submitButton.

 <button type="button" id="submitButton">Send and then Receive JSON</button> <br>After clicking the button, the JSON string sent from the browser to the server will show in the console. <br>Then the JSON response string from the server will replace what is below. <br><br> <div class='error' style='color: red; display: none;'></div> <pre>Response gets inserted here</div> <script>     'use strict';     const submitButton = document.getElementById('submitButton');     function exchangeJSON() {         submitButton.disabled = true; // assure post isn't sent again prior to a response         const testObjToSend = { message1: 'Test message from browser', message2: 'could be a complex JSON string' };         const errorDiv = document.querySelector('.error');         errorDiv.style.display = 'none'; // Hide previous error         // If you want to cause an error if the transaction is taking way longer than expected,         // see https://stackoverflow.com/q/46946380         fetch(             'ajaxTest1',             {                 method: 'POST',                 body: JSON.stringify(testObjToSend),                 headers: {                     'Accept': 'application/json',                     'Content-Type': 'application/json'                 },             },         )             .then(res => res.text()) // if you wanted to expand error handling, could check if response is OK first             .then((result) => {                 try {                     // If it's not JSON, this will throw                     JSON.parse(result);                 } catch(e) {                     // Send text to catch handler below                     throw new Error(result);                 }                 document.querySelector('pre').textContent = result;             })             .catch((error) => {                 errorDiv.style.display = 'block';                 errorDiv.textContent = JSON.stringify(error.message);             })             .finally(() => {                 submitButton.disabled = false; // post completed, so enable posting again             });     }     submitButton.addEventListener('click', exchangeJSON); </script> 

Onto the backend:

Serve static files with DRY code The request and response handler for both the HTML file and the favicon are currently hard-coded into the HTTP server. While that can work, it takes an annoying amount of boilerplate code and is kind of ugly. Consider if you had 4 or 5 static files to serve instead; your current method isn't scaleable.

I prefer to avoid libraries unless there is compelling reason to use them

This is a very compelling reason to use them. Although it's true they often come with many features one doesn't care about, it's worth it for the one or two or three nontrivial features that you would otherwise have to tediously implement yourself.

Which one? I'd recommend Express, its use is very widespread, and it's well-documented on their site, on Stack Overflow, and on many other places around the internet.

Separate routes into different files Although I refactored all but one out below, for the general case when you have multiple non-static endpoints on a server that need to handle different logic, consider separating out the different routes into different files. For example, you could have one file that exports a function that handles ajaxTest1 requests, and another file that exports a function that handles login requests (just as an example). As your application grows, this is much more maintainable than putting everything into a single file.

'use strict'; const port = 8000; const express = require('express'); // Recommended to use compression if you're transferring large files: // https://github.com/expressjs/compression const compression = require('compression');  const app = express(); app     .use(compression())     // Put static files into the "public" directory:     .use(express.static(__dirname + '/public'))     // Parse JSON request bodies:     .use(express.json())     .post('/ajaxTest1', (req, res) => {         console.log(req.body);         res.status(200).json({ data1: 'Test message from server', data2: 'could be a complex json object' });     })     .listen(port); console.log(`Server is running on http://localhost:${port}. (^c to cancel)`); 
 
 
       
       

Relacionados problema

5  Validación del formulario AJAX  ( Ajax form validation ) 
Este es mi primer intento de la validación del formulario AJAX, y todo funciona como se esperaba, el ejemplo final final mantendrá muchos más campos de entrad...

3  MARCA DE TEXTBOX JavaScript de doble modo  ( Dual mode javascript textbox watermark ) 
Solo estaba tratando de hacer una marca de agua AJAX alternativa desde "Fecha de inserción" (primer modo) a "MM / DD / YYAY" (segundo modo). Después de algún ...

2  Parámetros JavaScript - Mejores prácticas para configuraciones objeto / devolución de llamada  ( Javascript parameters best practices for settings object callback ) 
Tengo la siguiente función que se ejecuta como se esperaba y se define en el objeto 9988777665544339 .appedTo0 que funciona como se esperaba. Sin em...

8  Biblioteca básica de JavaScript  ( Basic javascript library ) 
Mi propio CMS está utilizando actualmente JQERY, pero como uno de los objetivos es que todo el proyecto sea muy pequeño, he decidido escribir mi propia biblio...

1  Actualización de una página web de DRUPAL con contenido basado en texto ingresado  ( Updating a drupal web page with content based on inputted text ) 
La intención es actualizar una página web de drupal existente con contenido basado en texto ingresado en un cuadro de texto. La página existente muestra dat...

7  Plugin de paginación  ( Pagination plugin ) 
Este complemento está escrito en jQuery y se hace para admitir el marco de CodeIgniter . Es un complemento de paginación de tabla de AJAX diseñada para propo...

0  ¿Cómo puedo mostrar los resultados de mysql_fetch_assoc en una tabla dinámica?  ( How can i display the results of mysql fetch assoc in a dynamic table ) 
Tengo una base de datos MySQL con varias columnas y dos filas de datos, hasta ahora. Estoy usando mysql_fetch_assoc para devolver los datos en la base de dato...

2  Llamada Ajax en JavaScript  ( Ajax call in javascript ) 
Sé que hay muchas bibliotecas con esto construido en $.get() , etc ... Pero como para escribir esto en Javascript puro ¿Cómo se ve esto? Cells0 ...

8  Tema / Cambio de la piel ... Versión 2! (CSS-Drived)  ( Theme skin swap version 2 css driven ) 
Básicamente, tengo una piel de AJAX (HTML / CSS) que se carga en un elemento ficticio (ID = SkinContainer) cuando la página está cargada, y luego el contenido...

8  Escribiendo un widget de jquery: plantilla  ( Writing a jquery widget templating ) 
Estoy haciendo mi primer mayor desarrollo de jQuery. Es un widget para eventos recurrentes, y es como una bestia bastante compleja. El código completo está di...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos