35 - Refactor MAUI App for API

  • last month
Transcript
00:00Welcome back guys.
00:02In this lesson, we're going to be refactoring
00:04our mobile app so that we can communicate with our API.
00:07Now, I'm going to start off with our database service.
00:11Initially, it was called CarService, I believe.
00:15I've called it CarDatabaseService,
00:18so it's evident and very clear that this is
00:21for the database connectivity operations.
00:25In the same breath,
00:26we're just going to go ahead and add another service,
00:30and I'll just call this one CarAPIService.
00:34So CarAPIService,
00:37so it's very clear what this one is for also.
00:40So what we want to do is,
00:44well, we want to make this public,
00:47and then I'm going to initialize an object
00:50of type HTTP client.
00:52Well, at least I'll declare it here,
00:54but in the constructor, when this is invoked,
00:57I will create a new instance of HTTP client.
01:00So HTTP client is our class that allows us
01:03to open a socket to communicate with an API, all right?
01:08So whenever this is going to be invoked,
01:11we need a new object of it or a new instance of it,
01:14and then we can carry out our operations.
01:17Now, of course, every API needs an address to connect to,
01:21and we kind of went through that whole cycle
01:24about the addresses.
01:25We already opened up our manifest
01:28to know that it can allow clear text communications
01:32through 10.0.22 localhost,
01:37and well, this was my IP address for my local domain,
01:40which might be different for you.
01:42So another thing though is that with the Android devices,
01:46we need to have that 10.0.22 address
01:50versus other devices which can see localhost.
01:54So what I'll do here is a public static string.
01:58I'll create a base address,
02:01and then I'm going to give it the value
02:03based on what kind of device we're on.
02:06So we have this static class called device info
02:10that gives us this enum called the platform,
02:13and then we can check if platform is equivalent to,
02:18and for this one, I'm going to say Android.
02:22So, sorry, this will be device platform.
02:25If device platform, yes, dot Android.
02:28So you see, you can check which kind of operating system
02:31you're dealing with here, all right?
02:33So if it's Android,
02:35then I would want to place that HTTP 10.0.2.2
02:42alongside the port 1899,
02:45because that's what I had put as my port
02:47when I deployed the API.
02:49So whatever port you had put,
02:51if you used 8099 like I did, then use that.
02:55If you use something else,
02:56then you can feel free to put that number.
02:59Otherwise, we can look at localhost,
03:01and that is what is expected.
03:04So whenever we are going to be using the Android device
03:08or the Android emulator,
03:09it will know which one to select.
03:12Otherwise, we can see localhost
03:14if you're using the iOS emulator
03:17or the Windows emulator, et cetera.
03:20So those are two modifications.
03:24So for the HTTP client,
03:26I can actually just right here,
03:27set that base address, right?
03:29So I can just say base address
03:31is equal to the local variable base address.
03:36So that should take care of that,
03:38and it will not because this needs a new URI.
03:41So let me just do that in the same situation.
03:45There we go.
03:46So base address is equal to a new URI of type base address.
03:50All right, looking good so far.
03:53So next, we are going to have to wire up
03:56our car API service with the same kind of
03:59CRUD functionality that we had for the database service.
04:02So I'm going to be kind of copying some things
04:05from this side, just to make sure that we are consistent.
04:10For instance, that string status message,
04:12I want to make sure we send out the same status message.
04:14Now I can say public async task,
04:19and I want to return a list of type car, right?
04:25Where is this coming from?
04:26Well, we want to do that get method for all the cars, right?
04:31So list of type car, I will call it get cars.
04:36You can call it get cars async,
04:37if you want to follow that naming pattern as usual,
04:41or you can just follow along
04:43and we'll include the using statements
04:45so we have that model.
04:46Other ones that we would want,
04:48and I'll just wireframe them from now
04:50so we can fill them out.
04:52We want to get car,
04:53and we would retrieve that car via the ID of that car.
04:58We can also do add car, which is going to,
05:03oh, sorry, this one is going to return
05:05just a task of type car, not list of cars.
05:10Add car would just be a task.
05:13We don't need to return anything.
05:15And this is add car.
05:18And this one takes a parameter of type car.
05:21It takes the entire car object.
05:24And then we have delete car,
05:26which is going to look similar to get car,
05:29except it's a task that doesn't return anything.
05:33And it does take that ID.
05:36And then finally we have update car,
05:39which we know is going to take that int ID
05:43as well as an object of type car, right?
05:48And this one does not return anything.
05:52And we call it update car.
05:59All right, so now that we have the wireframing done,
06:01let's add the meat.
06:03So get cars.
06:04And of course I can just bounce over
06:07and look at what we did with get cars on this side.
06:10I'm actually just going to do a control and paste,
06:14but of course we're going to modify
06:15because we don't have any init
06:17and we're not going to be returning any con to any table.
06:21Instead, what I'm going to try
06:24is to get a response from my HTTP client
06:28when it calls that URL.
06:29So I'm going to say var response is equal to HTTP client.
06:35Please go and get,
06:37so the HTTP client comes with methods
06:41that match the verbs that you'd usually use, right?
06:44So you can do get async, you can do put,
06:47you can do patch, you can do delete,
06:49you can do all of those verbs
06:52that we have just developed in our API.
06:55So I can now say get async,
06:57or you could do get string async
06:59when it's sure that it will return a string.
07:02Get async will actually return an HTTP response message,
07:07which you then have to interrogate for the string, right?
07:11Another thing is that get async
07:14will actually return the response message
07:16with the response code.
07:19So you would be able to interrogate it
07:21and see if it's a 500 or 400 or 200,
07:26whereas one like get stream or get string,
07:29those that are getting the specific types,
07:32they would have a method that's built into the call
07:35called ensure success code.
07:38So let me just demonstrate that quickly.
07:41So if I do get async,
07:42and then I would give it the base address.
07:45So, well, it already has a base address, right?
07:47So what I would really want to do
07:49is just append the actual endpoint to the base address.
07:53So if this is the base address,
07:55if you go back to the swagger,
07:56you would have seen localhost colon 8099 slash cards.
08:02So to get the cards now,
08:04because we could have had multiple endpoints on our API.
08:08So now I'm going to say get,
08:10it has a base address already,
08:12and I'm saying at slash cards.
08:14So it will form all that request base address
08:17and then append whatever we put in here, right?
08:20Now, when it does that get,
08:23that response has is success code.
08:29Oh, I'm sorry.
08:30Let me await this.
08:31I need to await that call so I can see properly.
08:33So response, that response object, there we go.
08:38We'll have the content on it.
08:40We can say that ensure success status code,
08:44which means that if it's anything other than
08:46the 200, 300 range,
08:47it will throw an exception and it will get caught here.
08:52Right?
08:53So this method can be used to just make sure
08:55that it always comes back as a success code.
08:58So you don't run into problems
08:59when you're trying to get like the content,
09:02not knowing what the content is and the content itself,
09:05it makes no assumptions as to what the content might be.
09:08Cause it just says HTTP content.
09:10It's not up to us to convert it into whatever type we want,
09:14which in our case would be a list of type car.
09:18So what I'm saying now is that as a fail safe,
09:21you'd always want to either ensure success status code,
09:25or you'd want to check like done if statement to say,
09:30if response dot is success status code.
09:34So that's just a Boolean.
09:36If it is do this, if not, et cetera.
09:39Or once again, I can just say, get string async.
09:46And then that string async will automatically
09:49have that ensure status code.
09:51So if it's ensure success status code.
09:53So if it is not successful, it will throw that exception
09:57and it will catch it automatically.
09:59So that just shortens the amount of word
10:01that we have to put in.
10:02Now we're sure that this response is a string.
10:06And based on what we've seen,
10:07that string should be in the form of JSON.
10:09So I can say var cars, which is, well,
10:13right now equal to a new list of type car,
10:17but var cars, I can know JSON dot JSON convert.
10:24And this is a method that is a part of the Newton's
10:27of JSON library.
10:29So if you're not seeing it appear like this on your screen,
10:33you'll probably have the option to install via NuGet
10:35or you can always just go to NuGet package manager
10:38and fetch that one Newton soft dot JSON, right?
10:42So Newton soft dot JSON,
10:44make sure we have that missing reference.
10:46And then we can deserialize.
10:50So it's a deserialize object.
10:54And I specify the type I want,
10:57which is a list of cars.
10:58And it will do that with the response.
11:00So this operation will take that huge string of JSON objects
11:05and convert them into hard typed
11:08or strongly typed objects of type car in a list,
11:12which I can then return.
11:14And of course this could also be done in two lines
11:17where all I do is return at this point.
11:21So I can just return that deserialization of the car
11:27and that's it, right?
11:29So it really seems long because of the extra explanation,
11:32but look at how short it's going to be down here, right?
11:35So when we're going to get car by ID,
11:37actually can repeat most of what I've done here
11:41with the exception that I'm not returning a list
11:44of type cars, just a car.
11:46And the cars URL needs to have the ID on it, right?
11:52So I have to say cars, and then I could say slash
11:57and then append the ID.
12:00So the relevance of this is that I'm saying,
12:03go base URL, we know that already,
12:06go to the cars endpoint, we know that already,
12:08but we know that we need to put on that ID
12:11on the URL that is formed.
12:15So cars slash and we append or concatenate that ID value
12:20onto that string, makes the call,
12:22and then we would just return that data converted
12:26as a car object.
12:27And if we don't find anything,
12:29we just return an empty object at any rate,
12:33this would make sure it was successful,
12:35if not throw the exception, right?
12:38At this point, I'm not comfortable about returning objects,
12:43I'll just return null.
12:45Because if I return null,
12:46then we can also have a fail safe on our calling method
12:50that just says null, right?
12:53So we don't expect any data,
12:55if we fail to retrieve data,
12:56then we want to send back null, there is no data.
12:59Now let's look at the add car.
13:01So once again, let me copy,
13:04we'll try catch, we don't have to return anything here,
13:07so I'm just going to try.
13:08What am I going to try?
13:09So I got the car object,
13:12and what I need to do is post that car object.
13:16So I'm going to say post async,
13:20give it the URL, which is cars,
13:25or slash car, and then what does it ask for?
13:27It asks for the URI or the URL, right?
13:30And then the content it should post.
13:32So I'm not going to be appending anything here,
13:35instead, I'm going to comma separate,
13:37and the content would be the object of car.
13:40So the cool thing here is that this method
13:43will actually decode it for me.
13:48Oops, there is post as JSON.
13:50Actually, that's not the method I want.
13:52I want post as JSON async, there we go.
13:55So post as JSON async takes it a bit further,
13:59and it actually allows you to just send over the value,
14:04and it will serialize it as JSON for you,
14:07and put it on the wire, right?
14:09So I just have to say the URL and the object,
14:12it will take care of the serialization for me.
14:16After this, now, I would be able to say response,
14:21or I would want to say response dot,
14:24let me just fix my alignment here.
14:26So response dot ensure success status code,
14:31and then I can set my status message
14:35to be equal to insert successful.
14:39And once that is done, we now have the add car.
14:43Let's move on to delete.
14:44So we're just going to go down the line
14:46as we know we need to.
14:48So for the delete, it's going to be pretty simple.
14:51We just have to say HTTP client dot delete async,
14:57give it the URL,
14:58and this one is going to look similar to the get,
15:01where we just need to append the ID.
15:04We can ensure success status code,
15:07and we can just say delete was successful.
15:11Of course, we need to change our status messages
15:13as we go along,
15:14because I keep on neglecting this one.
15:18So fail to delete data,
15:21and the other one would have said failed to add data, right?
15:27All right, looking good.
15:28So let us move on to the update,
15:30and then this part of the service creation
15:33is pretty much done.
15:35So for the update, we're going to do this.
15:37We're going to say HTTP or VAR response
15:42is equal to HTTP client put as JSON async,
15:47and I just left that placeholder there URL
15:50so that we know that the URL needs to go there,
15:52plus ID, and then we give it the object
15:55of type car that came in.
15:57We ensure that it was a success status code,
16:01and then we send back the status message.
16:04Otherwise, we say fail to update data.
16:08So that is it for setting up our car service.
16:13So let us go over to our mauiprogram.cs,
16:19go and say builder.services,
16:25and we're going to add transient.
16:27Now we're adding transient,
16:30and we're adding the car API service.
16:34We're doing this one as transient
16:35because for each time we open a connection,
16:38we want a brand new object of that, right?
16:40So we want to make sure that that is indeed transient,
16:45and then we can go ahead and inject it
16:47into our car list view model.
16:52So I had a few changes there that I cleaned up
16:54so that we can make sure we're on the same page.
16:57So let me just go ahead and say car API service,
17:05include any missing using reference.
17:10So car API service, I'll call it car API service,
17:13and then we instantiate a new field of it, all right?
17:19So now I want us to get the car list.
17:24So what we do, we check if there are any cars,
17:28and then we retrieve the cars from the database,
17:30and then for each car that we got from the database,
17:32we return.
17:33So what I'm going to do here is comment this out,
17:38and we're going to try it this way.
17:40Cars is now equal to car API service.getCars, all right?
17:50So the expectation is that this is now going
17:52to meet that API call,
17:55and of course you have to make sure that we await that.
17:58And what I'm going to do is run this in debug mode.
18:03So I'm going to place a break point right here
18:06on this getCars line, and then let's test this out.
18:11Right before you do that, one modification,
18:14I removed the call to getCars from the constructor
18:18for the view model.
18:19I removed it.
18:20So you can go ahead and remove that.
18:22What we'll do is just manually refresh the list
18:25just to make sure that it works.
18:27All right, so when our app is up and running,
18:29we can do the refresh.
18:30I'll just drag, and it will hit our break point,
18:34and sure enough, it does.
18:36So when we take a look at our HTTP client object,
18:40well, there's not really much to interrogate,
18:42but we do see here that we have the base address
18:44like we set it during the initialization,
18:47and this is going to use the 10.0.2.2 address, right?
18:53And then we can hit F10 so that it can just step over,
19:00and then it will make the API call at that point,
19:03and then it gets the response.
19:05If we look at that response, you see here
19:07that it's just one big, well, kind of formatted string
19:12for our JSON text, right?
19:15And then we're just going to convert to the list of cars,
19:19and that's what gets returned, and there we go.
19:22So it looks just the same way that we left it
19:25when we were using the database, right?
19:27Nice and easy.
19:29And if we wanted to retrofit everything now
19:32to start using our API, of course,
19:35we can modify our CRUD operations.
19:38So the delete car, instead of calling our database service,
19:42we would do that instead of doing the updates,
19:45et cetera, et cetera.
19:46So there are a number of things we can refactor here.
19:48I'll actually leave that as a coding challenge for you.
19:51Just go ahead and refactor all of these operations.
19:55For now, I suggest instead of deleting though,
19:58don't delete these, just comment them out
20:00because I'm going to show you a little operation
20:02that we can consider when we start dealing with APIs.