/ .Net MAUI, Local Web Api Access From Mob...
By Evan Dangol
05 Feb 2024
09
35
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 As you can see the api call succeded for windows app but not for android app.
Lets modify the code to make it work in android
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
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.
Enter your email to receive our latest newsletter.
Don't worry, we don't spam
zenstack
machine-learning
Explore the process of deploying a serverless API with Vercel, leveraging Zenstack for Prisma integration, TypeScript for enhanced development, and Neon's complimentary PostgreSQL database for your project. -by Evan Dangol
Unveiling Machine Learning's Power- Detecting Toxic Comments with C# -by Evan Dangol
It's unlikely that gRPC will entirely replace REST (Representational State Transfer) API in the near future, as both technologies have their own strengths and are suitable for different use cases.gRPC (Google Remote Procedure Call) is a high-performance...