maui-rest-api

Consuming REST APIs in .NET MAUI

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "maui-rest-api" with this command: npx skills add davidortinau/maui-skills/davidortinau-maui-skills-maui-rest-api

Consuming REST APIs in .NET MAUI

HttpClient & JSON setup

Always configure a shared JsonSerializerOptions with camel-case naming:

private static readonly JsonSerializerOptions _jsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, PropertyNameCaseInsensitive = true };

DI registration

Register HttpClient as a singleton or use IHttpClientFactory . Set BaseAddress once:

// MauiProgram.cs builder.Services.AddSingleton(sp => new HttpClient { BaseAddress = new Uri("https://api.example.com") }); builder.Services.AddSingleton<IMyApiService, MyApiService>();

For more control, use the factory pattern:

builder.Services.AddHttpClient<IMyApiService, MyApiService>(client => { client.BaseAddress = new Uri("https://api.example.com"); });

Service interface + implementation

Define a clean interface for each API resource:

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); }

Implement the interface, injecting HttpClient :

public class MyApiService : IMyApiService { private readonly HttpClient _httpClient;

private static readonly JsonSerializerOptions _jsonOptions = new()
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    PropertyNameCaseInsensitive = true
};

public MyApiService(HttpClient httpClient)
{
    _httpClient = httpClient;
}

CRUD operations

GET (list)

public async Task&#x3C;List&#x3C;Item>> GetItemsAsync()
{
    var response = await _httpClient.GetAsync("api/items");
    response.EnsureSuccessStatusCode();
    var content = await response.Content.ReadAsStringAsync();
    return JsonSerializer.Deserialize&#x3C;List&#x3C;Item>>(content, _jsonOptions) ?? [];
}

GET (single)

public async Task&#x3C;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&#x3C;Item>(content, _jsonOptions);
}

POST (create)

public async Task&#x3C;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&#x3C;Item>(responseBody, _jsonOptions);
}

PUT (update)

public async Task&#x3C;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;
}

DELETE

public async Task&#x3C;bool> DeleteItemAsync(int id)
{
    var response = await _httpClient.DeleteAsync($"api/items/{id}");
    return response.IsSuccessStatusCode;
}

}

Error handling

Check IsSuccessStatusCode or call EnsureSuccessStatusCode() depending on the scenario:

  • Use EnsureSuccessStatusCode() when failure is unexpected (throws HttpRequestException ).

  • Use IsSuccessStatusCode when you need to branch on specific status codes.

Wrap calls in try/catch for network-level failures:

try { var items = await _apiService.GetItemsAsync(); } catch (HttpRequestException ex) { // Network error or non-success status code } catch (JsonException ex) { // Deserialization failure }

Common HTTP response codes

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

Platform-specific: local development with HTTP clear-text

Emulators and simulators block clear-text HTTP by default. When targeting a local dev server over http:// :

Android — add a network security config in 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>

Reference it in AndroidManifest.xml :

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

iOS / Mac Catalyst — add an NSAppTransportSecurity exception in Info.plist :

<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsLocalNetworking</key> <true/> </dict>

Note: Android emulators reach the host machine at 10.0.2.2 . iOS simulators use localhost directly.

Rules

  • Always use async/await ; never block with .Result or .Wait() .

  • Register HttpClient once (singleton or factory); do not create per-request instances.

  • Set BaseAddress in DI; use relative URIs in service methods.

  • Apply JsonSerializerOptions consistently with CamelCase naming policy.

  • Check IsSuccessStatusCode before deserializing response bodies.

  • Wrap API calls in try/catch for HttpRequestException and JsonException .

  • Use the service interface pattern so view models depend on abstractions.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

maui-performance

No summary provided by upstream source.

Repository SourceNeeds Review
General

maui-data-binding

No summary provided by upstream source.

Repository SourceNeeds Review
General

maui-permissions

No summary provided by upstream source.

Repository SourceNeeds Review
General

maui-dependency-injection

No summary provided by upstream source.

Repository SourceNeeds Review