Connecting WASM Apps to APIs: Overcoming CORS Challenges

Connecting WASM Apps to APIs: Overcoming CORS Challenges

Last week, I was diving into Uno Platform to understand its UI paradigms. What particularly caught my attention is Uno’s ability to render a webapp using WebAssembly (WASM). Having worked with WASM apps before, I’m all too familiar with the challenges of connecting to data sources and handling persistence within these applications.

My Previous WASM Struggles

About a year ago, I faced a significant challenge: connecting a desktop WebAssembly app to an old WCF webservice. Despite having the CORS settings correctly configured (or so I thought), I simply couldn’t establish a connection from the WASM app to the server. I spent days troubleshooting both the WCF service and another ASMX service, but both attempts failed. Eventually, I had to resort to webserver proxies to achieve my goal.

This experience left me somewhat traumatized by the mere mention of “connecting WASM with an API.” However, the time came to face this challenge again during my weekend experiments.

A Pleasant Surprise with Uno Platform

This weekend, I wanted to connect a XAF REST API to an Uno Platform client. To my surprise, it turned out to be incredibly straightforward. I successfully performed this procedure twice: once with a XAF REST API and once with the API included in the Uno app template. The ease of this integration was a refreshing change from my previous struggles.

Understanding CORS and Why It Matters for WASM Apps

To understand why my previous attempts failed and my recent ones succeeded, it’s important to grasp what CORS is and why it’s crucial for WebAssembly applications.

What is CORS?

CORS (Cross-Origin Resource Sharing) is a security feature implemented by web browsers that restricts web pages from making requests to a domain different from the one that served the original web page. It’s an HTTP-header based mechanism that allows a server to indicate which origins (domains, schemes, or ports) other than its own are permitted to load resources.

The Same-Origin Policy

Browsers enforce a security restriction called the “same-origin policy” which prevents a website from one origin from requesting resources from another origin. An origin consists of:

  • Protocol (HTTP, HTTPS)
  • Domain name
  • Port number

For example, if your website is hosted at https://myapp.com, it cannot make AJAX requests to https://myapi.com without the server explicitly allowing it through CORS.

Why CORS is Required for Blazor WebAssembly

Blazor WebAssembly (which uses similar principles to Uno Platform’s WASM implementation) is fundamentally different from Blazor Server in how it operates:

  1. Separate Deployment: Blazor WebAssembly apps are fully downloaded to the client’s browser and run entirely in the browser using WebAssembly. They’re typically hosted on a different server or domain than your API.
  2. Client-Side Execution: Since all code runs in the browser, when your Blazor WebAssembly app makes HTTP requests to your API, they’re treated as cross-origin requests if the API is hosted on a different domain, port, or protocol.
  3. Browser Security: Modern browsers block these cross-origin requests by default unless the server (your API) explicitly permits them via CORS headers.

Implementing CORS in Startup.cs

The solution to these CORS issues lies in properly configuring your server. In your Startup.cs file, you can configure CORS as follows:

public void ConfigureServices(IServiceCollection services) {
    services.AddCors(options => {
        options.AddPolicy("AllowBlazorApp",
            builder => {
                builder.WithOrigins("https://localhost:5000") // Replace with your Blazor app's URL
                       .AllowAnyHeader()
                       .AllowAnyMethod();
            });
    });

    // Other service configurations...
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
    // Other middleware configurations...

    app.UseCors("AllowBlazorApp");

    // Other middleware configurations...
}

Conclusion

My journey with connecting WebAssembly applications to APIs has had its ups and downs. What once seemed like an insurmountable challenge has now become much more manageable, especially with platforms like Uno that simplify the process. Understanding CORS and implementing it correctly is crucial for successful WASM-to-API communication.

If you’re working with WebAssembly applications and facing similar challenges, I hope my experience helps you avoid some of the pitfalls I encountered along the way.

About Us

YouTube

https://www.youtube.com/c/JocheOjedaXAFXAMARINC

Our sites

Let’s discuss your XAF

https://www.udemy.com/course/microsoft-ai-extensions/

Our free A.I courses on Udemy

Troubleshooting MAUI Android HTTP Client Issues: Native vs Managed Implementation

Troubleshooting MAUI Android HTTP Client Issues: Native vs Managed Implementation

When developing cross-platform mobile applications with .NET MAUI (or previously Xamarin), you may encounter situations where your app works perfectly with public APIs but fails when connecting to internal network services. These issues often stem from HTTP client implementation differences, certificate validation, and TLS compatibility. This article explores how to identify, troubleshoot, and resolve these common networking challenges.

Understanding HTTP Client Options in MAUI/Xamarin

In the MAUI/.NET ecosystem, developers have access to two primary HTTP client implementations:

1. Managed HttpClient (Microsoft’s implementation)

  • Cross-platform implementation built into .NET
  • Consistent behavior across different operating systems
  • May handle SSL/TLS differently than platform-native implementations
  • Uses the .NET certificate validation system

2. Native HttpClient (Android’s implementation)

  • Leverages the platform’s native networking stack
  • Typically offers better performance on the specific platform
  • Uses the device’s system certificate trust store
  • Follows platform-specific security policies and restrictions

