node.js - make - nodejs http server
nodejs-¿Cómo promisificar http.request? rechazo fue llamado dos veces (3)
Estoy tratando de envolver http.request
en Promise
:
new Promise(function(resolve, reject) {
var req = http.request({
host: ''127.0.0.1'',
port: 4000,
method: ''GET'',
path: ''/api/v1/service''
}, function(res) {
if (res.statusCode < 200 || res.statusCode >= 300) {
// First reject
reject(new Error(''statusCode='' + res.statusCode));
return;
}
var body = [];
res.on(''data'', function(chunk) {
body.push(chunk);
});
res.on(''end'', function() {
try {
body = JSON.parse(Buffer.concat(body).toString());
} catch(e) {
reject(e);
return;
}
resolve(body);
});
});
req.on(''error'', function(err) {
// Second reject
reject(err);
});
req.write(''test'');
}).then(function(data) {
console.log(data);
}).catch(function(err) {
console.log(err);
});
Si recibo un statusCode
estado statusCode
del servidor remoto, llamará Primero rechazar y después de un poco de tiempo Segundo rechazo . ¿Cómo hacer correctamente para que solo llame a un solo rechazo (creo que el primer rechazo es correcto en este caso)? Creo que necesito cerrar los res
, pero no hay close()
método close()
en el objeto ClientResponse
.
UPD: El segundo rechazo se dispara muy raramente, ¿por qué?
Es más fácil para usted usar bluebird api, puede promisify módulo de solicitud y usar la función de solicitud async como una promesa en sí misma, o tiene la opción de usar el módulo request-promise , que hace que no trabaje para crear una promesa sino que use y el objeto que ya encapsula el módulo usando la promesa, aquí hay un ejemplo:
var rp = require(''request-promise'');
rp({host: ''127.0.0.1'',
port: 4000,
method: ''GET'',
path: ''/api/v1/service''})
.then(function (parsedBody) {
// GET succeeded...
})
.catch(function (err) {
// GET failed...
});
Sé que esta pregunta es antigua, pero la respuesta me inspiró a escribir una versión moderna de un cliente HTTP ligero y prometido. Aquí hay una nueva versión que:
- Utilice la sintaxis de JavaScript actualizada
- Validar entrada
- Soporta múltiples métodos
- Es fácil de ampliar para el soporte HTTPS
- Permitirá que el cliente decida cómo tratar con los códigos de respuesta
- También permitirá que el cliente decida cómo tratar con cuerpos que no son JSON
Código abajo:
function httpRequest(method, url, body = null) {
if (![''get'', ''post'', ''head''].includes(method)) {
throw new Error(`Invalid method: ${method}`);
}
let urlObject;
try {
urlObject = new URL(url);
} catch (error) {
throw new Error(`Invalid url ${url}`);
}
if (body && method !== ''post'') {
throw new Error(`Invalid use of the body parameter while using the ${method.toUpperCase()} method.`);
}
let options = {
method: method.toUpperCase(),
hostname: urlObject.hostname,
port: urlObject.port,
path: urlObject.pathname
};
if (body) {
options.headers[''Content-Length''] = Buffer.byteLength(body);
}
return new Promise((resolve, reject) => {
const clientRequest = http.request(options, incomingMessage => {
// Response object.
let response = {
statusCode: incomingMessage.statusCode,
headers: incomingMessage.headers,
body: []
};
// Collect response body data.
incomingMessage.on(''data'', chunk => {
response.body.push(chunk);
});
// Resolve on end.
incomingMessage.on(''end'', () => {
if (response.body.length) {
response.body = response.body.join();
try {
response.body = JSON.parse(response.body);
} catch (error) {
// Silently fail if response is not JSON.
}
}
resolve(response);
});
});
// Reject on request error.
clientRequest.on(''error'', error => {
reject(error);
});
// Write request body if present.
if (body) {
clientRequest.write(body);
}
// Close HTTP connection.
clientRequest.end();
});
}
Tu código está casi bien. Para reformular un poco, desea una función que envuelva http.request con este formulario:
function httpRequest(params, postData) {
return new Promise(function(resolve, reject) {
var req = http.request(params, function(res) {
// on bad status, reject
// on response data, cumulate it
// on end, parse and resolve
});
// on request error, reject
// if there''s post data, write it to the request
// important: end the request req.end()
});
}
Observe la adición de params
y postData
para que se pueda utilizar como una solicitud de propósito general. Y observe que la última línea req.end()
, que siempre debe llamarse, faltaba del código OP.
Aplicando esos cambios de pareja al código OP ...
function httpRequest(params, postData) {
return new Promise(function(resolve, reject) {
var req = http.request(params, function(res) {
// reject on bad status
if (res.statusCode < 200 || res.statusCode >= 300) {
return reject(new Error(''statusCode='' + res.statusCode));
}
// cumulate data
var body = [];
res.on(''data'', function(chunk) {
body.push(chunk);
});
// resolve on end
res.on(''end'', function() {
try {
body = JSON.parse(Buffer.concat(body).toString());
} catch(e) {
reject(e);
}
resolve(body);
});
});
// reject on request error
req.on(''error'', function(err) {
// This is not a "Second reject", just a different sort of failure
reject(err);
});
if (postData) {
req.write(postData);
}
// IMPORTANT
req.end();
});
}
Esto no se ha probado, pero debería funcionar bien ...
var params = {
host: ''127.0.0.1'',
port: 4000,
method: ''GET'',
path: ''/api/v1/service''
};
// this is a get, so there''s no post data
httpRequest(params).then(function(body) {
console.log(body);
});
Y estas promesas pueden ser encadenadas, también ...
httpRequest(params).then(function(body) {
console.log(body);
return httpRequest(otherParams);
}).then(function(body) {
console.log(body);
// and so on
});