Take your routing skills to the next level and learn to recover from errors, initiate downloads, and go beyond simple request and response. When we get a route that does not match we don't want the page to just error out. With
.redirect we can send the user to an error page and display a helpful message.
We will go on to see how to download a file with
.download and track behavior with
We've got a decent little app here that lets us view users, but we're not actually verifying any of these users. So if we put in an invalid name here, we're going to get an error that spits out to the browser and the console, because that user doesn't exist. Let's take a look at how we can verify that.
We'll start by creating a function called verifyUser that will just be a regular route handler function like our others, pass in the request and the response objects, and then we're actually going to use this getuser file path function that we've declared previously, and so we're going to get our username parameter off of the request object, pass it in to that function, and then that will give us the full path to the JSON file.
What we're going to do then is check to see if that file actually exists. If there's no matching file then we've got an invalid user. So fs.exists will call back to this function, and basically just passes in a Boolean flag or whether or not the file exists. If yes is true, then we're going to call next, and to get this working with what we actually want it to call next, we're going to add this an additional handler for the get route. We can ignore these errors for now.
You can pass in multiple handlers, or you could pass in an array of handlers, or a mix of handlers and arrays, but so we're saying when you get this route matches, call verifyuser, and also call this other one as long as next passes it to us. Now what do we do if the file doesn't exist and we have an invalid user? Well, we're actually going to just call next again, but in this case we're going to pass in the string "route."
What this tells us, tells Express, is to bail out and skip this second handler, and actually move on to the next route definition that matches our URL. All we're really matching with this /:username is a single string path, and so /:foo is the same thing, and if it gets to this one, we're just going to send back some text that says, "Whoops," so we know we got to this.
If we go over here, this is a valid user so we get our page like we would expect, but if we change this we get "Whoops" because that file didn't exist, next route was called, so it passed on to that handler. Now rather than just passing along to the next random route handler, let's actually use the redirect method of the response object. If we call response.redirect and then give it a new URL, in this case we'll do /error and then append our username on there, we can actually redirect to a new URL.
We'll go and we'll get rid of this foo handler and replace it with our /error/username handler and then we can actually provide somewhat useful message to the user saying that no user with that name could be found. Now when we try and load an invalid user, we get redirected to our error URL, and we get that slightly more helpful error message.
The response object has another capability that maybe we really want to communicate that this was a missing page, and so we can actually just say response.status404.send and then that will actually send back that response as you can see here in the inspector, with a 404 HTTP status. So we saw redirect, but the response object that you get in your handlers actually has some other really handy methods on it as well. One of them being a download method.
So you can actually trigger a file download extremely easily in Express by calling response.download. What we're going to do here is define a route that is a string pattern, sort of a halfway point between a regular route like we've been using, and the regex routes that we saw in a previous lesson. This is going to say any route that ends in .json use this handler.
Any time a route that ends in .json is detected, we're actually just going to trigger a download, and we're going to create the path to that corresponding JSON file by appending the path to the users directory which is where our files are. If we clear out this list, go to our user here, and then add .json, we will actually get a file downloaded for us in the browser. You can see right there, it's got the name of the actual file.
If you want to provide a custom file name, maybe something helpful, or funny, or otherwise, you can pass that as the second parameter to response.download. Now if we go back and try that again, you can see, "Oh my gosh, we've downloaded a virus." One more useful method on the response object that I wanted to cover was the response.json object. If you're building something that's more along the lines of an API server, things like this can be really helpful because you can just pass back your data.
Now we're going to send it back out by saying response.json and passing it that object. If we actually come in here to the browser, put this data path in here, you'll see that we can actually send back that chunk of JSON representing that user, so like I said, that's extremely useful if you want to create an API server. Now we've got a handful of handlers here for our username route, and I wanted to show one more method that you can define which is the all method.
This is going to be called for any of the HTTP methods that match this route. So if we define this that just logs out what we're seeing, and then go use our app, you can see that we're getting logs for which user was accessed, and what type of HTTP verb was used.