HttpClientService.cs 9.09 KB
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Rcs.Application.Shared;

namespace Rcs.Infrastructure.Shared;

/// <summary>
/// HTTP客户端服务实现
/// </summary>
public class HttpClientService : IHttpClientService
{
    private readonly HttpClient _httpClient;
    private readonly ILogger<HttpClientService> _logger;
    private readonly JsonSerializerOptions _jsonOptions;

    public HttpClientService(HttpClient httpClient, ILogger<HttpClientService> logger)
    {
        _httpClient = httpClient;
        _logger = logger;
        _jsonOptions = new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true,
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        };
    }

    public async Task<TResponse?> GetAsync<TResponse>(
        string url,
        Dictionary<string, string>? headers = null,
        CancellationToken cancellationToken = default)
    {
        try
        {
            using var request = new HttpRequestMessage(HttpMethod.Get, url);
            AddHeaders(request, headers);

            var response = await _httpClient.SendAsync(request, cancellationToken);

            return await HandleResponse<TResponse>(response, cancellationToken);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error sending GET request to {Url}", url);
            throw;
        }
    }

    public async Task<TResponse?> PostAsync<TRequest, TResponse>(
        string url,
        TRequest data,
        Dictionary<string, string>? headers = null,
        CancellationToken cancellationToken = default)
    {
        try
        {
            using var request = new HttpRequestMessage(HttpMethod.Post, url);
            AddHeaders(request, headers);
            request.Content = CreateJsonContent(data);

            _logger.LogInformation("Sending POST request to {Url}", url);
            var response = await _httpClient.SendAsync(request, cancellationToken);

            return await HandleResponse<TResponse>(response, cancellationToken);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error sending POST request to {Url}", url);
            throw;
        }
    }

    public async Task<HttpResponseMessage> PostAsync<TRequest>(
        string url,
        TRequest data,
        Dictionary<string, string>? headers = null,
        CancellationToken cancellationToken = default)
    {
        try
        {
            using var request = new HttpRequestMessage(HttpMethod.Post, url);
            AddHeaders(request, headers);
            request.Content = CreateJsonContent(data);

            _logger.LogInformation("Sending POST request to {Url}", url);
            var response = await _httpClient.SendAsync(request, cancellationToken);
            response.EnsureSuccessStatusCode();

            return response;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error sending POST request to {Url}", url);
            throw;
        }
    }

    public async Task<TResponse?> PutAsync<TRequest, TResponse>(
        string url,
        TRequest data,
        Dictionary<string, string>? headers = null,
        CancellationToken cancellationToken = default)
    {
        try
        {
            using var request = new HttpRequestMessage(HttpMethod.Put, url);
            AddHeaders(request, headers);
            request.Content = CreateJsonContent(data);

            _logger.LogInformation("Sending PUT request to {Url}", url);
            var response = await _httpClient.SendAsync(request, cancellationToken);

            return await HandleResponse<TResponse>(response, cancellationToken);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error sending PUT request to {Url}", url);
            throw;
        }
    }

    public async Task<HttpResponseMessage> PutAsync<TRequest>(
        string url,
        TRequest data,
        Dictionary<string, string>? headers = null,
        CancellationToken cancellationToken = default)
    {
        try
        {
            using var request = new HttpRequestMessage(HttpMethod.Put, url);
            AddHeaders(request, headers);
            request.Content = CreateJsonContent(data);

            _logger.LogInformation("Sending PUT request to {Url}", url);
            var response = await _httpClient.SendAsync(request, cancellationToken);
            response.EnsureSuccessStatusCode();

            return response;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error sending PUT request to {Url}", url);
            throw;
        }
    }

    public async Task<TResponse?> DeleteAsync<TResponse>(
        string url,
        Dictionary<string, string>? headers = null,
        CancellationToken cancellationToken = default)
    {
        try
        {
            using var request = new HttpRequestMessage(HttpMethod.Delete, url);
            AddHeaders(request, headers);

            _logger.LogInformation("Sending DELETE request to {Url}", url);
            var response = await _httpClient.SendAsync(request, cancellationToken);

            return await HandleResponse<TResponse>(response, cancellationToken);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error sending DELETE request to {Url}", url);
            throw;
        }
    }

    public async Task<HttpResponseMessage> DeleteAsync(
        string url,
        Dictionary<string, string>? headers = null,
        CancellationToken cancellationToken = default)
    {
        try
        {
            using var request = new HttpRequestMessage(HttpMethod.Delete, url);
            AddHeaders(request, headers);

            _logger.LogInformation("Sending DELETE request to {Url}", url);
            var response = await _httpClient.SendAsync(request, cancellationToken);
            response.EnsureSuccessStatusCode();

            return response;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error sending DELETE request to {Url}", url);
            throw;
        }
    }

    /// <summary>
    /// 下载文件流
    /// @author zzy
    /// </summary>
    public async Task<Stream> DownloadStreamAsync(
        string url,
        Dictionary<string, string>? headers = null,
        CancellationToken cancellationToken = default)
    {
        try
        {
            using var request = new HttpRequestMessage(HttpMethod.Get, url);
            AddHeaders(request, headers);

            _logger.LogInformation("Downloading file from {Url}", url);
            var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
            response.EnsureSuccessStatusCode();

            var memoryStream = new MemoryStream();
            await response.Content.CopyToAsync(memoryStream, cancellationToken);
            memoryStream.Position = 0;
            return memoryStream;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error downloading file from {Url}", url);
            throw;
        }
    }

    public async Task<TResponse?> PatchAsync<TRequest, TResponse>(
        string url,
        TRequest data,
        Dictionary<string, string>? headers = null,
        CancellationToken cancellationToken = default)
    {
        try
        {
            using var request = new HttpRequestMessage(HttpMethod.Patch, url);
            AddHeaders(request, headers);
            request.Content = CreateJsonContent(data);

            _logger.LogInformation("Sending PATCH request to {Url}", url);
            var response = await _httpClient.SendAsync(request, cancellationToken);

            return await HandleResponse<TResponse>(response, cancellationToken);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error sending PATCH request to {Url}", url);
            throw;
        }
    }

    private void AddHeaders(HttpRequestMessage request, Dictionary<string, string>? headers)
    {
        if (headers == null) return;

        foreach (var header in headers)
        {
            request.Headers.TryAddWithoutValidation(header.Key, header.Value);
        }
    }

    private HttpContent CreateJsonContent<T>(T data)
    {
        var json = JsonSerializer.Serialize(data, _jsonOptions);
        return new StringContent(json, Encoding.UTF8, "application/json");
    }

    private async Task<TResponse?> HandleResponse<TResponse>(
        HttpResponseMessage response,
        CancellationToken cancellationToken)
    {
        if (!response.IsSuccessStatusCode)
        {
            var errorContent = await response.Content.ReadAsStringAsync(cancellationToken);
            _logger.LogError(
                "HTTP request failed with status {StatusCode}. Response: {Response}",
                response.StatusCode,
                errorContent);
            response.EnsureSuccessStatusCode();
        }

        var content = await response.Content.ReadAsStringAsync(cancellationToken);
        if (string.IsNullOrWhiteSpace(content))
        {
            return default;
        }

        return JsonSerializer.Deserialize<TResponse>(content, _jsonOptions);
    }
}