Blog Details

image

05 Feb 2024

09

35

Introduction

This documentation shows you how you can configure Local Api Access From Android Phone.

In the context of MAUI (Multi-platform App UI) development, accessing a local WebAPI from mobile devices introduces specific considerations. MAUI facilitates cross-platform app development for Android, iOS, and Windows with a single codebase, using .NET MAUI and Xamarin.Forms technologies. When dealing with local WebAPIs, developers may encounter challenges related to device emulators, network configurations, and security protocols.

Imagine code like this git git As you can see the api call succeded for windows app but not for android app.

So what can we do about it?

Lets modify the code to make it work in android git

You need to add some code like this to make it work

    public class HttpsClientHandlerService : IHttpsClientHandlerService
    {
        public HttpMessageHandler GetPlatformMessageHandler()
        {
 #if ANDROID
            var handler = new Xamarin.Android.Net.AndroidMessageHandler();
        handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
        {
            if (cert != null && cert.Issuer.Equals("CN=localhost"))
                return true;
            return errors == System.Net.Security.SslPolicyErrors.None;
        };
        return handler;
#elif IOS
            var handler = new NSUrlSessionHandler
            {
                TrustOverrideForUrl = IsHttpsLocalhost
            };
            return handler;
#else
            //throw new PlatformNotSupportedException("Only Android and iOS supported.");
            return new HttpClientHandler();
#endif
        }

#if IOS
        public bool IsHttpsLocalhost(NSUrlSessionHandler sender, string url, Security.SecTrust trust)
        {
            if (url.StartsWith("https://localhost"))
                return true;
            return false;
        }
#endif
    }

Then again windows app will not work with url 10.0.2.2

So what is the ultimate solution?

The ultimate solution is to write code something like this

public interface IHttpsClientHandlerService
{
    HttpMessageHandler GetPlatformMessageHandler();
}

public class HttpClientOptions
{
    public string? BaseAddress { get; set; }
    public string? LocalAddress { get; set; }
    public string? RemoteAddress { get; set; }
    // public DeviceType DeviceType { get; set; }
    public TimeSpan Timeout { get; set; }
    public IDictionary<string, string> DefaultRequestHeaders { get; set; }
    public string? BearerToken { get; set; }
    public int PortNumber { get; set; } // New property for port number

    public HttpClientOptions()
    {
        DefaultRequestHeaders = new Dictionary<string, string>();
        PortNumber = 7101;
        RemoteAddress = "https://evanapi.azurewebsites.net";
    }
}

public class HttpClientOptionstightCoupled
{
    public string? BaseAddress { get; set; }
    public string? LocalAddress { get; set; }
    public string? RemoteAddress { get; set; }
    // public DeviceType DeviceType { get; set; }
    public TimeSpan Timeout { get; set; } = new TimeSpan(10);
    public IDictionary<string, string> DefaultRequestHeaders { get; set; }
    public string? BearerToken { get; set; }
    public int PortNumber { get; set; } 
    public IHttpsClientHandlerService? httpsClientHandlerService { get; set; } 

    public HttpClientOptionstightCoupled()
    {
        DefaultRequestHeaders = new Dictionary<string, string>();
        PortNumber = 7101;
        RemoteAddress = "https://evanapi.azurewebsites.net";
    }
}
public static class EvanHttpClientExtensions
{

