WEBVTT

00:00.350 --> 00:00.800
Okay.

00:00.800 --> 00:03.860
In this section we're going to take a look at error handling.

00:03.860 --> 00:05.210
And to help us do this.

00:05.210 --> 00:07.160
First of all we're going to set a few things up.

00:07.160 --> 00:13.730
We're going to create a controller that's going to have some endpoints designed just to return HTTP

00:13.760 --> 00:14.840
error responses.

00:14.840 --> 00:21.230
And the idea of this is so that we can set up something on the client side, so that when we do get

00:21.230 --> 00:26.870
an error response from our API, we handle it centrally in a very specific way for each different type

00:26.870 --> 00:31.580
of error that we expect to get back from our API server.

00:31.580 --> 00:34.790
So first of all, we're going to deal with this in the API.

00:34.790 --> 00:41.480
So we're going to go back to our Solution Explorer view inside the API project and inside the controllers.

00:41.480 --> 00:44.240
What we've currently got is we've got two controllers.

00:44.240 --> 00:49.250
One was a demo controller that was supplied when we created the template, but they've got some features

00:49.250 --> 00:50.390
in common with each other.

00:50.390 --> 00:56.090
We've got a controller base that they all inherit from, and this is the base class for an MVC controller

00:56.090 --> 00:57.320
without view support.

00:57.320 --> 01:03.430
We're just having API endpoints so they receive HTTP requests and they return HTTP responses.

01:03.430 --> 01:09.700
They don't return HTML, which is what would be considered a view in MVC.

01:09.730 --> 01:12.880
The old model view controller terminology.

01:12.910 --> 01:16.930
So we use this when we've got an API controller.

01:16.930 --> 01:18.640
But this is the same code.

01:18.640 --> 01:21.970
And we've got our attributes here for the routing and the API controller.

01:21.970 --> 01:26.740
But it's pretty much the same code that we have in our products controller as well.

01:27.310 --> 01:29.440
Also deriving from the controller base.

01:29.440 --> 01:32.770
And we've got our two attributes inside here.

01:33.130 --> 01:37.420
So you'll often hear the terminology dry in coding.

01:37.450 --> 01:38.530
Do not repeat yourself.

01:38.530 --> 01:45.910
And we do make the effort where we can to reduce repetitive code and make things more reusable.

01:45.910 --> 01:51.850
So a quick and easy example is we're going to create a base API controller that has these attributes.

01:51.850 --> 01:58.150
And let's deriving from controller base to save us from repeating that in each controller that we create.

01:58.150 --> 02:03.640
So we're going to create, first of all, before we get started on error handling, we do have a template

02:03.640 --> 02:04.840
for an API controller.

02:04.840 --> 02:06.280
So we can use this one.

02:06.280 --> 02:12.430
And I'm going to call this base API controller and press return.

02:12.430 --> 02:17.770
And this completes the boilerplate that we need for a typical API controller anyway.

02:17.800 --> 02:20.020
And in fact this is exactly how we want it.

02:20.020 --> 02:25.750
Apart from the import layer or the using statement that we don't need inside this controller.

02:25.750 --> 02:31.390
So the idea of this, they're all going to have a route of API forward slash and then a square brackets

02:31.390 --> 02:36.310
controller with the API controller attributes and deriving from controller base.

02:36.310 --> 02:41.380
And then we can just derive from our base API controller in our API controllers.

02:41.380 --> 02:44.800
And they're automatically going to have these attributes.

02:44.800 --> 02:45.820
It's going to have that route.

02:45.820 --> 02:48.910
And it's going to be deriving from the correct class as well.

02:48.910 --> 02:51.340
So back to the explorer view.

02:51.340 --> 02:54.220
And we'll just update the weather forecast controller.

02:54.220 --> 03:00.270
First of all we'll take out the API controller attribute and instead of deriving from controller base,

03:00.270 --> 03:06.750
we can just derive from our base API controller and we'll have the same functionality with one small

03:06.750 --> 03:07.140
difference.

03:07.140 --> 03:13.710
Of course, instead of going to localhost 5001 Weather forecast, we would now need to go to API forward