Switching Between Native and Managed HttpClient

In MAUI Applications

MAUI provides a flexible handler registration system that lets you explicitly choose which implementation to use:

// In your MauiProgram.cs
public static MauiApp CreateMauiApp()
{
    var builder = MauiApp.CreateBuilder();
    builder
        .UseMauiApp<App>()
        .ConfigureMauiHandlers(handlers =>
        {
            // Use the managed implementation (Microsoft's .NET HttpClient)
            handlers.AddHandler(typeof(HttpClient), typeof(ManagedHttpMessageHandler));
            
            // OR use the native implementation (platform-specific)
            // handlers.AddHandler(typeof(HttpClient), typeof(PlatformHttpMessageHandler));
        });
    
    return builder.Build();
}

In Xamarin.Forms Legacy Applications

For Xamarin.Forms applications, set this in your platform-specific initialization code:

// In MainActivity.cs (Android) or AppDelegate.cs (iOS)
HttpClientHandler.UseNativePlatformHandler = false; // Use managed handler
// OR
HttpClientHandler.UseNativePlatformHandler = true;  // Use native handler

Creating Specific Client Instances

You can also explicitly create HttpClient instances with specific handlers when needed:

// Use the managed handler
var managedHandler = new HttpClientHandler();
var managedClient = new HttpClient(managedHandler);

// Use the native handler (with DependencyService in Xamarin)
var nativeHandler = DependencyService.Get<INativeHttpClientHandler>();
var nativeClient = new HttpClient(nativeHandler);

Using HttpClientFactory (Recommended for MAUI)

For better control, testability, and lifecycle management, consider using HttpClientFactory:

// In your MauiProgram.cs
builder.Services.AddHttpClient("ManagedClient", client => {
    client.BaseAddress = new Uri("https://your.api.url/");
})
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler());

// Then inject and use it in your services
public class MyApiService
{
    private readonly HttpClient _client;
    
    public MyApiService(IHttpClientFactory clientFactory)
    {
        _client = clientFactory.CreateClient("ManagedClient");
    }
}

Common Issues and Troubleshooting

1. Self-Signed Certificates

Internal APIs often use self-signed certificates that aren’t trusted by default. Here’s how to handle them:

// Option 1: Create a custom handler that bypasses certificate validation 
// (ONLY for development/testing environments)
var handler = new HttpClientHandler
{
    ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
};
var client = new HttpClient(handler);

For production environments, instead of bypassing validation:

  • Add your self-signed certificate to the Android trust store
  • Configure your app to trust specific certificates
  • Generate proper certificates from a trusted Certificate Authority

2. TLS Version Mismatches

Different Android versions support different TLS versions by default:

  • Android 4.1-4.4: TLS 1.0 by default
  • Android 5.0+: TLS 1.0, 1.1, 1.2
  • Android 10+: TLS 1.3 support

If your server requires a specific TLS version:

// Force specific TLS versions
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;

3. Network Configuration

Ensure your app has the proper permissions in the AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

For Android 9+ (API level 28+), configure network security:

<!-- Create a network_security_config.xml file in Resources/xml -->
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">your.internal.domain</domain>
    </domain-config>
</network-security-config>

Then reference it in your AndroidManifest.xml:

<application android:networkSecurityConfig="@xml/network_security_config">

Practical Troubleshooting Steps

  1. Test with both HTTP client implementationsSwitch between native and managed implementations to isolate whether the issue is specific to one implementation
  2. Test the API endpoint outside your appUse tools like Postman or curl on the same network
  3. Enable logging for network calls
    // Add this before making requests
    HttpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "YourApp/1.0");
  4. Capture and inspect network trafficUse Charles Proxy or Fiddler to inspect the actual requests/responses
  5. Check certificate information
    # On your development machine
    openssl s_client -connect your.internal.server:443 -showcerts
  6. Verify which implementation you’re using
    var client = new HttpClient();
    var handlerType = client.GetType().GetField("_handler", 
        System.Reflection.BindingFlags.Instance | 
        System.Reflection.BindingFlags.NonPublic)?.GetValue(client);
    
    Console.WriteLine($"Using handler: {handlerType?.GetType().FullName}");
  7. Debug specific errors
    • For Java.IO.IOException: “Trust anchor for certification path not found” – this means your app doesn’t trust the certificate
    • For HttpRequestException with “The SSL connection could not be established” – likely a TLS version mismatch

Conclusion

When your MAUI Android app connects successfully to public APIs but fails with internal network services, the issue often lies with HTTP client implementation differences, certificate validation, or TLS compatibility. By systematically switching between native and managed HTTP clients and applying the troubleshooting techniques outlined above, you can identify and resolve these networking challenges.

Remember that each implementation has its advantages – the native implementation typically offers better performance and follows platform-specific security policies, while the managed implementation provides more consistent cross-platform behavior. Choose the one that best fits your specific requirements and security considerations.

About Us

YouTube

https://www.youtube.com/c/JocheOjedaXAFXAMARINC

Our sites

Let’s discuss your XAF

https://www.udemy.com/course/microsoft-ai-extensions/

Our free A.I courses on Udemy