Loading...
Loading...
Guidance for consuming REST APIs in .NET MAUI apps. Covers HttpClient setup with System.Text.Json, DI registration, service interface/implementation pattern, full CRUD operations (GET, POST, PUT, DELETE), error handling, platform-specific clear-text traffic configuration, and async/await best practices. Use when adding API calls, creating data services, or wiring up HttpClient in a MAUI project.
npx skill4agent add davidortinau/maui-skills maui-rest-apiJsonSerializerOptionsprivate static readonly JsonSerializerOptions _jsonOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true
};HttpClientIHttpClientFactoryBaseAddress// MauiProgram.cs
builder.Services.AddSingleton(sp => new HttpClient
{
BaseAddress = new Uri("https://api.example.com")
});
builder.Services.AddSingleton<IMyApiService, MyApiService>();builder.Services.AddHttpClient<IMyApiService, MyApiService>(client =>
{
client.BaseAddress = new Uri("https://api.example.com");
});public interface IMyApiService
{
Task<List<Item>> GetItemsAsync();
Task<Item?> GetItemAsync(int id);
Task<Item?> CreateItemAsync(Item item);
Task<bool> UpdateItemAsync(Item item);
Task<bool> DeleteItemAsync(int id);
}HttpClientpublic class MyApiService : IMyApiService
{
private readonly HttpClient _httpClient;
private static readonly JsonSerializerOptions _jsonOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNameCaseInsensitive = true
};
public MyApiService(HttpClient httpClient)
{
_httpClient = httpClient;
} public async Task<List<Item>> GetItemsAsync()
{
var response = await _httpClient.GetAsync("api/items");
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<List<Item>>(content, _jsonOptions) ?? [];
} public async Task<Item?> GetItemAsync(int id)
{
var response = await _httpClient.GetAsync($"api/items/{id}");
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
return null;
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<Item>(content, _jsonOptions);
} public async Task<Item?> CreateItemAsync(Item item)
{
var json = JsonSerializer.Serialize(item, _jsonOptions);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync("api/items", content);
if (!response.IsSuccessStatusCode)
return null;
var responseBody = await response.Content.ReadAsStringAsync();
return JsonSerializer.Deserialize<Item>(responseBody, _jsonOptions);
} public async Task<bool> UpdateItemAsync(Item item)
{
var json = JsonSerializer.Serialize(item, _jsonOptions);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PutAsync($"api/items/{item.Id}", content);
return response.IsSuccessStatusCode;
} public async Task<bool> DeleteItemAsync(int id)
{
var response = await _httpClient.DeleteAsync($"api/items/{id}");
return response.IsSuccessStatusCode;
}
}IsSuccessStatusCodeEnsureSuccessStatusCode()EnsureSuccessStatusCode()HttpRequestExceptionIsSuccessStatusCodetry
{
var items = await _apiService.GetItemsAsync();
}
catch (HttpRequestException ex)
{
// Network error or non-success status code
}
catch (JsonException ex)
{
// Deserialization failure
}| Code | Meaning | Typical use |
|---|---|---|
| 200 | OK | Successful GET or PUT |
| 201 | Created | Successful POST (resource created) |
| 204 | No Content | Successful DELETE or PUT (no body) |
| 400 | Bad Request | Validation error in request body |
| 404 | Not Found | Resource does not exist |
| 409 | Conflict | Duplicate or state conflict |
http://Platforms/Android/Resources/xml/network_security_config.xml<?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>AndroidManifest.xml<application android:networkSecurityConfig="@xml/network_security_config" ... />NSAppTransportSecurityInfo.plist<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>Note: Android emulators reach the host machine at. iOS simulators use10.0.2.2directly.localhost
async/await.Result.Wait()HttpClientBaseAddressJsonSerializerOptionsCamelCaseIsSuccessStatusCodeHttpRequestExceptionJsonException