122 lines
3.9 KiB
C#
122 lines
3.9 KiB
C#
|
|
using System.Diagnostics;
|
||
|
|
using System.Text;
|
||
|
|
using System.Text.Json;
|
||
|
|
|
||
|
|
namespace S8n.Components.Basics;
|
||
|
|
|
||
|
|
public class HttpRequest
|
||
|
|
{
|
||
|
|
private static readonly HttpClient _httpClient = new();
|
||
|
|
|
||
|
|
private static string EnsureUrlSchema(string url)
|
||
|
|
{
|
||
|
|
if (string.IsNullOrWhiteSpace(url))
|
||
|
|
return url;
|
||
|
|
|
||
|
|
// Check if the URL already has a scheme
|
||
|
|
if (Uri.TryCreate(url, UriKind.Absolute, out var uri) && !string.IsNullOrEmpty(uri.Scheme))
|
||
|
|
{
|
||
|
|
return url; // Already has a scheme
|
||
|
|
}
|
||
|
|
|
||
|
|
// If no scheme, prepend http://
|
||
|
|
// Check if it starts with // (protocol-relative URL)
|
||
|
|
if (url.StartsWith("//"))
|
||
|
|
{
|
||
|
|
return "http:" + url;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Otherwise prepend http://
|
||
|
|
return "http://" + url;
|
||
|
|
}
|
||
|
|
|
||
|
|
public object Execute(string method, string url, Dictionary<string, string>? headers = null, object? body = null)
|
||
|
|
{
|
||
|
|
if (string.IsNullOrWhiteSpace(url))
|
||
|
|
{
|
||
|
|
throw new ArgumentException("URL is required");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (string.IsNullOrWhiteSpace(method))
|
||
|
|
{
|
||
|
|
throw new ArgumentException("HTTP method is required");
|
||
|
|
}
|
||
|
|
|
||
|
|
var normalizedUrl = EnsureUrlSchema(url);
|
||
|
|
|
||
|
|
var stopwatch = Stopwatch.StartNew();
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
using var request = new HttpRequestMessage(HttpMethod.Parse(method), normalizedUrl);
|
||
|
|
|
||
|
|
// Add headers if provided
|
||
|
|
if (headers != null)
|
||
|
|
{
|
||
|
|
foreach (var header in headers)
|
||
|
|
{
|
||
|
|
request.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Add body if provided and method supports it
|
||
|
|
if (body != null && (method.Equals("POST", StringComparison.OrdinalIgnoreCase) ||
|
||
|
|
method.Equals("PUT", StringComparison.OrdinalIgnoreCase) ||
|
||
|
|
method.Equals("PATCH", StringComparison.OrdinalIgnoreCase)))
|
||
|
|
{
|
||
|
|
string jsonBody;
|
||
|
|
if (body is JsonElement jsonElement)
|
||
|
|
{
|
||
|
|
jsonBody = jsonElement.GetRawText();
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
jsonBody = JsonSerializer.Serialize(body);
|
||
|
|
}
|
||
|
|
request.Content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
|
||
|
|
}
|
||
|
|
|
||
|
|
// Execute request
|
||
|
|
using var response = _httpClient.SendAsync(request).GetAwaiter().GetResult();
|
||
|
|
stopwatch.Stop();
|
||
|
|
|
||
|
|
// Read response content
|
||
|
|
var responseContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
|
||
|
|
|
||
|
|
// Extract response headers
|
||
|
|
var responseHeaders = new Dictionary<string, string>();
|
||
|
|
foreach (var header in response.Headers)
|
||
|
|
{
|
||
|
|
responseHeaders[header.Key] = string.Join(", ", header.Value);
|
||
|
|
}
|
||
|
|
foreach (var header in response.Content.Headers)
|
||
|
|
{
|
||
|
|
responseHeaders[header.Key] = string.Join(", ", header.Value);
|
||
|
|
}
|
||
|
|
|
||
|
|
return new
|
||
|
|
{
|
||
|
|
StatusCode = (int)response.StatusCode,
|
||
|
|
StatusText = response.StatusCode.ToString(),
|
||
|
|
Response = responseContent,
|
||
|
|
Headers = responseHeaders,
|
||
|
|
Duration = stopwatch.ElapsedMilliseconds
|
||
|
|
};
|
||
|
|
}
|
||
|
|
catch (HttpRequestException ex)
|
||
|
|
{
|
||
|
|
stopwatch.Stop();
|
||
|
|
throw new Exception($"HTTP request failed: {ex.Message}");
|
||
|
|
}
|
||
|
|
catch (UriFormatException ex)
|
||
|
|
{
|
||
|
|
throw new Exception($"Invalid URL format: {ex.Message}");
|
||
|
|
}
|
||
|
|
catch (Exception ex) when (ex is not ArgumentException)
|
||
|
|
{
|
||
|
|
stopwatch.Stop();
|
||
|
|
throw new Exception($"Request failed: {ex.Message}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|