Ecng.Net.Clients
1.0.303
dotnet add package Ecng.Net.Clients --version 1.0.303
NuGet\Install-Package Ecng.Net.Clients -Version 1.0.303
<PackageReference Include="Ecng.Net.Clients" Version="1.0.303" />
<PackageVersion Include="Ecng.Net.Clients" Version="1.0.303" />
<PackageReference Include="Ecng.Net.Clients" />
paket add Ecng.Net.Clients --version 1.0.303
#r "nuget: Ecng.Net.Clients, 1.0.303"
#:package Ecng.Net.Clients@1.0.303
#addin nuget:?package=Ecng.Net.Clients&version=1.0.303
#tool nuget:?package=Ecng.Net.Clients&version=1.0.303
Ecng.Net.Clients
A powerful .NET library that provides utility base classes for building REST API clients with built-in serialization, error handling, retry policies, and caching support.
Table of Contents
Overview
Ecng.Net.Clients wraps HttpClient with serialization and error handling, allowing your API client classes to stay clean and concise. Instead of writing repetitive HTTP client code, you can focus on defining your API endpoints.
Why Use This Library?
Standard .NET Approach:
using var client = new HttpClient { BaseAddress = new Uri("https://api.example.com/") };
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
var response = await client.GetAsync("users/123");
response.EnsureSuccessStatusCode();
var user = JsonSerializer.Deserialize<User>(
await response.Content.ReadAsStringAsync());
Using Ecng.Net.Clients:
public class MyApiClient : RestBaseApiClient
{
public MyApiClient(string token)
: base(new HttpClient(), new JsonMediaTypeFormatter(), new JsonMediaTypeFormatter())
{
BaseAddress = new Uri("https://api.example.com/");
AddAuthBearer(token);
}
public Task<User> GetUser(int id, CancellationToken cancellationToken = default)
=> GetAsync<User>(GetCurrentMethod(), cancellationToken, id);
}
// Usage
var client = new MyApiClient(token);
var user = await client.GetUser(123);
Installation
Add a reference to the Ecng.Net.Clients project or include the NuGet package (if published):
<ItemGroup>
<ProjectReference Include="..\..\Ecng\Net.Clients\Net.Clients.csproj" />
</ItemGroup>
Quick Start
Creating a Basic REST API Client
using Ecng.Net;
using System.Net.Http.Formatting;
public class GitHubApiClient : RestBaseApiClient
{
public GitHubApiClient(string accessToken = null)
: base(new HttpClient(), new JsonMediaTypeFormatter(), new JsonMediaTypeFormatter())
{
BaseAddress = new Uri("https://api.github.com/");
if (!string.IsNullOrEmpty(accessToken))
AddAuthBearer(accessToken);
// Configure retry policy
RetryPolicy.ReadMaxCount = 3;
RetryPolicy.WriteMaxCount = 2;
}
// GET request: GET /users/{username}
public Task<GitHubUser> GetUser(string username, CancellationToken cancellationToken = default)
=> GetAsync<GitHubUser>(GetCurrentMethod(), cancellationToken, username);
// POST request: POST /repos/{owner}/{repo}/issues
public Task<Issue> CreateIssue(string owner, string repo, CreateIssueRequest request,
CancellationToken cancellationToken = default)
=> PostAsync<Issue>(GetCurrentMethod(), cancellationToken, owner, repo, request);
// PUT request: PUT /user/starred/{owner}/{repo}
public Task StarRepository(string owner, string repo, CancellationToken cancellationToken = default)
=> PutAsync<VoidType>(GetCurrentMethod(), cancellationToken, owner, repo);
// DELETE request: DELETE /user/starred/{owner}/{repo}
public Task UnstarRepository(string owner, string repo, CancellationToken cancellationToken = default)
=> DeleteAsync<VoidType>(GetCurrentMethod(), cancellationToken, owner, repo);
}
// Usage
var client = new GitHubApiClient("your_access_token");
var user = await client.GetUser("octocat");
Console.WriteLine($"Name: {user.Name}, Followers: {user.Followers}");
Core Features
REST API Client
The RestBaseApiClient abstract base class provides the foundation for building REST API clients.
Constructor Parameters
public abstract class RestBaseApiClient(
HttpMessageInvoker http, // HttpClient or custom message invoker
MediaTypeFormatter request, // Formatter for request serialization
MediaTypeFormatter response) // Formatter for response deserialization
HTTP Methods
The base class provides protected methods for all standard HTTP verbs:
// GET request
protected Task<TResult> GetAsync<TResult>(
string methodName,
CancellationToken cancellationToken,
params object[] args)
// POST request
protected Task<TResult> PostAsync<TResult>(
string methodName,
CancellationToken cancellationToken,
params object[] args)
// PUT request
protected Task<TResult> PutAsync<TResult>(
string methodName,
CancellationToken cancellationToken,
params object[] args)
// DELETE request
protected Task<TResult> DeleteAsync<TResult>(
string methodName,
CancellationToken cancellationToken,
params object[] args)
URL Construction
By default, method names are automatically converted to URL paths:
GetUserAsync→getuserCreateOrder→createorder
Arguments are added as:
- GET/DELETE: Query string parameters
- POST/PUT: Request body
Authentication
The library supports multiple authentication schemes:
Bearer Token Authentication
public class MyApiClient : RestBaseApiClient
{
public MyApiClient(string token)
: base(new HttpClient(), new JsonMediaTypeFormatter(), new JsonMediaTypeFormatter())
{
BaseAddress = new Uri("https://api.example.com/");
AddAuthBearer(token); // Adds "Authorization: Bearer {token}"
}
}
Basic Authentication
public class MyApiClient : RestBaseApiClient
{
public MyApiClient(string username, string password)
: base(new HttpClient(), new JsonMediaTypeFormatter(), new JsonMediaTypeFormatter())
{
BaseAddress = new Uri("https://api.example.com/");
var credentials = Convert.ToBase64String(
Encoding.UTF8.GetBytes($"{username}:{password}"));
AddAuth(AuthenticationSchemes.Basic, credentials);
}
}
Custom Authentication
public class MyApiClient : RestBaseApiClient
{
public MyApiClient(string apiKey)
: base(new HttpClient(), new JsonMediaTypeFormatter(), new JsonMediaTypeFormatter())
{
BaseAddress = new Uri("https://api.example.com/");
AddAuth("X-API-Key", apiKey);
}
}
Per-Request Headers
public class MyApiClient : RestBaseApiClient
{
public MyApiClient()
: base(new HttpClient(), new JsonMediaTypeFormatter(), new JsonMediaTypeFormatter())
{
BaseAddress = new Uri("https://api.example.com/");
// Headers added to every request
PerRequestHeaders["User-Agent"] = "MyApp/1.0";
PerRequestHeaders["Accept-Language"] = "en-US";
}
}
Request/Response Handling
JSON Serialization (Default)
using System.Net.Http.Formatting;
using Newtonsoft.Json;
var formatter = new JsonMediaTypeFormatter
{
SerializerSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
DateFormatString = "yyyy-MM-dd",
Converters = { new StringEnumConverter() }
}
};
public class MyApiClient : RestBaseApiClient
{
public MyApiClient()
: base(new HttpClient(), formatter, formatter)
{
BaseAddress = new Uri("https://api.example.com/");
}
}
Form URL Encoded
For APIs that expect form-urlencoded data:
using Ecng.Net;
public class MyApiClient : RestBaseApiClient
{
public MyApiClient()
: base(
new HttpClient(),
new RestApiFormUrlEncodedMediaTypeFormatter(), // Request
new JsonMediaTypeFormatter()) // Response
{
BaseAddress = new Uri("https://api.example.com/");
}
public Task<TokenResponse> GetToken(string username, string password,
CancellationToken cancellationToken = default)
=> PostAsync<TokenResponse>(GetCurrentMethod(), cancellationToken, username, password);
}
// Sends: username=john&password=secret123
Plain Text Responses
For APIs that return plain text:
using Ecng.Net;
public class MyApiClient : RestBaseApiClient
{
public MyApiClient()
: base(
new HttpClient(),
new JsonMediaTypeFormatter(),
new TextMediaTypeFormatter(new[] { "text/plain", "text/html" }))
{
BaseAddress = new Uri("https://api.example.com/");
}
public Task<string> GetPlainText(CancellationToken cancellationToken = default)
=> GetAsync<string>(GetCurrentMethod(), cancellationToken);
}
Retry Policies
Built-in retry mechanism with exponential backoff:
public class MyApiClient : RestBaseApiClient
{
public MyApiClient()
: base(new HttpClient(), new JsonMediaTypeFormatter(), new JsonMediaTypeFormatter())
{
BaseAddress = new Uri("https://api.example.com/");
// Configure retry policy
RetryPolicy.ReadMaxCount = 5; // Retry GET requests up to 5 times
RetryPolicy.WriteMaxCount = 2; // Retry POST/PUT/DELETE up to 2 times
// RetryPolicy uses exponential backoff by default
}
}
The RetryPolicyInfo class automatically handles:
- Network failures
- Timeout exceptions
- Server errors (5xx status codes)
- Exponential backoff between retries
Caching
Cache API responses to reduce network calls:
In-Memory Cache
using Ecng.Net;
public class MyApiClient : RestBaseApiClient
{
public MyApiClient()
: base(new HttpClient(), new JsonMediaTypeFormatter(), new JsonMediaTypeFormatter())
{
BaseAddress = new Uri("https://api.example.com/");
// Cache GET requests for 5 minutes
Cache = new InMemoryRestApiClientCache(TimeSpan.FromMinutes(5));
}
}
Custom Cache Implementation
public class RedisRestApiClientCache : IRestApiClientCache
{
private readonly IConnectionMultiplexer _redis;
public RedisRestApiClientCache(IConnectionMultiplexer redis)
{
_redis = redis;
}
public bool TryGet<T>(HttpMethod method, Uri uri, object body, out T value)
{
var db = _redis.GetDatabase();
var key = $"{method}:{uri}";
var cached = db.StringGet(key);
if (cached.HasValue)
{
value = JsonConvert.DeserializeObject<T>(cached);
return true;
}
value = default;
return false;
}
public void Set<T>(HttpMethod method, Uri uri, object body, T value)
{
var db = _redis.GetDatabase();
var key = $"{method}:{uri}";
var serialized = JsonConvert.SerializeObject(value);
db.StringSet(key, serialized, TimeSpan.FromMinutes(10));
}
public void Remove(HttpMethod method = default, string uriLike = default,
ComparisonOperator op = ComparisonOperator.Greater)
{
// Implementation for cache invalidation
}
}
// Usage
var client = new MyApiClient
{
Cache = new RedisRestApiClientCache(redisConnection)
};
Cache Invalidation
public class MyApiClient : RestBaseApiClient
{
public MyApiClient()
: base(new HttpClient(), new JsonMediaTypeFormatter(), new JsonMediaTypeFormatter())
{
BaseAddress = new Uri("https://api.example.com/");
Cache = new InMemoryRestApiClientCache(TimeSpan.FromMinutes(5));
}
public async Task UpdateUser(int userId, UserUpdateRequest request,
CancellationToken cancellationToken = default)
{
await PutAsync<User>(GetCurrentMethod(), cancellationToken, userId, request);
// Invalidate cached user data
Cache.Remove(HttpMethod.Get, $"users/{userId}", ComparisonOperator.Equal);
}
}
Request Logging
Monitor API calls for debugging and analytics:
public class MyApiClient : RestBaseApiClient
{
public MyApiClient()
: base(new HttpClient(), new JsonMediaTypeFormatter(), new JsonMediaTypeFormatter())
{
BaseAddress = new Uri("https://api.example.com/");
// Enable request logging
LogRequest += (method, uri, body) =>
{
Console.WriteLine($"[{DateTime.Now}] {method} {uri}");
if (body != null)
Console.WriteLine($"Body: {JsonConvert.SerializeObject(body)}");
};
}
}
Performance Tracing
public class MyApiClient : RestBaseApiClient
{
public MyApiClient()
: base(new HttpClient(), new JsonMediaTypeFormatter(), new JsonMediaTypeFormatter())
{
BaseAddress = new Uri("https://api.example.com/");
// Enable performance tracing
Tracing = true;
}
protected override void TraceCall(HttpMethod method, Uri uri, TimeSpan elapsed)
{
if (elapsed.TotalSeconds > 1)
Console.WriteLine($"SLOW: {method} {uri} took {elapsed.TotalSeconds:F2}s");
}
}
Advanced Features
Custom Formatters
Create custom formatters for specialized serialization:
public class XmlMediaTypeFormatter : MediaTypeFormatter
{
public XmlMediaTypeFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml"));
}
public override bool CanReadType(Type type) => true;
public override bool CanWriteType(Type type) => true;
public override async Task<object> ReadFromStreamAsync(
Type type, Stream readStream, HttpContent content,
IFormatterLogger formatterLogger, CancellationToken cancellationToken)
{
var serializer = new XmlSerializer(type);
return serializer.Deserialize(readStream);
}
public override async Task WriteToStreamAsync(
Type type, object value, Stream writeStream,
HttpContent content, TransportContext transportContext,
CancellationToken cancellationToken)
{
var serializer = new XmlSerializer(type);
serializer.Serialize(writeStream, value);
}
}
// Usage
public class MyApiClient : RestBaseApiClient
{
public MyApiClient()
: base(
new HttpClient(),
new XmlMediaTypeFormatter(),
new XmlMediaTypeFormatter())
{
BaseAddress = new Uri("https://api.example.com/");
}
}
REST Attributes
Use attributes to customize endpoint names and parameter handling:
Custom Endpoint Names
using Ecng.Net;
public class MyApiClient : RestBaseApiClient
{
// Method name: GetUserProfile
// Without attribute: GET /getuserprofile?id=123
// With attribute: GET /users/profile?id=123
[Rest(Name = "users/profile")]
public Task<UserProfile> GetUserProfile(int id, CancellationToken cancellationToken = default)
=> GetAsync<UserProfile>(GetCurrentMethod(), cancellationToken, id);
}
Custom Parameter Names
public class MyApiClient : RestBaseApiClient
{
// Without attributes: GET /search?searchTerm=hello&pageSize=10
// With attributes: GET /search?q=hello&limit=10
public Task<SearchResults> Search(
[Rest(Name = "q")] string searchTerm,
[Rest(Name = "limit")] int pageSize,
CancellationToken cancellationToken = default)
=> GetAsync<SearchResults>(GetCurrentMethod(), cancellationToken, searchTerm, pageSize);
}
Ignoring Parameters
public class MyApiClient : RestBaseApiClient
{
// The 'options' parameter is used locally but not sent to the API
public Task<User> GetUser(
int userId,
[Rest(Ignore = true)] RequestOptions options,
CancellationToken cancellationToken = default)
{
// Use options for client-side logic
if (options?.UseCache == true)
Cache = new InMemoryRestApiClientCache(TimeSpan.FromMinutes(5));
return GetAsync<User>(GetCurrentMethod(), cancellationToken, userId);
}
}
Error Handling
Extracting Error Details
public class MyApiClient : RestBaseApiClient
{
public MyApiClient()
: base(new HttpClient(), new JsonMediaTypeFormatter(), new JsonMediaTypeFormatter())
{
BaseAddress = new Uri("https://api.example.com/");
// Extract detailed error messages from API responses
ExtractBadResponse = true;
}
}
// If API returns: {"error": "Invalid credentials", "code": 401}
// Exception message will include: "401 (Unauthorized): {\"error\":\"Invalid credentials\",\"code\":401}"
Custom Response Validation
public class MyApiClient : RestBaseApiClient
{
protected override async Task ValidateResponseAsync(
HttpResponseMessage response,
CancellationToken cancellationToken)
{
if (!response.IsSuccessStatusCode)
{
var errorContent = await response.Content.ReadAsStringAsync(cancellationToken);
var error = JsonConvert.DeserializeObject<ApiError>(errorContent);
throw new ApiException(error.Message, error.Code, response.StatusCode);
}
}
}
Custom Response Processing
public class MyApiClient : RestBaseApiClient
{
protected override async Task<TResult> GetResultAsync<TResult>(
HttpResponseMessage response,
CancellationToken cancellationToken)
{
// Handle empty responses
if (typeof(TResult) == typeof(VoidType))
return default;
var content = await response.Content.ReadAsStringAsync(cancellationToken);
// Unwrap API envelope: {"success": true, "data": {...}}
var envelope = JsonConvert.DeserializeObject<ApiEnvelope<TResult>>(content);
return envelope.Data;
}
}
Utilities
Sitemap Generation
Generate XML sitemaps for search engine optimization:
using Ecng.Net.Sitemap;
using System.Xml.Linq;
// Create sitemap nodes
var nodes = new List<SitemapNode>
{
new SitemapNode("https://example.com/")
{
LastModified = DateTime.UtcNow,
Frequency = SitemapFrequency.Daily,
Priority = 1.0
},
new SitemapNode("https://example.com/products")
{
LastModified = DateTime.UtcNow.AddDays(-1),
Frequency = SitemapFrequency.Weekly,
Priority = 0.8
},
new SitemapNode("https://example.com/about")
{
Frequency = SitemapFrequency.Monthly,
Priority = 0.5
}
};
// Generate sitemap XML
XDocument sitemap = SitemapGenerator.GenerateSitemap(nodes);
sitemap.Save("sitemap.xml");
Multilingual Sitemaps
using Ecng.Net.Sitemap;
var node = new SitemapNode("https://example.com/products")
{
LastModified = DateTime.UtcNow,
Frequency = SitemapFrequency.Weekly,
Priority = 0.8
};
// Add alternate language versions
node.AlternateLinks.Add(new XhtmlLink("https://example.com/en/products", "en"));
node.AlternateLinks.Add(new XhtmlLink("https://example.com/fr/products", "fr"));
node.AlternateLinks.Add(new XhtmlLink("https://example.com/de/products", "de"));
node.AlternateLinks.Add(new XhtmlLink("https://example.com/products", "x-default"));
var sitemap = SitemapGenerator.GenerateSitemap(new[] { node });
sitemap.Save("sitemap-multilingual.xml");
Sitemap Index
For large sites with multiple sitemaps:
using Ecng.Net.Sitemap;
var sitemapUrls = new[]
{
"https://example.com/sitemap-products.xml",
"https://example.com/sitemap-blog.xml",
"https://example.com/sitemap-pages.xml"
};
XDocument sitemapIndex = SitemapGenerator.GenerateSitemapIndex(sitemapUrls);
sitemapIndex.Save("sitemap-index.xml");
Sitemap Frequency Options
public enum SitemapFrequency
{
Never, // Archived URLs that never change
Yearly, // Changes yearly
Monthly, // Changes monthly
Weekly, // Changes weekly
Daily, // Changes daily
Hourly, // Changes hourly
Always // Changes on every access
}
Captcha Validation
Interface for implementing captcha validation:
using Ecng.Net.Captcha;
public class RecaptchaValidator : ICaptchaValidator<RecaptchaResponse>
{
private readonly string _secretKey;
private readonly HttpClient _httpClient;
public RecaptchaValidator(string secretKey)
{
_secretKey = secretKey;
_httpClient = new HttpClient();
}
public async Task<RecaptchaResponse> ValidateAsync(
string response,
string address,
CancellationToken cancellationToken = default)
{
var requestUri = "https://www.google.com/recaptcha/api/siteverify" +
$"?secret={_secretKey}&response={response}&remoteip={address}";
var httpResponse = await _httpClient.PostAsync(requestUri, null, cancellationToken);
var content = await httpResponse.Content.ReadAsStringAsync(cancellationToken);
return JsonConvert.DeserializeObject<RecaptchaResponse>(content);
}
}
public class RecaptchaResponse
{
public bool Success { get; set; }
public DateTime ChallengeTs { get; set; }
public string Hostname { get; set; }
public string[] ErrorCodes { get; set; }
}
// Usage
var validator = new RecaptchaValidator("your-secret-key");
var result = await validator.ValidateAsync(captchaResponse, userIpAddress);
if (result.Success)
{
// Captcha validated successfully
}
SMS Service
Interface for implementing SMS messaging:
using Ecng.Net.Sms;
public class TwilioSmsService : ISmsService
{
private readonly string _accountSid;
private readonly string _authToken;
private readonly string _fromNumber;
private readonly HttpClient _httpClient;
public TwilioSmsService(string accountSid, string authToken, string fromNumber)
{
_accountSid = accountSid;
_authToken = authToken;
_fromNumber = fromNumber;
_httpClient = new HttpClient();
var credentials = Convert.ToBase64String(
Encoding.ASCII.GetBytes($"{accountSid}:{authToken}"));
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", credentials);
}
public async Task<string> SendAsync(
string phone,
string message,
CancellationToken cancellationToken = default)
{
var requestUri = $"https://api.twilio.com/2010-04-01/Accounts/{_accountSid}/Messages.json";
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("From", _fromNumber),
new KeyValuePair<string, string>("To", phone),
new KeyValuePair<string, string>("Body", message)
});
var response = await _httpClient.PostAsync(requestUri, content, cancellationToken);
return await response.Content.ReadAsStringAsync(cancellationToken);
}
}
// Usage
var smsService = new TwilioSmsService("accountSid", "authToken", "+1234567890");
var result = await smsService.SendAsync("+1987654321", "Your verification code is: 123456");
Currency Converter
Interface for implementing currency conversion:
using Ecng.Net.Currencies;
public class ExchangeRateApiConverter : ICurrencyConverter
{
private readonly HttpClient _httpClient;
private readonly string _apiKey;
public ExchangeRateApiConverter(string apiKey)
{
_apiKey = apiKey;
_httpClient = new HttpClient
{
BaseAddress = new Uri("https://api.exchangerate-api.com/v4/")
};
}
public async Task<decimal> GetRateAsync(
CurrencyTypes from,
CurrencyTypes to,
DateTime date,
CancellationToken cancellationToken = default)
{
var response = await _httpClient.GetAsync(
$"latest/{from}?apikey={_apiKey}",
cancellationToken);
var content = await response.Content.ReadAsStringAsync(cancellationToken);
var data = JsonConvert.DeserializeObject<ExchangeRateResponse>(content);
return data.Rates[to.ToString()];
}
}
public class ExchangeRateResponse
{
public Dictionary<string, decimal> Rates { get; set; }
}
// Usage
var converter = new ExchangeRateApiConverter("your-api-key");
var rate = await converter.GetRateAsync(
CurrencyTypes.USD,
CurrencyTypes.EUR,
DateTime.UtcNow);
decimal amountInEur = 100m * rate;
Console.WriteLine($"$100 USD = €{amountInEur:F2} EUR");
API Reference
RestBaseApiClient
Base class for creating REST API clients.
Properties
| Property | Type | Description |
|---|---|---|
BaseAddress |
Uri |
The base URL for all API requests |
Http |
HttpMessageInvoker |
The underlying HTTP client |
RequestFormatter |
MediaTypeFormatter |
Serializer for request bodies |
ResponseFormatter |
MediaTypeFormatter |
Deserializer for responses |
PerRequestHeaders |
IDictionary<string, string> |
Headers added to every request |
Cache |
IRestApiClientCache |
Response cache implementation |
RetryPolicy |
RetryPolicyInfo |
Retry configuration |
ExtractBadResponse |
bool |
Include error details in exceptions |
Tracing |
bool |
Enable performance tracing |
Events
| Event | Description |
|---|---|
LogRequest |
Fired before each request is sent |
Protected Methods
| Method | Description |
|---|---|
GetAsync<TResult>() |
Execute HTTP GET request |
PostAsync<TResult>() |
Execute HTTP POST request |
PutAsync<TResult>() |
Execute HTTP PUT request |
DeleteAsync<TResult>() |
Execute HTTP DELETE request |
GetCurrentMethod() |
Get current method name for URL construction |
AddAuthBearer() |
Add Bearer token authentication |
AddAuth() |
Add custom authentication header |
IRestApiClientCache
Interface for implementing response caching.
Methods
| Method | Description |
|---|---|
TryGet<T>() |
Attempt to retrieve cached value |
Set<T>() |
Store value in cache |
Remove() |
Remove cached entries |
InMemoryRestApiClientCache
In-memory cache implementation with expiration.
Constructor
public InMemoryRestApiClientCache(TimeSpan timeout)
RestAttribute
Attribute for customizing REST API behavior.
Properties
| Property | Type | Description |
|---|---|---|
Name |
string |
Custom name for endpoint or parameter |
IsRequired |
bool |
Whether parameter is required |
Ignore |
bool |
Exclude parameter from request |
SitemapGenerator
Static class for generating XML sitemaps.
Methods
| Method | Description |
|---|---|
GenerateSitemap() |
Create sitemap XML from nodes |
GenerateSitemapIndex() |
Create sitemap index XML |
CheckDocumentSize() |
Validate sitemap size (max 10MB) |
CheckSitemapCount() |
Validate sitemap count (max 50,000) |
SitemapNode
Represents a URL in a sitemap.
Properties
| Property | Type | Description |
|---|---|---|
Url |
string |
The URL (required) |
LastModified |
DateTime? |
Last modification date |
Frequency |
SitemapFrequency? |
Change frequency hint |
Priority |
double? |
Priority (0.0 to 1.0) |
AlternateLinks |
XhtmlLinkCollection |
Alternate language versions |
Media Type Formatters
JsonMediaTypeFormatter
Standard JSON serialization using Newtonsoft.Json (from Microsoft.AspNet.WebApi.Client).
RestApiFormUrlEncodedMediaTypeFormatter
Form URL-encoded serialization for application/x-www-form-urlencoded content type.
TextMediaTypeFormatter
Plain text serialization/deserialization for text-based responses.
Best Practices
1. Use CancellationToken
Always support cancellation in your API methods:
public Task<User> GetUser(int id, CancellationToken cancellationToken = default)
=> GetAsync<User>(GetCurrentMethod(), cancellationToken, id);
2. Configure Appropriate Retry Policies
Set different retry counts for read vs. write operations:
public MyApiClient()
{
RetryPolicy.ReadMaxCount = 5; // More retries for reads
RetryPolicy.WriteMaxCount = 1; // Fewer retries for writes
}
3. Use Caching for Read-Heavy APIs
Enable caching for APIs with mostly GET requests:
public MyApiClient()
{
Cache = new InMemoryRestApiClientCache(TimeSpan.FromMinutes(5));
}
4. Handle Errors Gracefully
Enable detailed error extraction for better debugging:
public MyApiClient()
{
ExtractBadResponse = true;
}
5. Use Strongly-Typed DTOs
Define clear data transfer objects for requests and responses:
public class CreateUserRequest
{
public string Username { get; set; }
public string Email { get; set; }
}
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
public DateTime CreatedAt { get; set; }
}
public Task<User> CreateUser(CreateUserRequest request, CancellationToken cancellationToken = default)
=> PostAsync<User>(GetCurrentMethod(), cancellationToken, request);
License
This library is part of the StockSharp/Ecng project. Please refer to the project's license file for terms of use.
Contributing
Contributions are welcome! Please ensure that your code follows the existing patterns and includes appropriate documentation.
Support
For issues, questions, or feature requests, please use the StockSharp project's issue tracker.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Ecng.Net (>= 1.0.476)
- Microsoft.AspNet.WebApi.Client (>= 6.0.0)
-
net10.0
- Ecng.Net (>= 1.0.476)
- Microsoft.AspNet.WebApi.Client (>= 6.0.0)
-
net6.0
- Ecng.Net (>= 1.0.476)
- Microsoft.AspNet.WebApi.Client (>= 6.0.0)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Ecng.Net.Clients:
| Package | Downloads |
|---|---|
|
StockSharp.Mfd
MFD |
|
|
StockSharp.Web.Api.Interfaces
StockSharp WebApi |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.303 | 0 | 1/9/2026 |
| 1.0.302 | 26 | 1/9/2026 |
| 1.0.301 | 38 | 1/8/2026 |
| 1.0.300 | 38 | 1/8/2026 |
| 1.0.299 | 36 | 1/7/2026 |
| 1.0.298 | 37 | 1/6/2026 |
| 1.0.297 | 39 | 1/6/2026 |
| 1.0.296 | 81 | 1/5/2026 |
| 1.0.295 | 82 | 1/4/2026 |
| 1.0.294 | 90 | 1/1/2026 |
| 1.0.293 | 91 | 12/31/2025 |
| 1.0.292 | 89 | 12/30/2025 |
| 1.0.291 | 89 | 12/30/2025 |
| 1.0.290 | 97 | 12/29/2025 |
| 1.0.289 | 96 | 12/29/2025 |
| 1.0.288 | 102 | 12/26/2025 |
| 1.0.287 | 94 | 12/26/2025 |
| 1.0.286 | 99 | 12/26/2025 |
| 1.0.285 | 110 | 12/26/2025 |
| 1.0.284 | 185 | 12/25/2025 |
| 1.0.283 | 180 | 12/25/2025 |
| 1.0.282 | 184 | 12/24/2025 |
| 1.0.281 | 180 | 12/23/2025 |
| 1.0.280 | 171 | 12/22/2025 |
| 1.0.279 | 154 | 12/21/2025 |
| 1.0.278 | 200 | 12/19/2025 |
| 1.0.277 | 234 | 12/19/2025 |
| 1.0.276 | 270 | 12/18/2025 |
| 1.0.275 | 265 | 12/17/2025 |
| 1.0.274 | 261 | 12/15/2025 |
| 1.0.273 | 255 | 12/15/2025 |
| 1.0.272 | 219 | 12/14/2025 |
| 1.0.271 | 157 | 12/14/2025 |
| 1.0.270 | 168 | 12/13/2025 |
| 1.0.269 | 169 | 12/13/2025 |
| 1.0.268 | 122 | 12/12/2025 |
| 1.0.267 | 116 | 12/12/2025 |
| 1.0.266 | 118 | 12/12/2025 |
| 1.0.265 | 117 | 12/12/2025 |
| 1.0.264 | 121 | 12/12/2025 |
| 1.0.263 | 132 | 12/12/2025 |
| 1.0.262 | 128 | 12/12/2025 |
| 1.0.261 | 676 | 12/2/2025 |
| 1.0.260 | 674 | 12/2/2025 |
| 1.0.259 | 685 | 12/2/2025 |
| 1.0.258 | 268 | 11/30/2025 |
| 1.0.257 | 138 | 11/29/2025 |
| 1.0.256 | 144 | 11/28/2025 |
| 1.0.255 | 138 | 11/28/2025 |
| 1.0.254 | 192 | 11/27/2025 |
| 1.0.253 | 200 | 11/24/2025 |
| 1.0.252 | 195 | 11/24/2025 |
| 1.0.251 | 196 | 11/23/2025 |
| 1.0.250 | 175 | 11/23/2025 |
| 1.0.249 | 206 | 11/22/2025 |
| 1.0.248 | 1,011 | 11/20/2025 |
| 1.0.247 | 409 | 11/18/2025 |
| 1.0.246 | 402 | 11/18/2025 |
| 1.0.245 | 299 | 11/13/2025 |
| 1.0.244 | 240 | 11/10/2025 |
| 1.0.243 | 156 | 11/1/2025 |
| 1.0.242 | 177 | 10/31/2025 |
| 1.0.241 | 205 | 10/28/2025 |
| 1.0.240 | 203 | 10/27/2025 |
| 1.0.239 | 200 | 10/27/2025 |
| 1.0.238 | 127 | 10/25/2025 |
| 1.0.237 | 138 | 10/24/2025 |
| 1.0.236 | 189 | 10/20/2025 |
| 1.0.235 | 199 | 10/12/2025 |
| 1.0.234 | 135 | 10/11/2025 |
| 1.0.233 | 192 | 10/7/2025 |
| 1.0.232 | 204 | 10/6/2025 |
| 1.0.231 | 162 | 10/3/2025 |
| 1.0.230 | 198 | 10/1/2025 |
| 1.0.229 | 197 | 10/1/2025 |
| 1.0.228 | 199 | 9/30/2025 |
| 1.0.227 | 182 | 9/28/2025 |
| 1.0.226 | 201 | 9/25/2025 |
| 1.0.225 | 837 | 9/5/2025 |
| 1.0.224 | 191 | 9/2/2025 |
| 1.0.223 | 3,334 | 8/30/2025 |
| 1.0.222 | 360 | 8/30/2025 |
| 1.0.221 | 318 | 8/20/2025 |
| 1.0.220 | 187 | 8/20/2025 |
| 1.0.219 | 196 | 8/19/2025 |
| 1.0.218 | 173 | 8/15/2025 |
| 1.0.217 | 208 | 8/10/2025 |
| 1.0.216 | 446 | 7/16/2025 |
| 1.0.215 | 198 | 7/14/2025 |
| 1.0.214 | 213 | 7/13/2025 |
| 1.0.213 | 204 | 7/13/2025 |
| 1.0.212 | 162 | 7/12/2025 |
| 1.0.211 | 276 | 7/8/2025 |
| 1.0.210 | 180 | 7/4/2025 |
| 1.0.209 | 210 | 7/2/2025 |
| 1.0.208 | 246 | 6/24/2025 |
| 1.0.207 | 2,478 | 6/16/2025 |
| 1.0.206 | 367 | 6/9/2025 |
| 1.0.205 | 263 | 6/8/2025 |
| 1.0.204 | 317 | 5/21/2025 |
| 1.0.203 | 227 | 5/21/2025 |
| 1.0.202 | 213 | 5/17/2025 |
| 1.0.201 | 431 | 5/12/2025 |
| 1.0.200 | 284 | 5/12/2025 |
| 1.0.199 | 261 | 5/12/2025 |
| 1.0.198 | 208 | 5/11/2025 |
| 1.0.197 | 187 | 5/11/2025 |
| 1.0.196 | 139 | 5/10/2025 |
| 1.0.195 | 140 | 5/10/2025 |
| 1.0.194 | 233 | 5/6/2025 |
| 1.0.193 | 149 | 5/3/2025 |
| 1.0.192 | 273 | 4/17/2025 |
| 1.0.191 | 256 | 4/15/2025 |
| 1.0.190 | 177 | 4/12/2025 |
| 1.0.189 | 256 | 4/9/2025 |
| 1.0.188 | 1,977 | 4/6/2025 |
| 1.0.187 | 170 | 4/5/2025 |
| 1.0.186 | 304 | 3/22/2025 |
| 1.0.185 | 263 | 3/20/2025 |
| 1.0.184 | 222 | 3/20/2025 |
| 1.0.183 | 236 | 3/19/2025 |
| 1.0.182 | 537 | 2/26/2025 |
| 1.0.181 | 185 | 2/26/2025 |
| 1.0.180 | 437 | 2/8/2025 |
| 1.0.179 | 187 | 2/8/2025 |
| 1.0.178 | 198 | 2/8/2025 |
| 1.0.177 | 185 | 2/6/2025 |
| 1.0.176 | 187 | 2/6/2025 |
| 1.0.175 | 185 | 2/6/2025 |
| 1.0.174 | 200 | 2/6/2025 |
| 1.0.173 | 178 | 2/6/2025 |
| 1.0.172 | 2,054 | 2/5/2025 |
| 1.0.171 | 193 | 2/5/2025 |
| 1.0.170 | 202 | 2/5/2025 |
| 1.0.169 | 507 | 2/3/2025 |
| 1.0.168 | 217 | 2/2/2025 |
| 1.0.167 | 209 | 2/1/2025 |
| 1.0.166 | 196 | 1/31/2025 |
| 1.0.165 | 205 | 1/30/2025 |
| 1.0.164 | 176 | 1/26/2025 |
| 1.0.163 | 215 | 1/21/2025 |
| 1.0.162 | 180 | 1/20/2025 |
| 1.0.161 | 180 | 1/20/2025 |
| 1.0.160 | 181 | 1/19/2025 |
| 1.0.159 | 186 | 1/19/2025 |
| 1.0.158 | 402 | 1/15/2025 |
| 1.0.157 | 314 | 1/15/2025 |
| 1.0.156 | 312 | 1/15/2025 |
| 1.0.155 | 180 | 1/14/2025 |
| 1.0.154 | 185 | 1/12/2025 |
| 1.0.153 | 164 | 1/12/2025 |
| 1.0.152 | 217 | 1/12/2025 |
| 1.0.151 | 176 | 1/12/2025 |
| 1.0.150 | 189 | 1/10/2025 |
| 1.0.149 | 614 | 12/30/2024 |
| 1.0.148 | 193 | 12/27/2024 |
| 1.0.147 | 213 | 12/19/2024 |
| 1.0.146 | 216 | 11/20/2024 |
| 1.0.145 | 192 | 11/19/2024 |
| 1.0.144 | 200 | 11/19/2024 |
| 1.0.143 | 1,907 | 11/18/2024 |
| 1.0.142 | 229 | 11/18/2024 |
| 1.0.141 | 298 | 11/7/2024 |
| 1.0.140 | 183 | 10/31/2024 |
| 1.0.139 | 214 | 10/19/2024 |
| 1.0.138 | 252 | 10/19/2024 |
| 1.0.137 | 235 | 10/19/2024 |
| 1.0.136 | 1,171 | 10/13/2024 |
| 1.0.135 | 229 | 10/12/2024 |
| 1.0.134 | 304 | 10/9/2024 |
| 1.0.133 | 203 | 10/9/2024 |
| 1.0.132 | 254 | 10/5/2024 |
| 1.0.131 | 1,461 | 9/18/2024 |
| 1.0.130 | 215 | 9/18/2024 |
| 1.0.129 | 402 | 9/18/2024 |
| 1.0.128 | 211 | 9/17/2024 |
| 1.0.127 | 230 | 9/17/2024 |
| 1.0.126 | 407 | 9/3/2024 |
| 1.0.125 | 194 | 9/1/2024 |
| 1.0.124 | 566 | 8/8/2024 |
| 1.0.123 | 2,193 | 7/25/2024 |
| 1.0.122 | 190 | 7/23/2024 |
| 1.0.121 | 493 | 7/23/2024 |
| 1.0.120 | 332 | 7/23/2024 |
| 1.0.119 | 825 | 7/4/2024 |
| 1.0.118 | 1,316 | 6/12/2024 |
| 1.0.117 | 394 | 6/12/2024 |
| 1.0.116 | 204 | 6/12/2024 |
| 1.0.115 | 637 | 5/28/2024 |
| 1.0.114 | 452 | 5/4/2024 |
| 1.0.113 | 338 | 4/23/2024 |
| 1.0.112 | 2,096 | 4/21/2024 |
| 1.0.111 | 376 | 4/14/2024 |
| 1.0.110 | 868 | 4/5/2024 |
| 1.0.109 | 664 | 3/28/2024 |
| 1.0.108 | 241 | 3/17/2024 |
| 1.0.107 | 979 | 3/9/2024 |
| 1.0.106 | 382 | 2/23/2024 |
| 1.0.105 | 234 | 2/23/2024 |
| 1.0.104 | 754 | 2/18/2024 |
| 1.0.103 | 193 | 2/18/2024 |
| 1.0.102 | 202 | 2/16/2024 |
| 1.0.101 | 329 | 2/13/2024 |
| 1.0.100 | 302 | 2/8/2024 |
| 1.0.99 | 298 | 2/5/2024 |
| 1.0.98 | 207 | 2/4/2024 |
| 1.0.97 | 306 | 1/23/2024 |
| 1.0.96 | 216 | 1/23/2024 |
| 1.0.95 | 1,659 | 1/12/2024 |
| 1.0.94 | 1,792 | 1/2/2024 |
| 1.0.93 | 394 | 12/29/2023 |
| 1.0.92 | 565 | 12/17/2023 |
| 1.0.91 | 474 | 12/15/2023 |
| 1.0.90 | 214 | 12/15/2023 |
| 1.0.89 | 241 | 12/15/2023 |
| 1.0.88 | 921 | 12/13/2023 |
| 1.0.87 | 254 | 12/13/2023 |
| 1.0.86 | 244 | 12/10/2023 |
| 1.0.85 | 2,121 | 11/18/2023 |
| 1.0.84 | 216 | 11/18/2023 |
| 1.0.83 | 204 | 11/18/2023 |
| 1.0.82 | 184 | 11/17/2023 |
| 1.0.81 | 191 | 11/12/2023 |
| 1.0.80 | 189 | 11/12/2023 |
| 1.0.79 | 205 | 11/10/2023 |
| 1.0.78 | 185 | 11/10/2023 |
| 1.0.77 | 395 | 11/9/2023 |
| 1.0.76 | 202 | 11/9/2023 |
| 1.0.75 | 185 | 11/9/2023 |
| 1.0.74 | 575 | 11/3/2023 |
| 1.0.73 | 179 | 11/1/2023 |
| 1.0.72 | 215 | 11/1/2023 |
| 1.0.71 | 2,536 | 9/8/2023 |
| 1.0.70 | 427 | 9/8/2023 |
| 1.0.69 | 384 | 9/3/2023 |
| 1.0.68 | 243 | 8/27/2023 |
| 1.0.67 | 227 | 8/24/2023 |
| 1.0.66 | 276 | 8/21/2023 |
| 1.0.65 | 665 | 8/15/2023 |
| 1.0.64 | 254 | 8/14/2023 |
| 1.0.63 | 225 | 8/14/2023 |
| 1.0.62 | 858 | 8/10/2023 |
| 1.0.61 | 1,213 | 7/29/2023 |
| 1.0.60 | 1,570 | 7/1/2023 |
| 1.0.59 | 303 | 6/29/2023 |
| 1.0.58 | 2,176 | 6/4/2023 |
| 1.0.57 | 974 | 5/27/2023 |
| 1.0.56 | 591 | 5/21/2023 |
| 1.0.55 | 729 | 5/19/2023 |
| 1.0.54 | 2,712 | 5/8/2023 |
| 1.0.53 | 307 | 5/7/2023 |
| 1.0.52 | 342 | 5/6/2023 |
| 1.0.51 | 903 | 5/5/2023 |
| 1.0.50 | 281 | 5/5/2023 |
| 1.0.49 | 1,593 | 5/1/2023 |
| 1.0.48 | 1,818 | 4/22/2023 |
| 1.0.47 | 316 | 4/21/2023 |
| 1.0.46 | 323 | 4/21/2023 |
| 1.0.45 | 1,709 | 4/13/2023 |
| 1.0.44 | 764 | 4/3/2023 |
| 1.0.43 | 2,177 | 3/27/2023 |
| 1.0.42 | 1,538 | 3/21/2023 |
| 1.0.41 | 850 | 3/18/2023 |
| 1.0.40 | 389 | 3/18/2023 |
| 1.0.39 | 406 | 3/17/2023 |
| 1.0.38 | 805 | 3/13/2023 |
| 1.0.37 | 939 | 3/6/2023 |
| 1.0.36 | 1,412 | 2/26/2023 |
| 1.0.35 | 1,422 | 2/21/2023 |
| 1.0.34 | 410 | 2/20/2023 |
| 1.0.33 | 1,355 | 2/16/2023 |
| 1.0.32 | 441 | 2/15/2023 |
| 1.0.31 | 392 | 2/14/2023 |
| 1.0.30 | 393 | 2/14/2023 |
| 1.0.29 | 911 | 2/9/2023 |
| 1.0.28 | 2,153 | 2/7/2023 |
| 1.0.27 | 432 | 2/4/2023 |
| 1.0.26 | 436 | 2/4/2023 |
| 1.0.25 | 468 | 2/3/2023 |
| 1.0.24 | 441 | 2/3/2023 |
| 1.0.23 | 423 | 2/2/2023 |
| 1.0.22 | 444 | 1/30/2023 |
| 1.0.21 | 1,898 | 1/30/2023 |
| 1.0.20 | 2,540 | 1/25/2023 |
| 1.0.19 | 452 | 1/23/2023 |
| 1.0.18 | 451 | 1/23/2023 |
| 1.0.17 | 491 | 1/18/2023 |
| 1.0.16 | 472 | 1/15/2023 |
| 1.0.15 | 453 | 1/6/2023 |
| 1.0.14 | 482 | 1/1/2023 |
| 1.0.13 | 444 | 12/31/2022 |
| 1.0.12 | 447 | 12/30/2022 |
| 1.0.11 | 456 | 12/29/2022 |
| 1.0.10 | 455 | 12/23/2022 |
| 1.0.9 | 460 | 12/12/2022 |
| 1.0.8 | 443 | 12/8/2022 |
| 1.0.7 | 487 | 12/4/2022 |
| 1.0.6 | 476 | 12/4/2022 |
| 1.0.5 | 463 | 12/2/2022 |
| 1.0.4 | 482 | 11/30/2022 |
| 1.0.3 | 469 | 11/29/2022 |
| 1.0.2 | 509 | 11/28/2022 |
| 1.0.1 | 499 | 11/26/2022 |
| 1.0.0 | 471 | 11/26/2022 |