Skip to main content

Nethereum.Util.Rest

NuGet: Nethereum.Util.Rest | Source: src/Nethereum.Util.Rest/

Nethereum.Util.Rest

Generic REST client abstraction and implementation for HTTP API interactions with JSON serialization support.

Overview

Nethereum.Util.Rest provides a lightweight wrapper around HttpClient for making REST API calls with automatic JSON serialization/deserialization. It's designed to simplify HTTP communication in Nethereum components and user applications.

Key Features:

  • Generic interface for RESTful HTTP operations (GET, POST, PUT, DELETE)
  • Automatic JSON serialization/deserialization using System.Text.Json (.NET 8+) or Newtonsoft.Json (older versions)
  • Support for custom headers including Bearer token authentication
  • Multipart form data support for file uploads
  • HttpClient extension methods with built-in auth support
  • Dependency injection friendly with IRestHttpHelper interface
  • Used internally by Nethereum.Beaconchain, Nethereum.DataServices, and other packages

Installation

dotnet add package Nethereum.Util.Rest

Or via Package Manager Console:

Install-Package Nethereum.Util.Rest

Dependencies

External:

  • System.Text.Json (.NET 8+) or Newtonsoft.Json (older frameworks)
  • System.Net.Http.HttpClient

Nethereum:

  • None (this is a foundational utility package)

Key Concepts

IRestHttpHelper Interface

The IRestHttpHelper interface provides a generic abstraction for REST operations:

public interface IRestHttpHelper
{
Task<T> GetAsync<T>(string path, Dictionary<string, string> headers = null);
Task<TResponse> PostAsync<TResponse, TRequest>(string path, TRequest request, Dictionary<string, string> headers = null);
Task<TResponse> PutAsync<TResponse, TRequest>(string path, TRequest request, Dictionary<string, string> headers = null);
Task DeleteAsync(string path, Dictionary<string, string> headers = null);
Task<TResponse> PostMultipartAsync<TResponse>(string path, MultipartFormDataRequest request, Dictionary<string, string> headers = null);
}

RestHttpHelper Implementation

Concrete implementation that wraps HttpClient and handles JSON serialization automatically.

HttpClient Extensions

Extension methods for HttpClient with built-in Bearer token authentication support (NET 5.0+).

JSON Serialization Strategy

  • .NET 8+: Uses System.Text.Json for better performance
  • Older versions: Uses Newtonsoft.Json for compatibility
  • Transparent to API consumers - same interface across all frameworks

Quick Start

using Nethereum.Util.Rest;
using System.Net.Http;

// Create REST helper
var httpClient = new HttpClient();
var restHelper = new RestHttpHelper(httpClient);

// Make a GET request
var data = await restHelper.GetAsync<MyDataModel>("https://api.example.com/data");

// Make a POST request
var response = await restHelper.PostAsync<ResponseModel, RequestModel>(
"https://api.example.com/submit",
new RequestModel { Value = "test" }
);

Usage Examples

Example 1: Basic GET Request with Custom Headers

using Nethereum.Util.Rest;
using System.Collections.Generic;
using System.Net.Http;

// Create REST helper
var restHelper = new RestHttpHelper(new HttpClient());

// Define custom headers
var headers = new Dictionary<string, string>
{
{ "accept", "application/json" }
};

// Make GET request
var result = await restHelper.GetAsync<ApiResponse>(
"https://api.example.com/v1/data",
headers
);

public class ApiResponse
{
public string Status { get; set; }
public object Data { get; set; }
}

Example 2: Dependency Injection Pattern (Real Example from BeaconApiClient)

using Nethereum.Util.Rest;
using System.Net.Http;

