| MAUI-HttpClient-CRUD | MAUI-Cancellation-Token | |
REST API consumer in MAUI with error handling |
REST API consumer in MAUI with error handling so your app stays resilient when the network fails, the server responds incorrectly, or the user is offline.
Every API call should be protected:
public async Task<List<Post>> GetPostsAsync()
{
try
{
return await _httpClient.GetFromJsonAsync<List<Post>>("posts");
}
catch (HttpRequestException ex)
{
// Network or server unreachable
Console.WriteLine($"Network error: {ex.Message}");
return new List<Post>();
}
catch (NotSupportedException ex)
{
// Content type not supported
Console.WriteLine($"Unsupported content: {ex.Message}");
return new List<Post>();
}
catch (JsonException ex)
{
// Invalid JSON
Console.WriteLine($"Invalid JSON: {ex.Message}");
return new List<Post>();
}
}
This throws if the response isnβt 2xx:
var response = await _httpClient.PostAsJsonAsync("posts", newPost);
response.EnsureSuccessStatusCode();
Wrap in try/catch to handle gracefully.
Instead of just logging, surface messages in the UI:
public async Task<string> SafeCreatePostAsync(Post newPost)
{
try
{
var response = await _httpClient.PostAsJsonAsync("posts", newPost);
response.EnsureSuccessStatusCode();
return "Post created successfully!";
}
catch (Exception ex)
{
return $"Failed to create post: {ex.Message}";
}
}
Add a property for error messages:
private string _errorMessage;
public string ErrorMessage
{
get => _errorMessage;
set { _errorMessage = value; OnPropertyChanged(); }
}
public async Task LoadPosts()
{
try
{
var posts = await _api.GetPostsAsync();
Posts.Clear();
foreach (var post in posts)
Posts.Add(post);
ErrorMessage = string.Empty;
}
catch (Exception ex)
{
ErrorMessage = $"Could not load posts: {ex.Message}";
}
}
Show errors in your page:
<Label Text="{Binding ErrorMessage}" TextColor="Red" />
adding retry logic with exponential backoff makes your MAUI app much more resilient when consuming REST APIs. Instead of failing immediately on a network hiccup, the app will retry a few times with increasing delays before showing an error.
You can wrap your API calls in a retry loop:
private async Task<T> ExecuteWithRetry<T>(Func<Task<T>> operation, int maxRetries = 3)
{
int delay = 1000; // start with 1 second
for (int attempt = 1; attempt <= maxRetries; attempt++)
{
try
{
return await operation();
}
catch (HttpRequestException ex)
{
Console.WriteLine($"Attempt {attempt} failed: {ex.Message}");
if (attempt == maxRetries)
throw; // rethrow after last attempt
}
await Task.Delay(delay);
delay *= 2; // exponential backoff
}
throw new Exception("Operation failed after retries.");
}
Wrap your API calls:
public async Task<List<Post>> GetPostsAsync()
{
return await ExecuteWithRetry(async () =>
{
return await _httpClient.GetFromJsonAsync<List<Post>>("posts");
});
}
You can reuse ExecuteWithRetry for POST, PUT, and DELETE:
public async Task<Post> CreatePostAsync(Post newPost)
{
return await ExecuteWithRetry(async () =>
{
var response = await _httpClient.PostAsJsonAsync("posts", newPost);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<Post>();
});
}
In your ViewModel, catch the final exception and surface a message:
public async Task LoadPosts()
{
try
{
var posts = await _api.GetPostsAsync();
Posts.Clear();
foreach (var post in posts)
Posts.Add(post);
ErrorMessage = string.Empty;
}
catch (Exception ex)
{
ErrorMessage = $"Could not load posts after retries: {ex.Message}";
}
}
| MAUI-HttpClient-CRUD | MAUI-Cancellation-Token | |