Custom Not Found Page (404)
A dedicated not found view is created to handle unmatched routes. The router is updated to catch all undefined paths and direct them to a controller that renders this page, providing users with a clear message and a link back to the home page.
Internal Server Error Page (500)
An internal error view is implemented to handle unexpected failures. Controllers are updated to render or redirect to this page when critical errors occur, ensuring users do not see raw or blank error responses.
Router and Controller Updates
Routes are extended to include handlers for 404 and 500 errors. Controllers are refactored to consistently return appropriate responses (render or redirect), including handling special cases such as SSE redirects and admin routes.
Admin and Validation Error Handling
Admin routes are updated to use redirects with proper HTTP status codes and flash messages for error feedback. Validation and processing errors are handled uniformly to prevent inconsistent behavior across endpoints.
Logging Strategy in MVC Architecture
Logging is centralized in the controller layer, where errors are handled. Each log entry includes contextual information and the original error to maintain a clear error chain while avoiding excessive noise.
Production Readiness
With structured error pages and a consistent logging approach in place, the application is considered ready for deployment. The next step involves server setup, binary deployment, and Docker configuration.
We are so close to being ready to ship this live on our server and actually share this blog with people. But we need two things. We need a page that shows if we can find whatever got typed into the URL bar. And we also need an error page. And the first thing we're going to tell this, if I go up here and do something like this, we just get message not found. To deal with that,
We're going to add a new page here called, let's say, notfound.Timple, package, views, and we can go in, let's just take what we have from home and say, let's just say, not found, remove all of this, not found.
not even deal with all the metadata. It doesn't really matter here. We're going to stay with all of this. And then we're going to say, just say, could not find what we could not find. We could not find what you are looking for.
Page one here. And then say click and then an anchor tag. Let's go to just go to routes. Routes.Home page. Say here. Close the anchor tag to go back
Not the most creative one, but we don't really need more than this. Yes. So we just say, hey, we don't know what you're looking for. Click here to go back home. Should we maybe just actually instead use the link components. Come on, I can hit the keys right now.
No, I should be able to do link. Why can I not do link? Link. All right, we have only the button link one, that's right. So we do button link. Let's say here, oh, the href is
home page and title is gonna be here. Get rid of all of this. Give this a save. Okay, we should be able to do this. I cannot wrap it. If I run into a limitation of Timbal,
Okay, what if I just have a span? Mm-hmm, interesting. Okay, I don't care. Let's just say, click the button below to go back home. Yeah, button link.
route's home page. And then home. Close that. Let's say we want to have class text to Excel and class text base content. Give that a save. And then we need to go into our router. And then here at the bottom, we're going to say
E route not found. And then we say everything after slash that has not been matched at this point because again the order here matters. Then we're gonna say, let's say controller and not found. And then we of course need to go in here and create, where should we create it? Let's create it after home. So we just grab
home controller say not found and we are simply going to be returning views not found and call the function right now if I refresh we have the page but it's not looking that good so let's go back to
our view here and say this should also have text base content and we should be able to say flex call an items center. Okay, almost justify center. Yeah, I think
But this is perfectly fine. Feel free to go in and modify it if you want. But this, at least, gives the user, if they land on a page that doesn't exist, some idea of what's happening. We don't get the margin bottom. OK, that's fine. I think this is good. Right. And if we click Home, we go to the Home page.
Perfect. Next, we need to be able to turn something from the wrong page, an internal error kind of page. So whatever this error we have just been ignoring so far happens. We at least drove something to use like, hey, something went wrong. Please try again. Right. So in views, you can grab. Let's just take all of this and create a new file calls, internal error page, internal error. Yeah, I think that's good.
Paste all this say internal error. Then let's say something went wrong in the title and here say something went wrong that we could not recover from. Click the button below to go and not home but go back and then say
back and then let's just accept a route here. Like this, get rid of this. And now we can go into our controllers, controllers, and start to implement this. So again, if we have an error here,
We actually already have some error handling. Let's get rid of that and deal with that in a second. So we say views, we say render, easy, and then we say views, internal error. And if we have an error here, let's just return them back to home page because this is an error if we can't connect to the database. So we could technically send them into a loop here, but this is also one of the best effort of what we can do.
So what are we going to be doing here? We also have this one. So this is the load article page. Let's again just return them to the home page. Let's return them to the home page. Which can we even do that here? No, because we are using SSE. So we need to say SSE redirects. All right.
redirect I don't believe we can let me just test to be sure because we should consume it right so if we click here yeah we are not doing anything so let's instead say SSE and then redirect to
let's say, routes, internal in, internal error. And then, yes, so we technically need to create an error here, a controller for this page as well. Let me just add the routes. Let's say, a internal error. And let's say that this goes to slash 500.
100, internal error. Yeah, let's do internal error. All right, and then create a new controller. Internal error, equal context. And just return render.
easy and then views, internal error, and we just gonna route them back to home page. Good, and then we go into router, and say routes, internal error, internal error. Let's rename this to error as well.
Come on, let me exit out of this. There we go. Now, if I go in here and refresh and go to the next page, now we get the internal error. If something goes wrong, we can click back. So that at least mitigates that we don't show the blank error page to the user. Right. Same thing down here. And finally, this is also what we want.
Right. We want to, again, here, if we can't find the article, let's send them back to the homepage. Yes. Style. We're not going to be dealing with it because no users should technically go in and look for this. So that is fine. Also JavaScript, also JavaScript files. Robots.
Let's not deal with that either, and the same for site map. For the admin page, we also have an error here. For the admin page, we should probably begin by simply saying we want to, where do I go?
this one, so we're gonna be going to admin page, and then I need a cookies flash, so we're gonna say flash error, internal server error, plus, then we're gonna have the logs. So this should be fine, and if we error here, we error here, that's not more we can really do.
Let's also do it here. And that we are simply gonna be repeating. Throughout all of these, validate. Yes, same logic. Right. Right, yes. And then down here.
Here we are gonna do the SSE redirect that we have here. Yes, that's what we want. That's fine. Let's go up and grab again here for the create. Again, if we have an error here that is not
I'm going to turn this right. We already have a flash. Yes. Now it's looking good. And again, we want to grab the redirect that we have for SSE here and return it.
Yes. And at this point, if we can add the flash, let's just render the internal. Yes. Fine. So we're just going through and providing some information to ourselves that this is not the
most important part, but it's at least nice that we have some clue if something goes wrong. Again, we want to render the admin page here. Same thing with here. Yes. Or do we actually want to redirect? Am I doing this wrong? It should probably redirect. I'm doing this wrong.
Let's go in here and say easy redirect. And we're gonna be HTTP status. What is it? I can never remember what we should use status. See other. Right. So let's fix this here.
We're going to return to an internal server error. Because if we can't show the flash, then we probably cannot do much. Probably do it here as well. Yes. And this one should be like no.
The leading too much. No. Easy redirect is gonna be here. And HTTP status, see other. And the route is gonna be the admin page. Turn on our admin page with the flash. Yes, let me grab this and undo.
We need this instead. Yes. Now we are starting to get somewhere. I want the same thing here. And also here. All of the other things is correct. Here we want to redirect to
I'll do this, this, this, and this. All right. Let's look at the other ones. Validate should probably be the same here. We redirect. Yes, and here the same thing, and yep. Okay, edit, same logic.
Same logic. Right, and also in here. And in here. Right, we have, I think, this is the lockout.
redirect to home page and the same here. All right. Yes, I like it. And then in the log in home page, correct. Remove this.
redirect to this, and redirect again here to this, and redirect again to this. And if it's not valid, yes, and yes, and... All right, there we go. I think we got all of them.
We try to provide some message to ourselves, to our users. To users, they're only gonna see the internal error on the found page. We can see our internal error page and also with a flash if it's possible. So I think we've covered all grounds now. Let's go through and then add some error logging and talk about how you should think about logging. All right, so where do we add, wanna add logging? In general, we wanna add logging where something is
handled for our use case that is most often gonna be in the controller layer. It's really important that you start to not to add too much locking because you get too much noise in the output and you can't really figure out what is happening. And we're typically just adding on to the error so we can see the error chain whenever we actually lock something out. So, where do we want to start locking?
Well, we can see here, this is probably a good place to log, so we're gonna say log error, and we're gonna add context, request, and then context, I'm gonna say, could not, and then say, could not call, and then the method, then we write the error, and then finally the actual error that was returned to us.
Because whenever we go through, we're not going to be logging down in model there because it's just going to return the error up and then log it here. So this for model view controller architecture, this is more than plenty. We might have some server layers that does a lot of different things where we want to log down in that layer if we are doing more stuff down there or if we're handling the error. But everything that we do here, we're handling the error in the controller layer right now by just returning the internal error page.
So this is the correct place for all of our logs to be handled right now, and for probably most of the errors that you're going to be logging out, it's going to be in the controller layer. So this is a good mental model, try to do it, where the error is handled. For example, here, we actually need to, yes, we have an internal server error more than we forgot, so let me just
So again, here could not call string convert. This should technically not really happen, but it could happen. So we're going to do our internal error. We could technically argue to do or not found, but I think for most cases, this is going to be perfect. So let's just go through all of the errors. We can do it here as well.
And technically, we should also turn our error here. Right. Could not call. And do it like this. And then we simply just say, could not.
I need to say could not, call it like this, do it the same thing down here, but let's say this and be consistent here and say models, right? This is what we did up here, right? Do we call all grounds, could not call
Yes, yes, and yep, here we want to say it could not call models fine slug by UR, could not find article by slug. This could technically also be a not found error. I actually think this is better here, so we're gonna say not found and go back to the,
go back to the, yeah, so in here we just go back to the home page. And this could technically also be an info. I think we're just gonna keep it simple and sticking with the error logs for now. This one would be nice to know that we could not, could not call all of,
And let's grab this in small quotation marks. Same thing down here. So we at least have some, could not call. And then we can just grab this and say, could not call as defined. And then let's do this and say, Sprint.
and file name. Yes. Let's do it down here as well. And we don't need this split here. We just need to say we cannot call all of this.
Right, could not call, yes. Like this, I think we are almost through now. Right, could not, find published articles. Because this will also indicate our database error.
I could not call. Right, so this is all of the main routes that we need, and we need to continue this process. I'm not gonna show it because I'm just repeating myself for the admin routes, but we are basically trying to tell ourselves that something went wrong, give ourselves some context, and then the error had been wrong, so we know where to go in to look. So we're trying to provide ourselves just enough context that we can actually
fix the error or identify where something went wrong and then investigate further. So this is the basics of error pages and a logging strategy. We can now reasonably figure out if something goes wrong, especially on the more important part, which is the user facing pages. We will get some indication if something goes wrong and what went wrong. But this is it. We are basically ready now to go live and put this in production. So in the next
Part three, we're going to be setting our server. We're going to be deploying a binary. We're then going to be also trying to deploy a Docker file and deal with everything in between these processes. So fix the last of the locking strategy if you want to, but if not, let's hit into part three and actually get this thing deployed.