// Service class using dependency injection
public class BeaconApiClient
{
private readonly IRestHttpHelper _restHelper;
public string BaseUrl { get; }

// Constructor with HttpClient
public BeaconApiClient(string baseUrl, HttpClient httpClient = null)
{
BaseUrl = baseUrl.TrimEnd('/');
_restHelper = new RestHttpHelper(httpClient ?? new HttpClient());
}

// Constructor with IRestHttpHelper (for testing)
public BeaconApiClient(string baseUrl, IRestHttpHelper restHelper)
{
BaseUrl = baseUrl.TrimEnd('/');
_restHelper = restHelper;
}

// Example method
public async Task<BootstrapResponse> GetBootstrapAsync(string blockRoot)
{
var url = $"{BaseUrl}/eth/v1/beacon/light_client/bootstrap/{blockRoot}";
return await _restHelper.GetAsync<BootstrapResponse>(url);
}
}

Example 3: 4byte.directory API Integration (Real Example)

using Nethereum.Util.Rest;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;

public class FourByteDirectoryService
{
public const string BaseUrl = "https://www.4byte.directory";
private IRestHttpHelper _restHttpHelper;

public FourByteDirectoryService()
{
_restHttpHelper = new RestHttpHelper(new HttpClient());
}

public FourByteDirectoryService(IRestHttpHelper restHttpHelper)
{
_restHttpHelper = restHttpHelper;
}

// Get function signature by hex signature (e.g., "0xa9059cbb")
public Task<FourByteDirectoryResponse> GetFunctionSignatureByHexSignatureAsync(
string hexSignature)
{
var url = $"{BaseUrl}/api/v1/signatures/?hex_signature={hexSignature}";
return GetDataAsync<FourByteDirectoryResponse>(url);
}

// Get function signature by text (e.g., "transfer(address,uint256)")
public Task<FourByteDirectoryResponse> GetFunctionSignatureByTextSignatureAsync(
string textSignature)
{
var url = $"{BaseUrl}/api/v1/signatures/?text_signature={textSignature}";
return GetDataAsync<FourByteDirectoryResponse>(url);
}

private async Task<T> GetDataAsync<T>(string url)
{
var headers = new Dictionary<string, string>
{
{ "accept", "application/json" }
};

return await _restHttpHelper.GetAsync<T>(url, headers);
}
}

Example 4: Sourcify Contract Verification API (Real Example)

using Nethereum.Util.Rest;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;

public class SourcifyApiService
{
public const string BaseUrl = "https://sourcify.dev/server/";
public const string BaseUrlMeta = "https://repo.sourcify.dev/";
private IRestHttpHelper restHttpHelper;

public SourcifyApiService()
{
restHttpHelper = new RestHttpHelper(new HttpClient());
}

public SourcifyApiService(HttpClient httpClient)
{
restHttpHelper = new RestHttpHelper(httpClient);
}

// Get compilation metadata for verified contract
public Task<CompilationMetadata> GetCompilationMetadataAsync(
long chain,
string address,
bool fullMatch = true)
{
var matchType = fullMatch ? "full_match" : "partial_match";
var url = $"{BaseUrlMeta}/contracts/{matchType}/{chain}/{address}/metadata.json";
return GetDataAsync<CompilationMetadata>(url);
}

// Get source files for verified contract
public Task<List<SourcifyContentFile>> GetSourceFilesFullMatchAsync(
long chain,
string address)
{
var url = $"{BaseUrl}/files/{chain}/{address}";
return GetDataAsync<List<SourcifyContentFile>>(url);
}

private Task<T> GetDataAsync<T>(string url)
{
var headers = new Dictionary<string, string>
{
{ "accept", "application/json" }
};

return restHttpHelper.GetAsync<T>(url, headers);
}
}

Example 5: POST Request with Custom Object

using Nethereum.Util.Rest;
using System.Net.Http;

var restHelper = new RestHttpHelper(new HttpClient());

// Define request and response models
public class CreateUserRequest
{
public string Username { get; set; }
public string Email { get; set; }
}

public class CreateUserResponse
{
public int UserId { get; set; }
public string Status { get; set; }
}

// Make POST request
var request = new CreateUserRequest
{
Username = "alice",
Email = "alice@example.com"
};

var response = await restHelper.PostAsync<CreateUserResponse, CreateUserRequest>(
"https://api.example.com/users",
request
);

Console.WriteLine($"Created user ID: {response.UserId}");