03:13.710 --> 03:19.980
slash weather forecast because of the route configuration we have inside here, and we'll just do the

03:19.980 --> 03:22.620
same for the products controller as well.

03:22.650 --> 03:30.150
We will remove the routing attributes and we will derive from the base API controller.

03:30.180 --> 03:34.890
And let's just remove the unnecessary using statement inside here.

03:34.920 --> 03:40.830
Now that I have seen it, we can use the quick fix here just to remove the unnecessary usings.

03:40.830 --> 03:45.930
It doesn't do any harm, but obviously if you do see something that's not needed, you can just go ahead

03:45.930 --> 03:47.250
and remove it.

03:47.280 --> 03:53.850
It's not as noisy as our react application where we've got an import we're not using.

03:53.850 --> 03:54.930
That's very noisy.

03:54.960 --> 03:59.880
C-sharp is a bit more subtle, and it just sort of dims it out in the code.

03:59.910 --> 04:06.750
So now we have that in place, let's go ahead and create a new controller and I'll say new file.

04:06.750 --> 04:08.550
And we'll just make this a class.

04:08.550 --> 04:14.280
We won't use the API controller because we don't need the API controller attribute or the route when

04:14.280 --> 04:16.800
we're deriving from the base API controller.

04:16.800 --> 04:21.690
So this is going to be a class and I'm going to call it buggy controller.

04:21.690 --> 04:24.600
It's just going to have endpoints that return errors.

04:24.630 --> 04:29.850
Inside here we're going to derive from the base API controller as mentioned.

04:29.850 --> 04:32.070
And we're going to create some endpoints.

04:32.070 --> 04:33.600
And this is going to be an HTTP.

04:33.630 --> 04:37.890
Get this first one and we'll give it a route parameter of notfound.

04:37.920 --> 04:42.780
This is going to be an example of a request where we don't find what we're looking for.

04:42.780 --> 04:44.400
So I'm going to make it public.

04:44.400 --> 04:50.790
And if we're not returning a specific type then we don't need to use Actionresult.

04:50.790 --> 04:56.240
We can actually use a different version of Actionresult, the Actionresult result that doesn't return

04:56.390 --> 05:02.750
a specific type, but we use it when we want to return an HTTP response, for instance, and we don't

05:02.750 --> 05:04.400
want to supply a type.

05:04.400 --> 05:08.000
And I'm just going to give this method a name of get Notfound.

05:08.300 --> 05:12.020
And inside here we will return literally not found.

05:12.020 --> 05:15.800
The goal of this is just to return error responses.

05:15.800 --> 05:21.770
And then on our client side, just as an example, what we'll do when we do get a not found returned

05:21.770 --> 05:26.480
from our API, we'll redirect them to a client side not found page.

05:26.480 --> 05:32.180
So if they try to fetch a product that didn't exist in our database, we would return a not found from

05:32.180 --> 05:33.110
our API.

05:33.110 --> 05:37.820
And then on the client side, we'd redirect them to a not found page and say, hey, we couldn't find

05:37.850 --> 05:43.430
what you were looking for there, so I'm just going to copy this down and paste it below, and we'll

05:43.430 --> 05:45.020
have another endpoint.

05:45.020 --> 05:53.390
And this one is going to be for a bad request and we'll specify get bad request.

05:53.540 --> 05:59.450
So on occasions, if there's a client side error where they've done something that's considered a client

05:59.450 --> 06:08.000
side error, but it happens on our API server, then we would return, for instance, a bad request

06:08.000 --> 06:10.520
from our API controller endpoint.

06:10.520 --> 06:17.360
And I'll just say this is not a good request as the error message that we'll return with this one.

06:17.360 --> 06:21.020
So this is a 400 HTTP response.

06:21.050 --> 06:26.000
A 400 bad request is what we return from this particular endpoint.

06:26.060 --> 06:30.410
And I'll just copy this down and paste it below.

06:30.650 --> 06:35.360
And the next one we'll add an endpoint for is unauthorized.

06:35.360 --> 06:40.820
And we'll just say get unauthorized for this.

06:40.820 --> 06:45.920
And for this one we will return an unauthorized response.

