Error handling in an Express route - error-handling

I have a Node module that wraps a RESTful API. This client follows the standard Node callback pattern:
module.exports = {
GetCustomer = function(id, callback) { ...}
}
I am calling this client from various Express routes like so:
app.get('/customer/:customerId', function(req,res) {
MyClient.GetCustomer(customerId, function(err,data) {
if(err === "ConnectionError") {
res.send(503);
}
if(err === "Unauthorized") {
res.send(401);
}
else {
res.json(200, data);
}
};
};
The issue is that I think it's not DRY to check for "ConnectionError" every time I call this client. I don't believe I can call res.next(err) because that will get sent back as a 500 error.
Is there a Node or Javascript pattern I am missing here? In C# or Java, I would throw the appropriate exception in MyClient.

You want to create error handling middleware. Here's an example from Express: https://github.com/visionmedia/express/blob/master/examples/error-pages/index.js
Here's what I use:
module.exports = function(app) {
app.use(function(req, res) {
// curl https://localhost:4000/notfound -vk
// curl https://localhost:4000/notfound -vkH "Accept: application/json"
res.status(404);
if (req.accepts('html')) {
res.render('error/404', { title:'404: Page not found', error: '404: Page not found', url: req.url });
return;
}
if (req.accepts('json')) {
res.send({ title: '404: Page not found', error: '404: Page not found', url: req.url });
}
});
app.use( function(err, req, res, next) {
// curl https://localhost:4000/error/403 -vk
// curl https://localhost:4000/error/403 -vkH "Accept: application/json"
var statusCode = err.status || 500;
var statusText = '';
var errorDetail = (process.env.NODE_ENV === 'production') ? 'Sorry about this error' : err.stack;
switch (statusCode) {
case 400:
statusText = 'Bad Request';
break;
case 401:
statusText = 'Unauthorized';
break;
case 403:
statusText = 'Forbidden';
break;
case 500:
statusText = 'Internal Server Error';
break;
}
res.status(statusCode);
if (process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test') {
console.log(errorDetail);
}
if (req.accepts('html')) {
res.render('error/500', { title: statusCode + ': ' + statusText, error: errorDetail, url: req.url });
return;
}
if (req.accepts('json')) {
res.send({ title: statusCode + ': ' + statusText, error: errorDetail, url: req.url });
}
});
};

Related

Why is couchdb login good but couchdb session empty, using vuejs and pouchdb

Using Vusjs, pouchdb-browser, CouchDB, pouchdb-authentication
I want to check if a session is open to use for offline stay logged in.
When I login with db.logIn from pouchdb-authentication:
response: {ok: true, name: "01HAYJ", roles: Array(1)}
When I run "getSession" i get userCtx.name as null
session response:
info:
authentication_handlers: Array(2)
0: "cookie"
1: "default"
length: 2
__proto__: Array(0)
__proto__: Object
ok: true
userCtx:
name: null
roles: Array(0)
Here is a snippet of my action:
setUser({commit, dispatch},payload){
console.log('store action payload: ', payload);
var user = payload.user;
var pass = payload.pass
db.logIn(user, pass , function (err, response) {
if (err) {
if (err.name === 'unauthorized' || err.name === 'forbidden') {
alert("Login failed, check user or password!")
} else {
}
}
}).then(function (response) {
// handle response
console.log('response: ', response);
db.getSession(function (err, response) {
if (err) {
// network error
console.log('session error: ', error);
} else if (!response.userCtx.name) {
// nobody's logged in
console.log('nobody is logged in' );
} else {
// response.userCtx.name is the current user
console.log('response.userCtx.name: ', response.userCtx.name );
commit('setUser', response.userCtx.name)
router.push({ name: 'dashboard'})
}
});
commit('setUser', payload.user)
}).catch(function (error) {
// handle error
console.log('error: ', error);
dispatch('logOut');
});
}
There is a mix of callbacks and promises in your code. It is possible that db.login is not completing before the db.session call. I am not sure from the snippet if the "then" is part of another Promise, but here is how I would structure the call:
setUser({commit, dispatch},payload){
console.log('store action payload: ', payload);
var user = payload.user;
var pass = payload.pass
db.logIn(user, pass).then(function (response) {
// handle response
console.log('response: ', response);
return db.getSession();
}).then(response) {
//check user details from the session response here
commit('setUser', payload.user)
}).catch(function (error) {
// handle error
console.log('error: ', error);
dispatch('logOut');
});
}
(this is an example - there may be typos!)

strong-soap: call service method over https

I am trying to call a SOAP service using the strong-soap node.js library. Although I did set the client security to ClientSSLSecurity, I am getting an error when invoking a method on the client:
TypeError [ERR_INVALID_PROTOCOL]: Protocol "http:" not supported. Expected "https:"
How can I tell stong-soap to use https?
Here is my code so far:
"use strict";
var soap = require('strong-soap').soap;
var constants = require('constants');
var url = 'http://www.caqh.org/SOAP/WSDL/CORERule2.2.0.wsdl';
var WSDL = soap.WSDL;
var options = {};
WSDL.open(url,options,
function(err, wsdl) {
if (err) {
console.log(err);
return;
}
var clientOptions = {
WSDL_CACHE : {
caqhcorewsdl: wsdl
}
};
soap.createClient('caqhcorewsdl', clientOptions, function(err, client) {
if (err) {
console.log(err);
return;
}
else {
client.setSecurity(new soap.ClientSSLSecurity(
'rob.keystore'
, 'cert.pem'
, {
strictSSL: true,
secureOptions: constants.SSL_OP_NO_TLSv1_2
}
));
if(err) {
console.error(err);
return;
}
else {
console.log('success!');
client.RealTimeTransaction({name: 'value'}, function(err, result, envelope, soapHeader) {
if(err) {
// ERROR IS THROWN HERE:
console.error(err);
return;
}
else {
console.log('success!');
}
});
}
}
});
});
Thanks!
Rob G
replace the following line with this one:
var url = 'https://www.caqh.org/SOAP/WSDL/CORERule2.2.0.wsdl';

How to repeat action with query, in case of authentication issues?

I have get menu action with authentication.
async getMenu({rootState, commit}) {
try {
const { auth, lang } = rootState;
const {items} = await this.$axios.$get(`/api/${ lang.locale }/menu`, {
headers: {
'Authorization': `bearer ${auth.token}`,
'Accept-Language': `${lang.locale}`
},
});
if (items) {
// set items
commit('setMenu', items);
}
} catch (error) {
console.log({Error: error})
}
}
In case of error, I submit request with refresh token
$axios.onError(async (error) => {
const code = parseInt(error.response && error.response.status);
const message = error.response && error.response.data && error.response.data.error;
if (code === 403) {
await store.dispatch(
'auth/refreshToken',
{ refreshToken: store.state.auth.refreshToken },
{ root: true }
);
How to repeat action after get token ?
Use
$axios.defaults.validateStatus = (status) => {
return [200, 402].indexOf(status) !== -1;
};
$axios.onResponse(async (response) => {...}

No error shown in console when thrown from inside hapi plugin

For some reason no error shows up in the server console when I start my hapi server with nodemon and navigate to http://localhost:3000/hapi-ext-fetch and this makes debugging very difficult. Here is my code:
var Hapi = require('hapi');
var Joi = require('joi');
var fetch = require('isomorphic-fetch');
var debugMode = { debug: { request: [ 'error', 'request-internal' ] }};
var server = new Hapi.Server(debugMode);
server.connection({ port: 3000 });
var myPlugin = {
register: function (server, options, next) {
server.route([
{
method: 'GET',
path: '/{name}',
handler: function ( request, reply ) {
throw new Error('this error isnt shown!');
},
config: {
validate: {
params: {
name: Joi.string().min(3).max(10)
}
}
}
}
]);
next();
}
};
myPlugin.register.attributes = {
name: 'myPlugin',
version: '1.0.0'
};
server.register([
{
register: myPlugin,
routes: {
prefix: '/test'
}
}
], function() {
server.ext( 'onPreResponse', ( request, reply ) => {
if ( typeof request.response.statusCode !== 'undefined' ) {
return reply.continue();
}
fetch('http://localhost:3000/test/whatever')
.then(function(result) {
reply(result);
})
.catch(function(err) {
reply('error on server side: ' + err.stack);
});
});
server.start((err) => {
if (err) {
throw err;
}
console.log('Server running at:', server.info.uri);
});
});
I'm using hapi 13.0.0
Can't say I totally understand your use case here and if this question will be helpful to other people. But what you're trying to do it seems is:
Send a request to /hapi-fetch-ext
Have that request 404
And then in an onPreResponse go fetch another route /test/whatever
Hope to see the "this error isn't shown error"
Not sure if you're aware but this is going to cause an infinite cycle of requests (your fetch will cause another onPreResponse and so on and so on). So you should probably only go fetch on a 404:
server.ext( 'onPreResponse', ( request, reply ) => {
if (request.response.isBoom && request.response.output.statusCode === 404) {
return fetch('http://localhost:3000/test/whatever')
.then(function(result) {
reply(result);
})
.catch(function(err) {
reply('error on server side: ' + err.stack);
});
}
return reply.continue();
});

How to check unwrapError

var users = m.request({
method: "GET",
url: "hoge.json",
unwrapSuccess: function(response) {
return response;
},
unwrapError: function(response) {
//return response.error;
return "404 error";
}
});
users.then(function(result) {
console.log(result);
});
After delete "hoge.json".
I want to catch "404 error",but
uncaught SyntaxError: Unexpected token <
2016/2/18 add
I want to test alert ("unwrapError");
Below code is always alert ("unwrapSuccess");
How to change below code?
What is the unwrapError?
▼js
var users = m.request({
method: "GET",
url: "hoge.json",
unwrapSuccess: function(response) {
alert ("unwrapSuccess");
return response;
},
unwrapError: function(response) {
alert ("unwrapError");
return "error";
}
});
users.then(function(result) {
console.log(result);
});
▼hoge.json
[{"name": "John"}, {"name": "Mary"}]
If you take a look at mithril's source code you will see that m.request is just a wrapper for the XMLHttpRequest API. And that's what happens when the request's readyState attribute changes:
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
options.onload({type: "load", target: xhr})
} else {
options.onerror({type: "error", target: xhr})
}
}
}
So mithril's unwrapError callback will be called whenever the response status is not a 2xx.
I updated the fiddle calling a URL that returns a 500 response and now the unwrapError is called.