Example 6: PUT Request for Updates

using Nethereum.Util.Rest;
using System.Collections.Generic;
using System.Net.Http;

var restHelper = new RestHttpHelper(new HttpClient());

public class UpdateProfileRequest
{
public string DisplayName { get; set; }
public string Bio { get; set; }
}

public class UpdateProfileResponse
{
public bool Success { get; set; }
public string Message { get; set; }
}

// Update with custom headers
var headers = new Dictionary<string, string>
{
{ "X-Api-Version", "2.0" }
};

var updateRequest = new UpdateProfileRequest
{
DisplayName = "Alice Smith",
Bio = "Blockchain developer"
};

var result = await restHelper.PutAsync<UpdateProfileResponse, UpdateProfileRequest>(
"https://api.example.com/profile/123",
updateRequest,
headers
);

Example 7: DELETE Request

using Nethereum.Util.Rest;
using System.Collections.Generic;
using System.Net.Http;

var restHelper = new RestHttpHelper(new HttpClient());

// Delete with authentication header
var headers = new Dictionary<string, string>
{
{ "Authorization", "Bearer YOUR_TOKEN_HERE" }
};

await restHelper.DeleteAsync(
"https://api.example.com/items/456",
headers
);

Console.WriteLine("Item deleted successfully");

Example 8: HttpClient Extension Methods with Bearer Auth (.NET 5+)

#if NET5_0_OR_GREATER
using Nethereum.Util.Rest;
using System.Net.Http;

var httpClient = new HttpClient();
string bearerToken = "your-jwt-token";

// GET with Bearer token
var userData = await httpClient.GetAsync<UserProfile>(
"https://api.example.com/user/profile",
bearerToken
);

// POST with Bearer token
var createData = new { name = "New Item", value = 100 };
var createResponse = await httpClient.PostAsync<CreateResponse>(
"https://api.example.com/items",
createData,
bearerToken
);

// PUT with Bearer token
var updateData = new { value = 200 };
var updateResponse = await httpClient.PutAsync<UpdateResponse>(
"https://api.example.com/items/123",
updateData,
bearerToken
);

// DELETE with Bearer token (returns int status)
var deleteStatus = await httpClient.DeleteAsync(
"https://api.example.com/items/123",
bearerToken
);
#endif

Example 9: Multipart Form Data for File Uploads

using Nethereum.Util.Rest;
using System.Collections.Generic;
using System.Net.Http;

var restHelper = new RestHttpHelper(new HttpClient());

// Create multipart request
var multipartRequest = new MultipartFormDataRequest
{
Fields = new List<MultipartField>
{
new MultipartField { Name = "title", Value = "My Contract" },
new MultipartField { Name = "network", Value = "mainnet" }
},
Files = new List<MultipartFile>
{
new MultipartFile
{
FieldName = "file",
FileName = "Token.sol",
Content = "pragma solidity ^0.8.0; contract Token { ... }",
ContentType = "text/plain"
}
}
};

// Add authorization header
var headers = new Dictionary<string, string>
{
{ "Authorization", "Bearer YOUR_TOKEN" }
};

// Upload file
var uploadResponse = await restHelper.PostMultipartAsync<UploadResponse>(
"https://api.example.com/upload",
multipartRequest,
headers
);

Console.WriteLine($"Upload status: {uploadResponse.Status}");

public class UploadResponse
{
public string Status { get; set; }
public string FileId { get; set; }
}

API Reference

IRestHttpHelper

Generic REST client interface.

public interface IRestHttpHelper
{
// GET request with JSON deserialization
Task<T> GetAsync<T>(string path, Dictionary<string, string> headers = null);

// POST request with JSON serialization/deserialization
Task<TResponse> PostAsync<TResponse, TRequest>(
string path,
TRequest request,
Dictionary<string, string> headers = null);

// PUT request with JSON serialization/deserialization
Task<TResponse> PutAsync<TResponse, TRequest>(
string path,
TRequest request,
Dictionary<string, string> headers = null);

// DELETE request
Task DeleteAsync(string path, Dictionary<string, string> headers = null);

// POST multipart form data (file uploads)
Task<TResponse> PostMultipartAsync<TResponse>(
string path,
MultipartFormDataRequest request,
Dictionary<string, string> headers = null);
}

