Android MAUI.NET8: Integrate Authorize Azure Function HTTP API with SQLite & Secure Storage
The word serverless is not truly serverless, meaning a cloud server is still needed for deployment of these serverless Azure functions base RESTful web APIs. The only advantage here is the focus on building business logic rather than worrying about the backend infrastructure for deployment. Currently, Azure cloud or any on premise server can run these serverless Azure functions REST web API. Azure functions although provides more than just developing RESTful web APIs, but for now my focus is on building Authorized RESTful web APIs with azure functions and later integrated that API with Android MAUI.NET8 platform.
Prerequisites:
- Knowledge of Android MAUI.NET8 Platform
- Knowledge of RESTful Web APIs.
- Knowledge of C# Programming.
Download Now!
Authorization Scheme
Develop Azure Function Server
2) I have changed my azure function default auto scaffold signature to something like shown below i.e.
... [Function("GetCountryListApi")] public async Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Function, "get", Route = "getCountryList")] HttpRequestData request) { // Initialization HttpResponseData response = null; // Do Something } ...
The important part here is "HttpRequestData" class, which is essential part of azure function to receive more complex request data. I have add the route to my API and I have also make this particular function to be HTTP GET. This is the authorized Azure function which cannot be accessed without API key. This particular function returns the county list data.
3) Next, Install following list of packages using nuget package manager i.e.
- Newtonsoft.Json
- CSVLibraryAK.Core
- sqlite-net-pcl
4) Now, before creating another Azure function HTTP trigger.
Create the SQLite database. To do so, create a new "AzServerlessApiFuncDatabase" class, which will contain all the necessary functions to create target database along with tables and CRUD operation methods. To give you a quick idea, the class will look something like below i.e.
... /// <summary> /// Azure Serverless API Function Database Class /// </summary> internal class AzServerlessApiFuncDatabase { #region Private Properties /// <summary> /// Gets or sets Database property. /// </summary> private readonly SQLiteAsyncConnection _database; #endregion #region Default Constructor method /// <summary> /// Initializes a new instance of the <see cref="AzServerlessApiFuncDatabase"/> class. /// </summary> /// <param name="dbPath">Database Path parameter</param> public AzServerlessApiFuncDatabase(string dbPath) { ... // Settings. this._database = new SQLiteAsyncConnection(dbPath); //create Database Tables. ... } #endregion #region Insert Key Value Pair method /// <summary> /// Insert Key Value Pair method /// </summary> /// <param name="keyValPairObj">Key valur pair object parameter</param> /// <returns> Return 0 if no data is inserted, otherwise greater than 0 value is returned </returns> public int InsertKeyValuePairAsync(KeyValuePairTable keyValPairObj) { // Initialization. int rowCount = 0; ... // Insert operation. ... // Info. return rowCount; } #endregion #region Read Key Value Pair method /// <summary> /// Read Key Value Pair method /// </summary> /// <returns> Return 0 if no data is inserted, otherwise greater than 0 value is returned </returns> public async Task<List<KeyValuePairTable>> ReadKeyValuePairAsync() { // Initialization. List<KeyValuePairTable> lst = new List<KeyValuePairTable>(); ... // Read operation. ... // Info. return lst; } #endregion } ...
5) Next, within the azure function class, create a private read only property "databaseManager" to access all the database operations. You also need to initialize the databaseManager property within the azure function class default constructor with the database access class that I have created previously as show below i.e.
... /// <summary> /// Gets or sets Database Manager property. /// </summary> private readonly AzServerlessApiFuncDatabase databaseManager; /// <summary> /// Initializes a new instance of the <see cref="AzFuncApi"/> class. /// </summary> /// <param name="logger">Logger parameter</param> public AzFuncApi(ILogger<AzFuncApi> logger) { ... // Varification. if (this.databaseManager == null) { // Get path location for database file. // Setting. this.databaseManager = new AzServerlessApiFuncDatabase(databasePath); } ... } #endregion ...
6) It's time to create another azure function but with anonymous authorization level. This particular azure function will save the API key into data as per the authorization scheme that I have already explained. This azure function will be a HTTP POST type method, which will take JSON input of API key name and API key value. Normally, when such HTTP POST API is created the signature of the method have an HttpReqesMessage type input and JToken form body type input. Since, in azure function HttpRequestData type input is supported, so, the method signature will look something like as shown below i.e.
... using FromBodyAttribute = Microsoft.Azure.Functions.Worker.Http.FromBodyAttribute; ... [Function("SaveKeyValuePairApi")] public async Task<HttpResponseData> SaveKeyValuePair([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "saveKeyValuePair")] HttpRequestData request, [FromBody] JToken postData) { // Initialization HttpResponseData response = null; // Do Something. } ...
In the above code, you need to include the using FormBodyAttribute library if you are going to use [FormBody] attribute within the method signature. When you you try to call this API using RESTED client. You will get error instead as shown below. The reason is that azure function cannot map JSON type input to JToken type like normal ASP.NET MVC or Core web API does.
7) To fix the above issue, the normal thinking is to post a JSON serialized string from client side and then deserialize the JSON string at API side. This means change the [Form body] parameter from JToken type to string instead as shown below. When the API is now called from the RESTED client, the response is successful, but, when similar API is called from android MAUI app using HttpClient class, an error occurs because azure function [FormBody] remains empty for some reason causing server side failure. This issue is happening in .NET8.
... using FromBodyAttribute = Microsoft.Azure.Functions.Worker.Http.FromBodyAttribute; ... [Function("SaveKeyValuePairApi")] public async Task<HttpResponseData> SaveKeyValuePair([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "saveKeyValuePair")] HttpRequestData request, [FromBody] string postData) { // Initialization HttpResponseData response = null; // Do Something. } ...
8) Now, to fix this issue especially when called from the android MAUI app. For .NET 8 isolated, the azure function signature is written as shown below, the form body will be captured by HttpRequestData class. Also, no need to include form body using library reference.
... [Function("SaveKeyValuePairApi")] public async Task<HttpResponseData> SaveKeyValuePair([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "saveKeyValuePair")] HttpRequestData request) { // Initialization HttpResponseData response = null; // Do Something. } ...
The input request data from the above signature can be extracted in three ways as shown below i.e.
... string postData = new StreamReader(request.Body).ReadToEnd(); ...
The above will capture the input request data as string. If you have sent JSON format then you can deserialize the above string into your target object.
...
KeyValuePairTable requestObj = await request.ReadFromJsonAsync<KeyValuePairTable>();
...
The above line of code allows to directly extract the input request JSON into the target object.
...
var query = System.Web.HttpUtility.ParseQueryString(req.Url.Query);
...
If you have passed the input data as URL query input then you can use the above line of code to extract the input request query data.
Develop Android MAUI App
Now that azure function REST web APIs are developed and tested successfully, It is time to create a simple Android MAUI app that loads country list data from azure function REST web API.
1) Create a new ".NET MAUI Multi-Project App" project and name it "AndroidMauiAzApi". Make sure to select .NET 8 as your framework and only select Android as the platform as shown below i.e.
2) Next, Install following list of packages using nuget package manager i.e.
- Newtonsoft.Json
- Microsoft.AspNet.WebApi.Client
3) Remember, MAUI secure storage on android is temporary, this means that it should not be auto-backup, but I also don't want to turn off the auto-backup. Luckily, MAUI android platform has a solution to only tun the partial auto-backup off for secure storage only. To do this expand the prject to "AndroidMauiAzApi.Droid->Resources" and then create "xml" folder.
4) Next, create "auto_backup_rules.xml" file under "AndroidMauiAzApi.Droid->Resources->xml" folder. Open the file and copy below lines of code within the file i.e.
<?xml version="1.0" encoding="utf-8"?> <full-backup-content> <include domain="sharedpref" path="."/> <exclude domain="sharedpref" path="${applicationId}.microsoft.maui.essentials.preferences.xml"/> </full-backup-content>
5) Now, open the "AndroidMauiAzApi.Droid->AndroidManifest.xml" file in the XML editor and add below line of code at the end of application tag as shown below i.e.
... <application ... android:fullBackupContent="@xml/auto_backup_rules"> </application> ...
6) Since, the Azure function RESTful web API is currently running on local server, therefore, some additional configurations at MAUI android end is needed to successfully test the azure function API integration with MAUI android app. To do so, open "AndroidMauiAzApi.Droid->MainApplication.cs" file and replace [Application] attribute as shown below i.e.
#if DEBUG [Application(UsesCleartextTraffic = true)] #else [Application] #endif
7) Then, create "network_security_config.xml" file under "AndroidMauiAzApi.Droid->Resources->xml" folder. Open the file and copy below lines of code within the file. You need to replace the IP address 10.0.2.2 within this file with your IP address i.e.
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">10.0.2.2</domain> </domain-config> </network-security-config>
8) Now, open the "AndroidMauiAzApi.Droid->AndroidManifest.xml"
file in the XML editor and add below line of code at the start of
application tag as shown below i.e.
... <application android:networkSecurityConfig="@xml/network_security_config" ...> ... </application> ...
9) Next, create the HTTP GET and POST APIs using HttpClient class to access the azure function RESTful web API within "AndroidMauiAzApi" project.
10) In the "App.xaml.cs" class file default constructor build the logic to save the API key inside the secure storage and call the Azure function HTTP POST method to save the generated API key at the server side as well. This is necessary, as this logic will execute only at the launch of the application.
11) Build the necessary List View and a button to call the authorize azure function HTTP GET API to get the country list data within the "MainPage.xaml" & "MainPage.xaml.cs" files.
12) Execute the azure function project first and keep it running. Then execute the android MAUI app project and click the "Load" button on the android app to get data from azure function RESTful web API as shown below i.e.
Conclusion
In this article, you will learn to integrate Authorized Azure Functions HTTP RESTful web APIs with Android MAUI.NET8
platform using SQLite database and Android MAUI Secure Storage. You will learn about free of cost authorization scheme for securing Azure Function REST web API using secret API key. You will also learn about creating SQLite database and secure storage as a part of authorization scheme. You will learn to enable localhost API testing from android app using HttpClient class. You will also enable partial auto-back on android MAUI app to ignore the secure storage auto-backup. Finally, develop and execute both azure function server and Android MAUI app.
Post a Comment