Ajustes para trasmitir expedientes

This commit is contained in:
2026-03-06 12:56:41 -07:00
parent dcfd606a1c
commit c52344d7f8
13 changed files with 924 additions and 401 deletions

View File

@@ -20,6 +20,7 @@ namespace EFCDesk.Classes
public class ApiClient : IDisposable
{
private static readonly HttpClient _sharedHttpClient = new HttpClient();
private readonly HttpClient _httpClient;
private readonly int _maxRetries;
private readonly TimeSpan _retryDelay;
@@ -30,12 +31,10 @@ public class ApiClient : IDisposable
public ApiClient(TimeSpan? timeout = null, int maxRetries = 3, TimeSpan? retryDelay = null)
{
_httpClient = new HttpClient();
_httpClient = _sharedHttpClient;
if (timeout.HasValue)
if (timeout.HasValue && timeout.Value > _httpClient.Timeout)
_httpClient.Timeout = timeout.Value;
else
_httpClient.Timeout = TimeSpan.FromSeconds(30);
_maxRetries = maxRetries >= 0 ? maxRetries : 3;
_retryDelay = retryDelay ?? TimeSpan.FromSeconds(2);
@@ -98,12 +97,14 @@ public class ApiClient : IDisposable
var jsonContent = JsonSerializer.Serialize(refreshData);
using var content = new StringContent(jsonContent, System.Text.Encoding.UTF8, "application/json");
// Limpiar headers para esta solicitud
_httpClient.DefaultRequestHeaders.Authorization = null;
var response = await _httpClient.PostAsync(refreshUrl, content);
// _httpClient.DefaultRequestHeaders.Authorization = null;
// var response = await _httpClient.PostAsync(refreshUrl, content);
using var request = new HttpRequestMessage(HttpMethod.Post, refreshUrl) { Content = content };
var response = await _httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
string responseContent = await response.Content.ReadAsStringAsync();
@@ -191,7 +192,7 @@ public class ApiClient : IDisposable
}
private async Task<string> SendWithRetriesAsync(Func<Task<HttpResponseMessage>> sendFunc, bool requireAuth = true)
private async Task<string> SendWithRetriesAsync(Func<HttpRequestMessage> requestFunc, bool requireAuth = true)
{
int attempts = 0;
while (true)
@@ -199,40 +200,61 @@ public class ApiClient : IDisposable
attempts++;
try
{
// Asegurar que tenemos token antes de enviar (solo si requiere auth)
string? token = null;
if (requireAuth)
{
await EnsureTokenAsync();
lock (_tokenLock)
{
token = _currentToken;
}
}
var response = await sendFunc();
var request = requestFunc();
if (requireAuth && token != null)
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
var response = await _httpClient.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
// Si es 401 Unauthorized y requiere auth, intentar refresh token
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized && requireAuth)
{
bool refreshed = await TryRefreshTokenAsync();
if (refreshed)
{
// Reintentar con nuevo token
response = await sendFunc();
string? newToken = null;
lock (_tokenLock)
{
newToken = _currentToken;
}
var newRequest = requestFunc();
if (newToken != null)
{
newRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", newToken);
}
response = await _httpClient.SendAsync(newRequest);
if (response.IsSuccessStatusCode)
return await response.Content.ReadAsStringAsync();
}
}
string errorContent = await response.Content.ReadAsStringAsync();
// throw new ApiException($"Error {response.StatusCode}: {errorContent}", (int)response.StatusCode);
return errorContent;
}
return await response.Content.ReadAsStringAsync();
}
catch (TaskCanceledException) when (attempts <= _maxRetries)
{
// Timeout: reintentar
await Task.Delay(_retryDelay);
}
catch (HttpRequestException) when (attempts <= _maxRetries)
{
// Error red: reintentar
await Task.Delay(_retryDelay);
}
catch (TaskCanceledException)
@@ -252,24 +274,24 @@ public class ApiClient : IDisposable
public async Task<string> GetAsync(string url)
{
return await SendWithRetriesAsync(() => _httpClient.GetAsync(url));
return await SendWithRetriesAsync(() => new HttpRequestMessage(HttpMethod.Get, url));
}
public async Task<string> PostJsonAsync(string url, string jsonContent)
{
using var content = new StringContent(jsonContent, System.Text.Encoding.UTF8, "application/json");
return await SendWithRetriesAsync(() => _httpClient.PostAsync(url, content));
return await SendWithRetriesAsync(() => new HttpRequestMessage(HttpMethod.Post, url) { Content = content });
}
public async Task<string> PutJsonAsync(string url, string jsonContent)
{
using var content = new StringContent(jsonContent, System.Text.Encoding.UTF8, "application/json");
return await SendWithRetriesAsync(() => _httpClient.PutAsync(url, content));
return await SendWithRetriesAsync(() => new HttpRequestMessage(HttpMethod.Put, url) { Content = content });
}
public async Task<string> DeleteAsync(string url)
{
return await SendWithRetriesAsync(() => _httpClient.DeleteAsync(url));
return await SendWithRetriesAsync(() => new HttpRequestMessage(HttpMethod.Delete, url));
}
// Método para enviar archivo y JSON juntos en multipart/form-data
@@ -290,7 +312,7 @@ public class ApiClient : IDisposable
var jsonContent = new StringContent(jsonString, System.Text.Encoding.UTF8, "application/json");
form.Add(jsonContent, jsonFieldName);
return await SendWithRetriesAsync(() => _httpClient.PostAsync(url, form));
return await SendWithRetriesAsync(() => new HttpRequestMessage(HttpMethod.Post, url) { Content = form });
}
public async Task<string> PostMultipartAsync(string url, string filePath, string organizacion, string pedimento,
@@ -316,7 +338,7 @@ public class ApiClient : IDisposable
form.Add(new StringContent(documentType), "document_type");
form.Add(new StringContent(fuente), "fuente");
return await SendWithRetriesAsync(() => _httpClient.PostAsync(url, form));
return await SendWithRetriesAsync(() => new HttpRequestMessage(HttpMethod.Post, url) { Content = form });
}
//public async Task<string> PostMultipartZipAsync(string url, string token, byte[] zipBytes, string zipFileName, string fileFieldName = "archivos")
@@ -369,14 +391,14 @@ public class ApiClient : IDisposable
//form.Add(new StringContent(documentType), "document_type");
//form.Add(new StringContent(fuente), "fuente");
return await SendWithRetriesAsync(() => _httpClient.PostAsync(url, form));
return await SendWithRetriesAsync(() => new HttpRequestMessage(HttpMethod.Post, url) { Content = form });
}
// Método existente que acepta string JSON
public async Task<string> PostJsonWithoutAuthAsync(string url, string jsonContent)
{
using var content = new StringContent(jsonContent, System.Text.Encoding.UTF8, "application/json");
return await SendWithRetriesAsync(() => _httpClient.PostAsync(url, content), requireAuth: false);
return await SendWithRetriesAsync(() => new HttpRequestMessage(HttpMethod.Post, url) { Content = content }, requireAuth: false);
}
// Nueva sobrecarga que acepta cualquier objeto
@@ -460,7 +482,7 @@ public class ApiClient : IDisposable
public void Dispose()
{
_httpClient.Dispose();
// No disposing _httpClient because it's a shared singleton
}
}
}