HttpClient og Refit i .NET MAUI: Type-Safe REST API Integration (2026)
Sådan opsætter du type-safe REST API-integration i .NET MAUI med IHttpClientFactory, Refit og Polly. Fra MauiProgram til authenticated calls, med produktionsklare kodeeksempler jeg selv har shippet.
Type-safe REST API-integration i .NET MAUI opnås bedst ved at kombinere IHttpClientFactory til livscyklus-styring, Refit til deklarative klient-interfaces og Polly til retry og circuit breaker. Kombinationen fjerner boilerplate, isolerer HTTP-detaljer bag et interface og gør din netværkskode testbar. I denne guide viser jeg dig hele opsætningen fra MauiProgram.cs til authenticated calls, med kode jeg selv har shippet i tre produktions-apps.
Brug IHttpClientFactory fra Microsoft.Extensions.Http, aldrig new HttpClient() direkte i view models.
Refit genererer type-safe klient-implementeringer fra et interface, hvilket eliminerer manuel JSON- og URL-håndtering.
Polly tilføjer retry, timeout og circuit breaker via Microsoft.Extensions.Http.Resilience uden at ændre din API-kode.
DelegatingHandlers er det rigtige sted at tilføje Bearer-tokens, logging og korrelations-ID'er (ikke i hver enkelt kald).
iOS kræver eksplicit NSAppTransportSecurity-konfiguration for lokale HTTP-endpoints under udvikling.
Kombinér IConnectivity med en outbox-tabel i SQLite for at understøtte kortvarige netværksafbrydelser.
Hvorfor du aldrig bør bruge new HttpClient() i MAUI
Det klassiske fejltrin i mobile .NET-apps er at instantiere new HttpClient() pr. kald. Klassen implementerer IDisposable, så udviklere pakker det pænt i using, og udmatter derefter socket-poolen efter få tusinde forespørgsler. På Android manifesterer det sig som SocketException under load. På iOS ses det som mystiske timeouts. Jeg fangede selv den her bug i en produktions-app, hvor QA aldrig så den, fordi den kun optrådte efter 30+ minutters brug. Ikke sjovt at debugge fra release notes.
IHttpClientFactory løser tre problemer på én gang: den genbruger HttpMessageHandler-instanser (som er de dyre objekter), den roterer handlers hvert andet minut for at respektere DNS-ændringer, og den lader dig konfigurere navngivne eller typede klienter centralt. I .NET MAUI er factory tilgængelig via Microsoft.Extensions.Http-pakken og integrerer direkte med den indbyggede DI-container. Se Microsofts officielle vejledning om IHttpClientFactory for baggrund.
En anden fordel: factory'en samarbejder med Polly, så retry- og circuit-breaker-politikker anvendes automatisk på hver forespørgsel, uden at forurene din forretningslogik. Vi kobler det sammen i næste sektion.
Opsætning af IHttpClientFactory i MauiProgram
Så, lad os starte med at tilføje pakkerne. I .NET 9 og nyere er Microsoft.Extensions.Http allerede med som transitiv afhængighed, men du skal eksplicit tilføje resilience- og Refit-pakkerne:
Konfigurér derefter en navngiven klient i MauiProgram.cs. Personligt foretrækker jeg navngivne klienter over typede, fordi de er nemmere at ombryde med Refit senere:
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
// Base HttpClient konfiguration
builder.Services.AddHttpClient("MobileApi", client =>
{
client.BaseAddress = new Uri("https://api.example.com/v1/");
client.Timeout = TimeSpan.FromSeconds(30);
client.DefaultRequestHeaders.Add("User-Agent", "MobileTechLead/1.0");
});
return builder.Build();
}
Bemærk at BaseAddress ender med en skråstreg. Uden den vil relative URI'er i Refit blive tolket forkert (og du får en time i debuggeren gratis). Timeout på 30 sekunder er min standard for mobile netværk; hvis dit API kører 3G-scenarier, kan du overveje 60 sekunder, men vær opmærksom på, at brugere opgiver længe før det.
Refit: type-safe klienter uden boilerplate
Refit lader dig deklarere API-endpoints som et interface med attributter, og genererer implementeringen ved kompilering. Det fjerner manuel URL-formatering, JSON-serialisering og fejlkontrol fra din view model-kode. Definér først din model og dit interface:
public record Product(int Id, string Name, decimal Price, string ImageUrl);
public record ProductPage(IReadOnlyList<Product> Items, int Total, int Page);
public interface IProductApi
{
[Get("/products")]
Task<ProductPage> GetProductsAsync(
[Query] int page = 1,
[Query] int pageSize = 20,
CancellationToken ct = default);
[Get("/products/{id}")]
Task<Product> GetProductAsync(int id, CancellationToken ct = default);
[Post("/products")]
Task<Product> CreateProductAsync(
[Body] Product product,
CancellationToken ct = default);
[Multipart]
[Post("/products/{id}/image")]
Task UploadImageAsync(
int id,
[AliasAs("file")] StreamPart image,
CancellationToken ct = default);
}
Registrér Refit-klienten sammen med din navngivne HttpClient:
Din view model modtager nu IProductApi via constructor injection, helt fri for HTTP-detaljer, hvilket gør den trivielt testbar. Se Refit-projektets officielle dokumentation for fulde attribut-oversigter, herunder [Header], [QueryUriFormat] og typede fejl-modeller via ApiException. Hvis du har brug for at genopfriske dependency injection-mønstre først, dækker vores guide til dependency injection i .NET MAUI alle service-livscyklusser i detaljer.
Resilience med Polly og retry-strategier
Mobile netværk fejler konstant: en tunnel, en overfyldt celletårn, en Wi-Fi-overgang. En stabil API-klient antager fejl som normen og reagerer med eksponentiel back-off. Microsoft.Extensions.Http.Resilience pakker Polly v8 med sensible standarder, som du kan tilføje til enhver HttpClient med én linje:
Standardhandler'en kombinerer fem strategier: timeout pr. forsøg, samlet timeout, retry med jitter, circuit breaker og rate limiter. Jitter er kritisk i mobile scenarier. Uden den vil hundredvis af klienter forsøge igen samtidig efter en netværksafbrydelse og forværre problemet.
Til produktion anbefaler jeg at logge circuit-breaker-transitioner via ILogger. Det er dit tidligste signal om et nedstrøms API-outage. Polly's telemetri integrerer med Microsoft.Extensions.Diagnostics, så du får dette gratis, hvis du allerede har opsat structured logging.
DelegatingHandler til authentication og logging
Bearer-tokens hører ikke hjemme i hvert enkelt Refit-kald. Brug en DelegatingHandler, som opsnapper alle forespørgsler for din klient og tilføjer headers centralt. Dette er også det rette sted til at håndtere token-fornyelse på 401-responses:
public sealed class AuthHeaderHandler : DelegatingHandler
{
private readonly ITokenProvider _tokens;
public AuthHeaderHandler(ITokenProvider tokens)
{
_tokens = tokens;
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken ct)
{
var token = await _tokens.GetAccessTokenAsync(ct);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await base.SendAsync(request, ct);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
token = await _tokens.RefreshAsync(ct);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
response.Dispose();
response = await base.SendAsync(request, ct);
}
return response;
}
}
Tokens bør gemmes i SecureStorage, aldrig i Preferences eller ren tekst. Vores guide til sikkerhed i .NET MAUI dækker dette i detaljer, inklusive biometrisk beskyttelse af refresh tokens.
iOS- og Android-platformkonfiguration
Både iOS og Android har platform-specifikke krav til HTTP-trafik i 2026. På iOS blokerer App Transport Security (ATS) al HTTP-trafik uden TLS 1.2+. Til produktion er det præcis hvad du vil have, men til lokal udvikling mod http://localhost:5000 skal du tilføje en undtagelse i Platforms/iOS/Info.plist:
På Android skal du eksplicit tillade cleartext-trafik pr. domæne via en network security config. Opret Platforms/Android/Resources/xml/network_security_config.xml:
Reference filen fra AndroidManifest.xml via <application android:networkSecurityConfig="@xml/network_security_config">. Android-emulatoren bruger 10.0.2.2 som alias for host-maskinens localhost, en detalje der har kostet mig flere timer over årene. Husk det, seriøst.
På Android bør du også opgradere til SocketsHttpHandler (default fra .NET 8) i stedet for den ældre Xamarin-handler. Det giver HTTP/2, connection pooling og bedre timeout-håndtering. .NET MAUI bruger den korrekte handler by default siden 8.0, men hvis du migrerer fra Xamarin.Forms, bør du eksplicit sætte <UseNativeHttpHandler>false</UseNativeHttpHandler> i din .csproj.
Håndtering af offline-tilstand og connectivity
En mobile-first API-klient skal reagere på ændringer i netværkstilstand. IConnectivity fra Microsoft.Maui.Essentials giver både en synkron tilstand og en event:
Dette mønster (stale-while-revalidate) er min standard. Vis cached data hurtigt, opdatér i baggrunden, og fald tilbage til cache ved API-fejl. Til skrive-operationer bruger jeg en outbox-tabel i SQLite, hvor pending mutations opbevares indtil netværket vender tilbage. Vores guide til SQLite og EF Core i .NET MAUI viser hvordan du opsætter den lokale store.
Test af dine API-klienter
Fordi din view model afhænger af IProductApi (interface) og ikke HttpClient (concrete), er unit tests trivielle:
[Fact]
public async Task LoadProducts_populates_collection()
{
var api = Substitute.For<IProductApi>();
api.GetProductsAsync(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<CancellationToken>())
.Returns(new ProductPage(new[] { new Product(1, "Test", 99m, "img.jpg") }, 1, 1));
var vm = new ProductListViewModel(api);
await vm.LoadCommand.ExecuteAsync(null);
Assert.Single(vm.Products);
}
Til integration tests mod en fake HTTP-server bruger jeg WireMock.Net, som lader dig scripte responses inklusive fejl-scenarier. Det er langt mere realistisk end at mocke Refit-interfacet, fordi du også får dine DelegatingHandlers og Polly-strategier med i testen. Vores komplette teststrategi for .NET MAUI dækker både unit-, integration- og UI-tests. Se også Microsofts vejledning om test af IHttpClientFactory.
Ofte stillede spørgsmål
Hvad er forskellen mellem HttpClient og Refit i .NET MAUI?
HttpClient er den lav-niveau .NET-klasse til at sende HTTP-forespørgsler manuelt. Refit er et bibliotek der genererer en type-safe klient-implementering fra et interface, så du deklarerer endpoints med attributter i stedet for at bygge HttpRequestMessage-objekter selv. Refit bruger stadig HttpClient internt, så det er egentlig et abstraktionslag ovenpå.
Kan man bruge IHttpClientFactory i .NET MAUI?
Ja, IHttpClientFactory er fuldt understøttet i .NET MAUI via Microsoft.Extensions.Http-pakken. Registrér dine klienter i MauiProgram.cs med builder.Services.AddHttpClient(), og inject enten IHttpClientFactory eller en typed client i dine services.
Hvordan tilføjer man authentication headers til alle API-kald?
Opret en klasse der arver fra DelegatingHandler, overskriv SendAsync og tilføj request.Headers.Authorization. Registrér den derefter på din HttpClient med .AddHttpMessageHandler<YourHandler>(). Dette centraliserer auth-logik og lader dig håndtere 401-responses med token-fornyelse ét sted.
Hvordan implementerer man retry-logik med Polly i .NET MAUI?
Installér Microsoft.Extensions.Http.Resilience, og tilføj .AddStandardResilienceHandler() til din klient-registrering. Standardhandler'en giver retry med eksponentiel back-off og jitter, timeouts og circuit breaker, og alt kan tilpasses via en options-callback uden at ændre din API-kode.
Skal man bruge System.Text.Json eller Newtonsoft.Json med Refit?
Brug System.Text.Json. Det er default i Refit 8, betydeligt hurtigere, og fylder mindre i din app. Newtonsoft.Json er kun nødvendigt hvis du bruger avancerede features som custom JsonConverter-hierarkier eller [JsonExtensionData], som System.Text.Json først for nyligt har fået fuld paritet på.
Komplet 2026-guide til deep linking i .NET MAUI: opsæt Android App Links med assetlinks.json, iOS Universal Links med AASA, og rout via Shell, inkl. migration efter Firebase Dynamic Links.
Lær at bygge en hurtig, sikker og vedligeholdbar lokal database i .NET MAUI med enten sqlite-net-pcl eller Entity Framework Core 9. Inkluderer setup, repository-mønster, migrationer, SQLCipher-kryptering og performance-tips med fungerende C#-eksempler.