RestHttpHelper

Concrete implementation of IRestHttpHelper.

public class RestHttpHelper : IRestHttpHelper
{
// Constructor - optionally provide HttpClient
public RestHttpHelper(HttpClient httpClient = null);

// Implements all IRestHttpHelper methods
// Throws Exception on non-success status codes
}

HttpClientExtensions (.NET 5.0+)

Extension methods for HttpClient with Bearer token authentication.

public static class HttpClientExtensions
{
// GET with Bearer token
public static Task<T> GetAsync<T>(
this HttpClient httpClient,
string url,
string token);

// POST with Bearer token and response
public static Task<T> PostAsync<T>(
this HttpClient httpClient,
string url,
object data,
string token);

// POST with Bearer token returning HttpResponseMessage
public static Task<HttpResponseMessage> PostAsync(
this HttpClient httpClient,
string url,
object data,
string token);

// PUT with Bearer token
public static Task<T> PutAsync<T>(
this HttpClient httpClient,
string url,
object data,
string token);

// DELETE with Bearer token
public static Task<int> DeleteAsync(
this HttpClient httpClient,
string url,
string token);
}

MultipartFormDataRequest

Container for multipart form data uploads.

public class MultipartFormDataRequest
{
public List<MultipartField> Fields { get; set; }
public List<MultipartFile> Files { get; set; }
}

public class MultipartField
{
public string Name { get; set; }
public string Value { get; set; }
}

public class MultipartFile
{
public string FieldName { get; set; } // Default: "file"
public string FileName { get; set; } // e.g., "Token.sol"
public string Content { get; set; } // File content as string
public string ContentType { get; set; } // Default: "text/plain"
}

Used By (Consumers)

  • Nethereum.Beaconchain - Beacon chain API client
  • Nethereum.DataServices - Data service integrations (Sourcify, 4byte.directory)
  • Nethereum.Consensus.LightClient - Light client API communication

Dependencies

  • None (foundational package)

Important Notes

Error Handling

All methods throw Exception on non-success HTTP status codes:

try
{
var data = await restHelper.GetAsync<MyData>("https://api.example.com/data");
}
catch (Exception ex)
{
// ex.Message contains: "Error: {StatusCode}, {ResponseBody}, {Headers}"
Console.WriteLine($"API error: {ex.Message}");
}

HttpClient Lifecycle

Best Practice: Reuse HttpClient instances to avoid socket exhaustion.

// WRONG - creates new HttpClient for every request
public Task<T> GetDataAsync<T>(string url)
{
var helper = new RestHttpHelper(new HttpClient()); // Don't do this!
return helper.GetAsync<T>(url);
}

// CORRECT - reuse HttpClient
private readonly IRestHttpHelper _restHelper;

public MyService()
{
var httpClient = new HttpClient(); // Create once
_restHelper = new RestHttpHelper(httpClient);
}

Even Better: Use HttpClientFactory in ASP.NET Core:

// Startup.cs or Program.cs
services.AddHttpClient();

// Your service
public class MyService
{
private readonly IRestHttpHelper _restHelper;

public MyService(IHttpClientFactory httpClientFactory)
{
var httpClient = httpClientFactory.CreateClient();
_restHelper = new RestHttpHelper(httpClient);
}
}

JSON Serialization

The package automatically uses the best serializer for your framework:

  • .NET 8+: System.Text.Json (faster, lower memory)
  • Older frameworks: Newtonsoft.Json (compatibility)

This is transparent - you don't need to change your code.

Case Sensitivity

HttpClientExtensions use case-insensitive JSON deserialization:

var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};

This allows matching API responses with different casing conventions.

Thread Safety

RestHttpHelper is thread-safe when using a shared HttpClient (which is thread-safe). You can safely use a single instance across multiple threads.

Additional Resources

License

This package is part of the Nethereum project and follows the same MIT license.