    public static void AddEvanHttpClient(this IServiceCollection services, Action<HttpClientOptions, IHttpsClientHandlerService> configureOptions)
    {
        services.AddScoped<HttpClient>(sp =>
        {
            HttpClient? httpClient = null;
            var options = new HttpClientOptions();
            
          

            IHttpsClientHandlerService httpsClientHandlerService = sp.GetRequiredService<IHttpsClientHandlerService>();
            configureOptions(options, httpsClientHandlerService);

            HttpMessageHandler handler = httpsClientHandlerService.GetPlatformMessageHandler();
            httpClient = new HttpClient(handler);
           

            string? baseAddress = options.BaseAddress;

            DeviceType deviceType = DeviceInfo.DeviceType;
            if (!string.IsNullOrEmpty(baseAddress))
            {
                if (deviceType == DeviceType.Virtual)
                {
                    baseAddress = $"https://10.0.2.2:{options.PortNumber}"; // Android Virtual Device
                    httpClient.BaseAddress = new Uri(baseAddress);
                }
                else
                {
                    httpClient.BaseAddress = new Uri(baseAddress);
                }
            }
            else
            {
                try
                {
                    if (deviceType == DeviceType.Virtual)
                    {
                        baseAddress = $"https://10.0.2.2:{options.PortNumber}";
                    }
                    else if (deviceType == DeviceType.Physical)
                    {
                        if (DeviceInfo.Platform == DevicePlatform.Android)
                        {
                            baseAddress = options.RemoteAddress;
                        }
                        else if (DeviceInfo.Platform == DevicePlatform.iOS)
                        {
                            baseAddress = "YOUR_IOS_BASE_URL"; // Replace with iOS base URL
                        }
                        else if (DeviceInfo.Platform == DevicePlatform.WinUI)
                        {
                            baseAddress = $"https://localhost:{options.PortNumber}";
                        }

                        else
                        {
                            baseAddress = $"https://localhost:{options.PortNumber}";
                        }
                    }
                    else
                    {
                        baseAddress = $"https://localhost:{options.PortNumber}";
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                    baseAddress = "DEFAULT_BASE_URL";
                }
                httpClient.BaseAddress = new Uri(baseAddress);
            }

            httpClient.Timeout = options.Timeout;

            if (options.DefaultRequestHeaders?.Count > 0)
            {
                foreach (var header in options.DefaultRequestHeaders)
                {
                    httpClient.DefaultRequestHeaders.Add(header.Key, header.Value);
                }
            }

            if (!string.IsNullOrEmpty(options.BearerToken))
            {
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", options.BearerToken);
            }

            return httpClient;
        });
    }
    public static void AddEvanHttpClientTightCoupled(this IServiceCollection services, Action<HttpClientOptionstightCoupled> configureOptions)
    {
        services.AddScoped<HttpClient>(sp =>
        {
            HttpClient? httpClient = null;
            var options = new HttpClientOptionstightCoupled();
            configureOptions(options);
            HttpMessageHandler handler = options.httpsClientHandlerService!.GetPlatformMessageHandler();
            httpClient = new HttpClient(handler);


            string? baseAddress = options.BaseAddress;

            DeviceType deviceType = DeviceInfo.DeviceType;
            if (!string.IsNullOrEmpty(baseAddress))
            {
                if (deviceType == DeviceType.Virtual)
                {
                    baseAddress = $"https://10.0.2.2:{options.PortNumber}"; // Android Virtual Device
                    httpClient.BaseAddress = new Uri(baseAddress);
                }
                else
                {
                    httpClient.BaseAddress = new Uri(baseAddress);
                }
            }
            else
            {
                try
                {
                    if (deviceType == DeviceType.Virtual)
                    {
                        baseAddress = $"https://10.0.2.2:{options.PortNumber}";
                    }
                    else if (deviceType == DeviceType.Physical)
                    {
                        if (DeviceInfo.Platform == DevicePlatform.Android)
                        {
                            baseAddress = options.RemoteAddress;
                        }
                        else if (DeviceInfo.Platform == DevicePlatform.iOS)
                        {
                            baseAddress = "YOUR_IOS_BASE_URL"; // Replace with iOS base URL
                        }
                        else if (DeviceInfo.Platform == DevicePlatform.WinUI)
                        {
                            baseAddress = $"https://localhost:{options.PortNumber}";
                        }

                        else
                        {
                            baseAddress = $"https://localhost:{options.PortNumber}";
                        }
                    }
                    else
                    {
                        baseAddress = $"https://localhost:{options.PortNumber}";
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                    baseAddress = "DEFAULT_BASE_URL";
                }
                httpClient.BaseAddress = new Uri(baseAddress);
            }

            //httpClient.Timeout = options.Timeout;

            if (options.DefaultRequestHeaders?.Count > 0)
            {
                foreach (var header in options.DefaultRequestHeaders)
                {
                    httpClient.DefaultRequestHeaders.Add(header.Key, header.Value);
                }
            }

            if (!string.IsNullOrEmpty(options.BearerToken))
            {
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", options.BearerToken);
            }

            return httpClient;
        });
    }

}

In this case access to webapi succeded for both Android and Windows app.

I will explain this code in my future posts.

Join our newsletter!

Enter your email to receive our latest newsletter.

Don't worry, we don't spam

image

Related Articles

image
05 Feb 2024

Embed Youtube video in mdx file.

How to Embed Youtube video in mdx file -by Evan Dangol

image
05 Feb 2024

Get Free Linux Virtual Machine Forever in Cloud.

Comparasion of free tier AWS, Azure and Google cloud virtual machines -by Evan Dangol

image
05 Feb 2024

.Net MAUI, Local Web Api Access From Mobile Phones During Development

How to Access local web api from mobile device -by Evan Dangol