Okay.
In this section we're going to take a look at error handling.
And to help us do this.
First of all we're going to set a few things up.
We're going to create a controller that's going to have some endpoints designed just to return HTTP
error responses.
And the idea of this is so that we can set up something on the client side, so that when we do get
an error response from our API, we handle it centrally in a very specific way for each different type
of error that we expect to get back from our API server.
So first of all, we're going to deal with this in the API.
So we're going to go back to our Solution Explorer view inside the API project and inside the controllers.
What we've currently got is we've got two controllers.
One was a demo controller that was supplied when we created the template, but they've got some features
in common with each other.
We've got a controller base that they all inherit from, and this is the base class for an MVC controller
without view support.
We're just having API endpoints so they receive HTTP requests and they return HTTP responses.
They don't return HTML, which is what would be considered a view in MVC.
The old model view controller terminology.
So we use this when we've got an API controller.
But this is the same code.
And we've got our attributes here for the routing and the API controller.
But it's pretty much the same code that we have in our products controller as well.
Also deriving from the controller base.
And we've got our two attributes inside here.
So you'll often hear the terminology dry in coding.
Do not repeat yourself.
And we do make the effort where we can to reduce repetitive code and make things more reusable.
So a quick and easy example is we're going to create a base API controller that has these attributes.
And let's deriving from controller base to save us from repeating that in each controller that we create.
So we're going to create, first of all, before we get started on error handling, we do have a template
for an API controller.
So we can use this one.
And I'm going to call this base API controller and press return.
And this completes the boilerplate that we need for a typical API controller anyway.
And in fact this is exactly how we want it.
Apart from the import layer or the using statement that we don't need inside this controller.
So the idea of this, they're all going to have a route of API forward slash and then a square brackets
controller with the API controller attributes and deriving from controller base.
And then we can just derive from our base API controller in our API controllers.
And they're automatically going to have these attributes.
It's going to have that route.
And it's going to be deriving from the correct class as well.
So back to the explorer view.
And we'll just update the weather forecast controller.
First of all we'll take out the API controller attribute and instead of deriving from controller base,
we can just derive from our base API controller and we'll have the same functionality with one small
difference.
Of course, instead of going to localhost 5001 Weather forecast, we would now need to go to API forward
slash weather forecast because of the route configuration we have inside here, and we'll just do the
same for the products controller as well.
We will remove the routing attributes and we will derive from the base API controller.
And let's just remove the unnecessary using statement inside here.
Now that I have seen it, we can use the quick fix here just to remove the unnecessary usings.
It doesn't do any harm, but obviously if you do see something that's not needed, you can just go ahead
and remove it.
It's not as noisy as our react application where we've got an import we're not using.
That's very noisy.
C-sharp is a bit more subtle, and it just sort of dims it out in the code.
So now we have that in place, let's go ahead and create a new controller and I'll say new file.
And we'll just make this a class.
We won't use the API controller because we don't need the API controller attribute or the route when
we're deriving from the base API controller.
So this is going to be a class and I'm going to call it buggy controller.
It's just going to have endpoints that return errors.
Inside here we're going to derive from the base API controller as mentioned.
And we're going to create some endpoints.
And this is going to be an HTTP.
Get this first one and we'll give it a route parameter of notfound.
This is going to be an example of a request where we don't find what we're looking for.
So I'm going to make it public.
And if we're not returning a specific type then we don't need to use Actionresult.
We can actually use a different version of Actionresult, the Actionresult result that doesn't return
a specific type, but we use it when we want to return an HTTP response, for instance, and we don't
want to supply a type.
And I'm just going to give this method a name of get Notfound.
And inside here we will return literally not found.
The goal of this is just to return error responses.
And then on our client side, just as an example, what we'll do when we do get a not found returned
from our API, we'll redirect them to a client side not found page.
So if they try to fetch a product that didn't exist in our database, we would return a not found from
our API.
And then on the client side, we'd redirect them to a not found page and say, hey, we couldn't find
what you were looking for there, so I'm just going to copy this down and paste it below, and we'll
have another endpoint.
And this one is going to be for a bad request and we'll specify get bad request.
So on occasions, if there's a client side error where they've done something that's considered a client
side error, but it happens on our API server, then we would return, for instance, a bad request
from our API controller endpoint.
And I'll just say this is not a good request as the error message that we'll return with this one.
So this is a 400 HTTP response.
A 400 bad request is what we return from this particular endpoint.
And I'll just copy this down and paste it below.
And the next one we'll add an endpoint for is unauthorized.
And we'll just say get unauthorized for this.
And for this one we will return an unauthorized response.
And this is going to return a 401 unauthorized to the client.
Now, we haven't set up any authentication yet, but that doesn't matter.
This is just the type of response that's going to be returned, and I'll be consistent with the spelling
of unauthorized there in the UK.
We use an S for this, but in the US that's with a Z and that's how the code is.
So I'll just be consistent there as well.
And let's add another endpoint because this one's going to be used for validation.
So I'm just going to say validation error.
And when we take a look at Crud operations on our API then we're going to be receiving in the body of
the HTTP request the properties that the user will be sending to either create something or update something.
And we can validate those requests on our API controllers.
And in fact, one of the features of the API controller attribute is it automatically validates the
properties that are being sent up as part of the request arguments or the body of the request, and
it will automatically generate a validation error if it doesn't.
If that request doesn't meet those requirements.
So we'll take a look at that in action soon.
But we don't have that functionality here really, because we're not going to send up anything in the
body of this request.
But I do want to send back what an equivalent of what we would get when using the validation that happens
as part of that API controller feature.
So I'm just going to call this method get validation error.
And to simulate a validation error we're going to tap into the model state.
And that's what the API controller attribute uses.
So if we get an error it's going to add something to what's referred to the model state.
It's an object that tracks errors for validation, and it can send one or more validation errors back
if it does have a validation error.
So to simulate this we're just going to say model state and model error.
And then this is a key value pair.
Really it's a key for the problem type.
And then we have a string for the error message.
So we'll replicate that.
And I'm just going to say problem one and say in the value say this is the first error.
And I'll copy this line down and have problem two and say this is the second error.
And then we can return instead of a bad request we'll return a validation problem.
Now this is still going to return a 400 bad request.
But as part of the error response it's also going to list the different validation errors that we had
for this particular request.
And let's see we'll add one more and I'll just copy this again and paste it below.
And this one's going to be for a server error, a server side error, and I'm going to call it get server
error.
And server errors.
In the HTTP world this is going to be effectively what is returned when our application encounters an
exception.
It's going to be a 500 internal server error.
That's what gets returned to the client.
And instead of returning an exception here, what we will do is we will throw a new exception and we'll
just put in as the exception message.
This is a server error.
And this will return a 500 HTTP 500 response.
And typically in production for sure we wouldn't send back the full details of the exception because
that comes with a stack trace.
It's quite big and it's not something we'd normally send back to the client, but in development we
will so that we get the full information about what problem we have encountered.
So that's our controller endpoints.
And because we've added a new controller at this stage then we need to restart our API server.
So let's go back to the API tab.
And it's been a while since we've looked at this.
So let's do a control C to shut it down.
And the dotnet watch again to restart this application.
And once it has restarted and it's restarted successfully, we'll go and take a look at postman.
And we'll just run through these different endpoints to see the kind of responses we're getting back
from our API right now.
So let's head to postman.
And if we take a look at I've got it listed as section five here, that should be section six.
So I'll just adjust this and it will be section six in your version of this.
And presumably that's going to be section seven for the next one down.
So I'll just make that minor adjustment now whilst I've seen it and not section six for you.
Section seven okay.
So we're in section six.
We've got we're dealing with error handling and let's just close down the other requests as well.
I'll just close that.
I won't save and I'll just close the other tabs.
So I'm working from a clean place and let's just minimize these as well.
Okay.
So the not found error.
What we'd expect to get back from this is a 404 not found response.
And that is what we get here.
Next one is a bad request error.
So it's going to API buggy bad request.
If we send this we get the 400 bad request and the error message saying this is not a good request.
The unauthorized error.
If we send this, we get the 404 not found.
That's not what I was expecting, but I did make a change to that route to use the US spelling.
So let me just change that inside here as well.
And if I click send, then we get the 401 unauthorized for the validation error.
If we click send on this one then we get the 400 bad request, but this time we get an errors object
and inside this errors object is the different problems.
And each different type of problem has can have an array of error messages.
So we see this is the first error and this is the second error.
And if we take a look at the server error and see what we get for this, then we get the Stacktrace
the system exception where we've got the this is a server error.
And then we have basically the stack trace down here, which gives us all the details of the problem.
These can look a little bit nasty when you first encounter them, but the first few lines typically
tell us what the problem is and where the problem occurred.
And then it's just a matter of looking at the code to understand why that exception was generated.
So great.
Now we've got that set up.
The next thing we'll do is take a look specifically at the exception we've got here, and we'll return
this in a specific format to make it easy to handle on the client side.
And we'll look at that next.