06:45.920 --> 06:51.080
And this is going to return a 401 unauthorized to the client.

06:51.110 --> 06:54.230
Now, we haven't set up any authentication yet, but that doesn't matter.

06:54.230 --> 06:59.120
This is just the type of response that's going to be returned, and I'll be consistent with the spelling

06:59.150 --> 07:00.950
of unauthorized there in the UK.

07:00.980 --> 07:06.830
We use an S for this, but in the US that's with a Z and that's how the code is.

07:06.830 --> 07:09.290
So I'll just be consistent there as well.

07:09.410 --> 07:15.230
And let's add another endpoint because this one's going to be used for validation.

07:15.350 --> 07:18.530
So I'm just going to say validation error.

07:18.770 --> 07:26.600
And when we take a look at Crud operations on our API then we're going to be receiving in the body of

07:26.600 --> 07:35.390
the HTTP request the properties that the user will be sending to either create something or update something.

07:35.390 --> 07:39.770
And we can validate those requests on our API controllers.

07:39.800 --> 07:47.780
And in fact, one of the features of the API controller attribute is it automatically validates the

07:47.780 --> 07:54.180
properties that are being sent up as part of the request arguments or the body of the request, and

07:54.180 --> 07:58.530
it will automatically generate a validation error if it doesn't.

07:58.560 --> 08:01.050
If that request doesn't meet those requirements.

08:01.050 --> 08:03.210
So we'll take a look at that in action soon.

08:03.210 --> 08:08.310
But we don't have that functionality here really, because we're not going to send up anything in the

08:08.310 --> 08:09.450
body of this request.

08:09.450 --> 08:16.470
But I do want to send back what an equivalent of what we would get when using the validation that happens

08:16.470 --> 08:19.530
as part of that API controller feature.

08:19.530 --> 08:23.250
So I'm just going to call this method get validation error.

08:23.400 --> 08:30.090
And to simulate a validation error we're going to tap into the model state.

08:30.090 --> 08:33.990
And that's what the API controller attribute uses.

08:33.990 --> 08:38.940
So if we get an error it's going to add something to what's referred to the model state.

08:38.940 --> 08:45.750
It's an object that tracks errors for validation, and it can send one or more validation errors back

08:45.750 --> 08:47.880
if it does have a validation error.

08:47.880 --> 08:51.640
So to simulate this we're just going to say model state and model error.

08:51.640 --> 08:56.020
And then this is a key value pair.

08:56.050 --> 08:58.720
Really it's a key for the problem type.

08:58.720 --> 09:00.700
And then we have a string for the error message.

09:00.700 --> 09:02.860
So we'll replicate that.

09:02.860 --> 09:11.770
And I'm just going to say problem one and say in the value say this is the first error.

09:11.770 --> 09:19.090
And I'll copy this line down and have problem two and say this is the second error.

09:19.090 --> 09:27.130
And then we can return instead of a bad request we'll return a validation problem.

09:27.160 --> 09:30.280
Now this is still going to return a 400 bad request.

09:30.280 --> 09:37.180
But as part of the error response it's also going to list the different validation errors that we had

09:37.210 --> 09:39.580
for this particular request.

09:39.880 --> 09:46.810
And let's see we'll add one more and I'll just copy this again and paste it below.

09:46.810 --> 09:53.900
And this one's going to be for a server error, a server side error, and I'm going to call it get server

09:53.930 --> 09:54.740
error.

09:54.800 --> 09:56.150
And server errors.

09:56.150 --> 10:03.740
In the HTTP world this is going to be effectively what is returned when our application encounters an

10:03.740 --> 10:04.580
exception.

10:04.610 --> 10:08.300
It's going to be a 500 internal server error.

10:08.330 --> 10:10.970
That's what gets returned to the client.

10:10.970 --> 10:19.700
And instead of returning an exception here, what we will do is we will throw a new exception and we'll

10:19.700 --> 10:22.400
just put in as the exception message.

10:22.400 --> 10:25.370
This is a server error.

10:25.370 --> 10:29.660
And this will return a 500 HTTP 500 response.

10:29.660 --> 10:35.090
And typically in production for sure we wouldn't send back the full details of the exception because

