1. Trang chủ >
  2. Công Nghệ Thông Tin >
  3. Kỹ thuật lập trình >

Section 3.16: Using middleware and the next callback

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



Xem Thêm
Tải bản đầy đủ (.pdf) (334 trang)

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×