• No results found

Increased Flexibility: GET Params

In document Learning Node js pdf (Page 94-98)

On the response side, you have already used two methods: writeHead and end . You must call end on the response object once and only once for each incoming request. Otherwise, the client never gets the response and continues to listen on the connection for more data. When you are writing your responses, you should take care to make sure you think about your HTTP response codes (see the sidebar “HTTP Response Codes”). Part of writing your servers includes thinking logically about what you are trying to communicate to the calling clients and sending them as much information as possible to help them understand your response.

HTTP Response Codes

The HTTP specification contains a large number of response codes a server can return to call- ing clients. You can learn more about them on Wikipedia ( http://en.wikipedia.org/wiki/List_of_ HTTP_status_codes ).

Although a dizzying number of response codes is possible, you’ll find yourself using a few of the more common responses in most of your applications:

200 OK—Everything went fine.

301 Moved Permanently—The requested URL has been moved, and the client should re- request it at the URL specified in the response.

400 Bad Request—The format of the client’s request is invalid and needs to be fixed. ■ 401 Unauthorized—The client has asked for something it does not have permission to

view. It should try again authenticating the request first.

403 Forbidden—For whatever reason, the server is refusing to process this request. This is not the same as 401, where the client can try again with authentication.

404 Not Found—The client has asked for something that does not exist.

500 Internal Server Error—Something happened resulting in the server being unable to process the request. You typically use this error for situations in which you know the code has entered some sort of inconsistent or buggy state and needs developer attention.

503 Service Unavailable—This indicates some sort of runtime failure, such as temporar- ily low on memory or having troubles with network resources. It’s still a fatal error like 500, but it does suggest the client could try again in a while.

These are the codes you will use the most, but there are many others that you are encouraged to browse over. If you’re unsure when to use one, look at code that others have written and see how they do it. The correct code for any given situation can be cause for great debate, but you can usually get the right ones without too much trouble.

Increased Flexibility: GET Params

When you start adding a lot of photos to your albums, you will have too many photos to display efficiently on one “page” of the application, so you should add paging functionality to

it. Clients should be able to say how many photos they want and what page they want, like this:

curl -X GET 'http://localhost:8080/albums/italy2012.json?page=1&page_size=20' If you’re not familiar with the terminology, the bolded part of the preceding URL is the

query string , commonly just referred to as the GET params for the request. If you run this curl command with the previous version of the program, you’ll probably notice that it doesn’t quite work any more. If you add the following to the beginning of handle_incoming_request , you can see why:

console.log(req.url); The URL now looks like this:

/albums/italy2012.json ?page=1&page_size=20

The code is looking for the .json at the end of the string, not buried in the middle of it. To fix the code to handle paging, you have to do three things:

1. Modify the handle_incoming_request function to parse the URL properly.

2. Parse the query string and get the values for page and page_size .

3. Modify the load_album function to support these parameters.

You are fortunate in that you can do the first two in one fell swoop. If you add the url module that Node ships with, you can then use the url.parse function to extract both the core URL pathname and the query parameters. The url.parse function helps a little bit further in that you can add a second parameter, true , which instructs it to parse the query string and gener- ate an object with the GET parameters in it. If you print out the results of url.parse on the preceding URL, you should see

{ search: '?page=1&page_size=20', query: { page: '1', page_size: '20' }, pathname: '/albums/italy2012.json',

path: '/albums/italy2012.json?page=1&page_size=20', href: '/albums/italy2012.json?page=1&page_size=20' }

Now you can modify the handle_incoming_request function to parse the URL and store it back on the request object in parsed_ur l. The function now looks like this:

function handle_incoming_request(req, res) { req.parsed_url = url.parse(req.url, true); var core_url = req.parsed_url.pathname;

// test this fixed url to see what they're asking for if ( core_url == '/albums.json') {

81 Increased Flexibility: GET Params

} else if ( core_url.substr(0, 7) == '/albums'

&& core_url.substr( core_url.length - 5) == '.json') { handle_get_album(req, res);

} else {

send_failure(res, 404, invalid_resource()); }

}

For the last part, you modify the handle_get_album function to look for the page and page_ num query parameters. You can set some reasonable default values for them when the incoming values are not provided or are not valid values. (The servers should always assume that incom- ing values are dangerous or nonsensical and check them carefully.)

function handle_get_album(req, res) { // get the GET params

var getp = req.parsed_url.query;

var page_num = getp.page ? getp.page : 0;

var page_size = getp.page_size ? getp.page_size : 1000; if (isNaN(parseInt(page_num))) page_num = 0;

if (isNaN(parseInt(page_size))) page_size = 1000;

// format of request is /albums/album_name.json var core_url = req.parsed_url.pathname;

var album_name = core_url.substr(7, core_url.length - 12); load_album(

album_name, page_num, page_size,

function (err, album_contents) {

if (err && err.error == "no_such_album") { send_failure(res, 404, err);

} else if (err) {

send_failure(res, 500, err); } else {

send_success(res, { album_photos: album_contents }); }

} ); }

Finally, you modify the load_album function to extract the subarray of the files_only array when it’s done with all its work:

function load_album(album_name, page , page_size , callback) { fs.readdir(

"albums/" + album_name, function (err, files) { if (err) {

if (err.code == "ENOENT") { callback(no_such_album()); } else {

callback({ error: "file_error",

message: JSON.stringify(err) }); }

return; }

var only_files = [];

var path = "albums/" + album_name + "/"; (function iterator(index) {

if (index == files.length) { var ps;

// slice fails gracefully if params are out of range ps = only_files.splice(page * page_size, page_size); var obj = { short_name: album_name,

photos: ps }; callback(null, obj); return; } fs.stat( path + files[index], function (err, stats) { if (err) {

callback({ error: "file_error",

message: JSON.stringify(err) }); return;

}

if (stats.isFile()) {

var obj = { filename: files[index], desc: files[index] }; only_files.push(obj); } iterator(index + 1) } ); })(0); } ); }

83

In document Learning Node js pdf (Page 94-98)

Related documents