Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (2.79 MB, 334 trang )
single routes across multiple handlers. Calling next() with no arguments tells express to continue to the next
matching middleware or route handler. Calling next(err) with an error will trigger any error handler middleware.
Calling next('route') will bypass any subsequent middleware on the current route and jump to the next matching
route. This allows domain logic to be decoupled into reusable components that are self-contained, simpler to test,
and easier to maintain and change.
Multiple matching routes
Requests to /api/foo or to /api/bar will run the initial handler to look up the member and then pass control to the
actual handler for each route.
app.get('/api', function(req, res, next) {
// Both /api/foo and /api/bar will run this
lookupMember(function(err, member) {
if (err) return next(err);
req.member = member;
next();
});
});
app.get('/api/foo', function(req, res, next) {
// Only /api/foo will run this
doSomethingWithMember(req.member);
});
app.get('/api/bar', function(req, res, next) {
// Only /api/bar will run this
doSomethingDifferentWithMember(req.member);
});
Error handler
Error handlers are middleware with the signature function(err, req, res, next). They could be set up per route
(e.g. app.get('/foo', function(err, req, res, next)) but typically, a single error handler that renders an error
page is sufficient.
app.get('/foo', function(req, res, next) {
doSomethingAsync(function(err, data) {
if (err) return next(err);
renderPage(data);
});
});
// In the case that doSomethingAsync return an error, this special
// error handler middleware will be called with the error as the
// first parameter.
app.use(function(err, req, res, next) {
renderErrorPage(err);
});
Middleware
Each of the functions above is actually a middleware function that is run whenever a request matches the route
defined, but any number of middleware functions can be defined on a single route. This allows middleware to be
defined in separate files and common logic to be reused across multiple routes.
app.get('/bananas', function(req, res, next) {
getMember(function(err, member) {
GoalKicker.com – Node.js Notes for Professionals
41
if (err) return next(err);
// If there's no member, don't try to look
// up data. Just go render the page now.
if (!member) return next('route');
// Otherwise, call the next middleware and fetch
// the member's data.
req.member = member;
next();
});
}, function(req, res, next) {
getMemberData(req.member, function(err, data) {
if (err) return next(err);
// If this member has no data, don't bother
// parsing it. Just go render the page now.
if (!data) return next('route');
// Otherwise, call the next middleware and parse
// the member's data. THEN render the page.
req.member.data = data;
next();
});
}, function(req, res, next) {
req.member.parsedData = parseMemberData(req.member.data);
next();
});
app.get('/bananas', function(req, res, next) {
renderBananas(req.member);
});
In this example, each middleware function would be either in it's own file or in a variable elsewhere in the file so
that it could be reused in other routes.
Section 3.17: Error handling
Basic docs can be found here
app.get('/path/:id(\\d+)', function (req, res, next) { // please note: "next" is passed
if (req.params.id == 0) // validate param
return next(new Error('Id is 0')); // go to first Error handler, see below
// Catch error on sync operation
var data;
try {
data = JSON.parse('/file.json');
} catch (err) {
return next(err);
}
// If some critical error then stop application
if (!data)
throw new Error('Smth wrong');
// If you need send extra info to Error handler
// then send custom error (see Appendix B)
if (smth)
next(new MyError('smth wrong', arg1, arg2))
// Finish request by res.render or res.end
res.status(200).end('OK');
});
GoalKicker.com – Node.js Notes for Professionals
42
// Be sure: order of app.use have matter
// Error handler
app.use(function(err, req, res, next)) {
if (smth-check, e.g. req.url != 'POST')
return next(err); // go-to Error handler 2.
console.log(req.url, err.message);
if (req.xhr) // if req via ajax then send json else render error-page
res.json(err);
else
res.render('error.html', {error: err.message});
});
// Error handler 2
app.use(function(err, req, res, next)) {
// do smth here e.g. check that error is MyError
if (err instanceof MyError) {
console.log(err.message, err.arg1, err.arg2);
}
...
res.end();
});
Appendix A
// "In Express, 404 responses are not the result of an error,
// so the error-handler middleware will not capture them."
// You can change it.
app.use(function(req, res, next) {
next(new Error(404));
});
Appendix B
// How to define custom error
var util = require('util');
...
function MyError(message, arg1, arg2) {
this.message = message;
this.arg1 = arg1;
this.arg2 = arg2;
Error.captureStackTrace(this, MyError);
}
util.inherits(MyError, Error);
MyError.prototype.name = 'MyError';
Section 3.18: Handling POST Requests
Just like you handle get requests in Express with app.get method, you can use app.post method to handle post
requests.
But before you can handle POST requests, you will need to use the body-parser middleware. It simply parses the
body of POST, PUT, DELETE and other requests.
Body-Parser middleware parses the body of the request and turns it into an object available in req.body
var bodyParser = require('body-parser');
GoalKicker.com – Node.js Notes for Professionals
43
const express = require('express');
const app = express();
// Parses the body for POST, PUT, DELETE, etc.
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.post('/post-data-here', function(req, res, next){
console.log(req.body); // req.body contains the parsed body of the request.
});
app.listen(8080, 'localhost');
GoalKicker.com – Node.js Notes for Professionals
44
Chapter 4: Filesystem I/O
Section 4.1: Asynchronously Read from Files
Use the filesystem module for all file operations:
const fs = require('fs');
With Encoding
In this example, read hello.txt from the directory /tmp. This operation will be completed in the background and
the callback occurs on completion or failure:
fs.readFile('/tmp/hello.txt', { encoding: 'utf8' }, (err, content) => {
// If an error occurred, output it and return
if(err) return console.error(err);
// No error occurred, content is a string
console.log(content);
});
Without Encoding
Read the binary file binary.txt from the current directory, asynchronously in the background. Note that we do not
set the 'encoding' option - this prevents Node.js from decoding the contents into a string:
fs.readFile('binary', (err, binaryContent) => {
// If an error occurred, output it and return
if(err) return console.error(err);
// No error occurred, content is a Buffer, output it in
// hexadecimal representation.
console.log(content.toString('hex'));
});
Relative paths
Keep in mind that, in general case, your script could be run with an arbitrary current working directory. To address
a file relative to the current script, use __dirname or __filename:
fs.readFile(path.resolve(__dirname, 'someFile'), (err, binaryContent) => {
//Rest of Function
}
Section 4.2: Listing Directory Contents with readdir or
readdirSync
const fs = require('fs');
// Read the contents of the directory /usr/local/bin asynchronously.
// The callback will be invoked once the operation has either completed
// or failed.
fs.readdir('/usr/local/bin', (err, files) => {
// On error, show it and return
GoalKicker.com – Node.js Notes for Professionals
45
if(err) return console.error(err);
// files is an array containing the names of all entries
// in the directory, excluding '.' (the directory itself)
// and '..' (the parent directory).
// Display directory entries
console.log(files.join(' '));
});
A synchronous variant is available as readdirSync which blocks the main thread and therefore prevents execution
of asynchronous code at the same time. Most developers avoid synchronous IO functions in order to improve
performance.
let files;
try {
files = fs.readdirSync('/var/tmp');
} catch(err) {
// An error occurred
console.error(err);
}
Using a generator
const fs = require('fs');
// Iterate through all items obtained via
// 'yield' statements
// A callback is passed to the generator function because it is required by
// the 'readdir' method
function run(gen) {
var iter = gen((err, data) => {
if (err) { iter.throw(err); }
return iter.next(data);
});
iter.next();
}
const dirPath = '/usr/local/bin';
// Execute the generator function
run(function* (resume) {
// Emit the list of files in the directory from the generator
var contents = yield fs.readdir(dirPath, resume);
console.log(contents);
});
Section 4.3: Copying files by piping streams
This program copies a file using readable and a writable stream with the pipe() function provided by the stream
class
// require the file system module
var fs = require('fs');
/*
GoalKicker.com – Node.js Notes for Professionals
46