Table of Contents
Introduction
Modern applications frequently need to communicate with remote services over the network.
.NET MAUI provides robust support for various networking scenarios, allowing you to build
connected experiences across different platforms. This tutorial explores common networking
tasks in .NET MAUI, focusing on HttpClient
and other essential tools.
Efficient network communication is crucial for user experience. Understanding how to fetch, send data, and handle potential issues is key to building reliable MAUI applications.
Using HttpClient
The System.Net.Http.HttpClient
class is the primary tool for making
HTTP requests in .NET. It's designed for asynchronous operations, making it efficient
for UI applications where blocking the main thread is undesirable.
Getting Data
To retrieve data from a web API, you can use the GetAsync
method. This
method returns a HttpResponseMessage
, which contains the server's response.
using System;
using System.Net.Http;
using System.Threading.Tasks;
public class DataService
{
private readonly HttpClient _httpClient;
public DataService()
{
// It's recommended to reuse a single HttpClient instance
_httpClient = new HttpClient();
// Set base address if needed
_httpClient.BaseAddress = new Uri("https://api.example.com/");
}
public async Task<string> GetDataAsync(string endpoint)
{
try
{
var response = await _httpClient.GetAsync(endpoint);
response.EnsureSuccessStatusCode(); // Throws an exception if the status code is not success
var content = await response.Content.ReadAsStringAsync();
return content;
}
catch (HttpRequestException e)
{
Console.WriteLine($"Request error: {e.Message}");
return null; // Or rethrow, or return a specific error object
}
}
}
After getting the response, you typically read the content as a string using
ReadAsStringAsync
or deserialize it into a .NET object, often using
a JSON deserializer like System.Text.Json
.
// Example with JSON deserialization
using System.Text.Json;
// ... inside GetDataAsync or another method
var jsonString = await response.Content.ReadAsStringAsync();
var data = JsonSerializer.Deserialize<MyDataModel>(jsonString);
Sending Data
To send data to a web API (e.g., creating a new resource), you can use methods
like PostAsync
, PutAsync
, or DeleteAsync
.
You'll typically send data as JSON or other content types.
using System.Text;
using System.Text.Json;
public async Task<bool> PostDataAsync<T>(string endpoint, T data)
{
try
{
var json = JsonSerializer.Serialize(data);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync(endpoint, content);
response.EnsureSuccessStatusCode();
return true;
}
catch (HttpRequestException e)
{
Console.WriteLine($"Post error: {e.Message}");
return false;
}
}
Remember to set the correct content type header (e.g., application/json
)
for your request.
Working with WebSockets
For real-time, bidirectional communication, WebSockets are an excellent choice.
.NET MAUI applications can leverage the System.Net.WebSockets.ClientWebSocket
class for this purpose.
WebSockets provide a persistent connection, ideal for chat applications, live updates, and gaming.
using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
public class WebSocketService
{
private ClientWebSocket _webSocket;
private CancellationTokenSource _cts;
public async Task ConnectAsync(string uri)
{
_webSocket = new ClientWebSocket();
_cts = new CancellationTokenSource();
await _webSocket.ConnectAsync(new Uri(uri), _cts.Token);
Console.WriteLine($"WebSocket connected: {_webSocket.State}");
// Start listening for messages in a background task
_ = ReceiveMessagesAsync();
}
private async Task ReceiveMessagesAsync()
{
var buffer = new byte[1024 * 4]; // 4KB buffer
while (_webSocket.State == WebSocketState.Open && !_cts.IsCancellationRequested)
{
try
{
var result = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), _cts.Token);
if (result.MessageType == WebSocketMessageType.Text)
{
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
Console.WriteLine($"Received: {message}");
// Process the message here (e.g., update UI)
}
else if (result.MessageType == WebSocketMessageType.Close)
{
Console.WriteLine("WebSocket close message received.");
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Client disconnecting", _cts.Token);
break;
}
}
catch (WebSocketException ex)
{
Console.WriteLine($"WebSocket receive error: {ex.Message}");
break;
}
catch (OperationCanceledException)
{
Console.WriteLine("WebSocket receive operation cancelled.");
break;
}
}
Console.WriteLine($"WebSocket connection closed or interrupted. State: {_webSocket.State}");
}
public async Task SendMessageAsync(string message)
{
if (_webSocket.State == WebSocketState.Open)
{
try
{
var bytes = Encoding.UTF8.GetBytes(message);
await _webSocket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true, _cts.Token);
Console.WriteLine($"Sent: {message}");
}
catch (WebSocketException ex)
{
Console.WriteLine($"WebSocket send error: {ex.Message}");
}
catch (OperationCanceledException)
{
Console.WriteLine("WebSocket send operation cancelled.");
}
}
else
{
Console.WriteLine("WebSocket is not open. Cannot send message.");
}
}
public async Task DisconnectAsync()
{
if (_webSocket != null && _webSocket.State != WebSocketState.Closed && _webSocket.State != WebSocketState.Aborted)
{
_cts?.Cancel();
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Client disconnecting", CancellationToken.None);
_webSocket.Dispose();
_webSocket = null;
Console.WriteLine("WebSocket disconnected.");
}
}
}
Remember to handle disconnections and potential errors gracefully.
Managing Network Access
It's good practice to check for network connectivity before attempting network operations. .NET MAUI doesn't provide a direct cross-platform API for this, but you can use platform-specific APIs or third-party libraries.
A common approach is to make a very small, quick request to a known reliable endpoint (like a DNS server or a small test API) and see if it succeeds.
Alternatively, you can use the Connectivity
API from the
Microsoft.Maui.Essentials
package (now integrated into .NET MAUI).
using Microsoft.Maui.Connectivity;
public bool IsInternetAvailable()
{
return Connectivity.NetworkAccess == NetworkAccess.Internet;
}
public void SubscribeToConnectivityChanges()
{
Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged;
}
public void UnsubscribeFromConnectivityChanges()
{
Connectivity.ConnectivityChanged -= Connectivity_ConnectivityChanged;
}
private void Connectivity_ConnectivityChanged(object sender, ConnectivityChangedEventArgs e)
{
if (e.NetworkAccess == NetworkAccess.Internet)
{
Console.WriteLine("Internet is now available.");
// Potentially re-initiate pending network operations
}
else
{
Console.WriteLine("Internet is no longer available.");
// Handle disconnection (e.g., cancel ongoing requests)
}
}
Subscribing to ConnectivityChanged
allows your application to react
dynamically to network status changes.
Handling Network Errors
Network operations are inherently prone to failures. Common errors include:
HttpRequestException
: For general HTTP request failures.WebException
: Often wrapped byHttpRequestException
.TaskCanceledException
: If the request times out or is explicitly cancelled.SocketException
: For lower-level network issues.JsonException
: If JSON parsing fails.
Always wrap your network calls in try-catch
blocks. Differentiate between
server errors (e.g., 404, 500) indicated by response.EnsureSuccessStatusCode()
and network connectivity issues.
Tip: Timeouts
Configure appropriate timeouts for your HttpClient
to prevent requests
from hanging indefinitely.
_httpClient.Timeout = TimeSpan.FromSeconds(30); // Set a default timeout
// Or for specific requests:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15)); // Timeout in 15 seconds
var response = await _httpClient.GetAsync(endpoint, cts.Token);
Best Practices
- Reuse HttpClient: Create a single instance of
HttpClient
and reuse it throughout your application's lifetime. Creating a new instance for every request can lead to socket exhaustion. - Asynchronous Operations: Always use
async
andawait
for network operations to keep your UI responsive. - Error Handling: Implement robust error handling and provide meaningful feedback to the user.
- Timeouts: Set reasonable timeouts to prevent long-running requests.
- Serialization: Use efficient JSON serializers like
System.Text.Json
. - Background Threads: For long-running or network-intensive operations that don't need immediate UI updates, consider using
Task.Run
orIBackgroundService
. - Security: Use HTTPS for all communications. Consider certificate validation and security best practices.
- Network Awareness: Be mindful of network availability and provide graceful degradation when connectivity is lost.