10:35.090 --> 10:36.770
that comes with a stack trace.

10:36.770 --> 10:41.420
It's quite big and it's not something we'd normally send back to the client, but in development we

10:41.420 --> 10:46.400
will so that we get the full information about what problem we have encountered.

10:46.490 --> 10:49.640
So that's our controller endpoints.

10:49.640 --> 10:55.370
And because we've added a new controller at this stage then we need to restart our API server.

10:55.400 --> 10:57.170
So let's go back to the API tab.

10:57.230 --> 10:59.330
And it's been a while since we've looked at this.

10:59.330 --> 11:01.910
So let's do a control C to shut it down.

11:01.910 --> 11:06.110
And the dotnet watch again to restart this application.

11:06.740 --> 11:12.860
And once it has restarted and it's restarted successfully, we'll go and take a look at postman.

11:12.860 --> 11:17.600
And we'll just run through these different endpoints to see the kind of responses we're getting back

11:17.600 --> 11:19.070
from our API right now.

11:19.070 --> 11:20.600
So let's head to postman.

11:20.600 --> 11:27.230
And if we take a look at I've got it listed as section five here, that should be section six.

11:27.230 --> 11:32.330
So I'll just adjust this and it will be section six in your version of this.

11:32.360 --> 11:37.640
And presumably that's going to be section seven for the next one down.

11:37.640 --> 11:42.860
So I'll just make that minor adjustment now whilst I've seen it and not section six for you.

11:42.890 --> 11:44.300
Section seven okay.

11:44.330 --> 11:46.040
So we're in section six.

11:46.040 --> 11:52.060
We've got we're dealing with error handling and let's just close down the other requests as well.

11:52.090 --> 11:53.380
I'll just close that.

11:53.410 --> 11:58.000
I won't save and I'll just close the other tabs.

11:58.000 --> 12:04.090
So I'm working from a clean place and let's just minimize these as well.

12:04.120 --> 12:04.540
Okay.

12:04.540 --> 12:05.590
So the not found error.

12:05.620 --> 12:09.730
What we'd expect to get back from this is a 404 not found response.

12:09.730 --> 12:12.250
And that is what we get here.

12:12.280 --> 12:14.230
Next one is a bad request error.

12:14.230 --> 12:16.600
So it's going to API buggy bad request.

12:16.600 --> 12:22.780
If we send this we get the 400 bad request and the error message saying this is not a good request.

12:22.810 --> 12:24.220
The unauthorized error.

12:24.220 --> 12:26.950
If we send this, we get the 404 not found.

12:26.950 --> 12:32.440
That's not what I was expecting, but I did make a change to that route to use the US spelling.

12:32.440 --> 12:35.620
So let me just change that inside here as well.

12:35.620 --> 12:41.140
And if I click send, then we get the 401 unauthorized for the validation error.

12:41.140 --> 12:48.010
If we click send on this one then we get the 400 bad request, but this time we get an errors object

12:48.010 --> 12:52.600
and inside this errors object is the different problems.

12:52.660 --> 12:58.510
And each different type of problem has can have an array of error messages.

12:58.510 --> 13:01.930
So we see this is the first error and this is the second error.

13:01.930 --> 13:07.450
And if we take a look at the server error and see what we get for this, then we get the Stacktrace

13:07.450 --> 13:12.010
the system exception where we've got the this is a server error.

13:12.010 --> 13:17.260
And then we have basically the stack trace down here, which gives us all the details of the problem.

13:17.320 --> 13:22.000
These can look a little bit nasty when you first encounter them, but the first few lines typically

13:22.000 --> 13:25.360
tell us what the problem is and where the problem occurred.

13:25.360 --> 13:30.610
And then it's just a matter of looking at the code to understand why that exception was generated.

13:30.610 --> 13:31.300
So great.

13:31.330 --> 13:32.410
Now we've got that set up.

13:32.410 --> 13:39.220
The next thing we'll do is take a look specifically at the exception we've got here, and we'll return

13:39.220 --> 13:43.060
this in a specific format to make it easy to handle on the client side.

13:43.060 --> 13:44.830
And we'll look at that next.
