2022-10-25 13:52:55 +00:00
|
|
|
using System.Net.Http.Headers;
|
2023-01-16 18:42:45 +00:00
|
|
|
using Elsa.Extensions;
|
2022-10-25 13:52:55 +00:00
|
|
|
using Elsa.Http.ContentWriters;
|
|
|
|
|
using Elsa.Workflows.Core.Attributes;
|
|
|
|
|
using Elsa.Workflows.Core.Models;
|
|
|
|
|
using Elsa.Workflows.Management.Models;
|
|
|
|
|
using Microsoft.AspNetCore.Http;
|
|
|
|
|
using HttpRequestHeaders = Elsa.Http.Models.HttpRequestHeaders;
|
|
|
|
|
|
|
|
|
|
namespace Elsa.Http;
|
|
|
|
|
|
2023-01-16 18:42:45 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Send an HTTP request.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[Activity("Elsa", "HTTP", "Send an HTTP request.", DisplayName = "HTTP Request", Kind = ActivityKind.Task)]
|
2023-01-19 20:49:24 +00:00
|
|
|
public class SendHttpRequest : CodeActivity<HttpResponse>
|
2022-10-25 13:52:55 +00:00
|
|
|
{
|
2022-10-27 10:05:30 +00:00
|
|
|
[Input] public Input<Uri?> Url { get; set; } = default!;
|
|
|
|
|
|
2023-01-16 18:42:45 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// The HTTP method to use when sending the request.
|
|
|
|
|
/// </summary>
|
2022-10-25 13:52:55 +00:00
|
|
|
[Input(
|
2023-01-16 18:42:45 +00:00
|
|
|
Description = "The HTTP method to use when sending the request.",
|
2022-10-27 10:05:30 +00:00
|
|
|
Options = new[] { "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD" },
|
2023-01-16 18:42:45 +00:00
|
|
|
DefaultValue = "GET",
|
2022-10-25 13:52:55 +00:00
|
|
|
UIHint = InputUIHints.Dropdown
|
|
|
|
|
)]
|
2023-01-16 18:42:45 +00:00
|
|
|
public Input<string> Method { get; set; } = new("GET");
|
2022-10-27 10:05:30 +00:00
|
|
|
|
2023-01-16 18:42:45 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// The content to send with the request. Can be a string, an object, a byte array or a stream.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[Input(Description = "The content to send with the request. Can be a string, an object, a byte array or a stream.")]
|
|
|
|
|
public Input<object?> Content { get; set; } = default!;
|
2022-10-27 10:05:30 +00:00
|
|
|
|
2023-01-16 18:42:45 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// The content type to use when sending the request.
|
|
|
|
|
/// </summary>
|
2022-10-25 13:52:55 +00:00
|
|
|
[Input(
|
2023-01-16 18:42:45 +00:00
|
|
|
Description = "The content type to use when sending the request.",
|
2022-10-25 13:52:55 +00:00
|
|
|
Options = new[] { "", "text/plain", "text/html", "application/json", "application/xml", "application/x-www-form-urlencoded" },
|
|
|
|
|
UIHint = InputUIHints.Dropdown
|
|
|
|
|
)]
|
|
|
|
|
public Input<string?> ContentType { get; set; } = default!;
|
2022-10-27 10:05:30 +00:00
|
|
|
|
2023-01-16 18:42:45 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// The Authorization header value to send with the request.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <example>Bearer {some-access-token}</example>
|
2022-10-25 13:52:55 +00:00
|
|
|
[Input(
|
2023-01-16 18:42:45 +00:00
|
|
|
Description = "The Authorization header value to send with the request. For example: Bearer {some-access-token}",
|
|
|
|
|
Category = "Security"
|
2022-10-25 13:52:55 +00:00
|
|
|
)]
|
2023-01-16 18:42:45 +00:00
|
|
|
public Input<string?> Authorization { get; set; } = default!;
|
2022-10-27 10:05:30 +00:00
|
|
|
|
2023-01-16 18:42:45 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// The headers to send along with the request.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[Input(Description = "The headers to send along with the request.", Category = "Advanced")]
|
|
|
|
|
public Input<HttpRequestHeaders?> RequestHeaders { get; set; } = new(new HttpRequestHeaders());
|
2022-10-27 10:05:30 +00:00
|
|
|
|
2023-01-16 18:42:45 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// The parsed content, if any.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[Output(Description = "The parsed content, if any.")]
|
|
|
|
|
public Output<object?> ParsedContent { get; set; } = default!;
|
2022-10-25 13:52:55 +00:00
|
|
|
|
2023-01-16 18:42:45 +00:00
|
|
|
/// <inheritdoc />
|
2022-10-25 13:52:55 +00:00
|
|
|
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
|
|
|
|
|
{
|
|
|
|
|
var request = PrepareRequest(context);
|
2022-10-27 10:05:30 +00:00
|
|
|
var httpClientFactory = context.GetRequiredService<IHttpClientFactory>();
|
2022-10-25 13:52:55 +00:00
|
|
|
var httpClient = httpClientFactory.CreateClient(nameof(SendHttpRequest));
|
|
|
|
|
var cancellationToken = context.CancellationToken;
|
|
|
|
|
var response = await httpClient.SendAsync(request, cancellationToken);
|
2023-01-16 18:42:45 +00:00
|
|
|
var parsedContent = await ParseContentAsync(context, response.Content);
|
2022-10-27 10:05:30 +00:00
|
|
|
|
2023-01-16 18:42:45 +00:00
|
|
|
context.Set(Result, response);
|
|
|
|
|
context.Set(ParsedContent, parsedContent);
|
2022-10-25 13:52:55 +00:00
|
|
|
}
|
2022-10-27 10:05:30 +00:00
|
|
|
|
2023-01-16 18:42:45 +00:00
|
|
|
private async Task<object?> ParseContentAsync(ActivityExecutionContext context, HttpContent httpContent)
|
2022-10-25 13:52:55 +00:00
|
|
|
{
|
2023-01-16 18:42:45 +00:00
|
|
|
if (!HasContent(httpContent))
|
|
|
|
|
return null;
|
2022-10-27 10:05:30 +00:00
|
|
|
|
2023-01-16 18:42:45 +00:00
|
|
|
var cancellationToken = context.CancellationToken;
|
|
|
|
|
var targetType = ParsedContent.GetTargetType(context);
|
|
|
|
|
var contentStream = await httpContent.ReadAsStreamAsync(cancellationToken);
|
|
|
|
|
var contentType = httpContent.Headers.ContentType?.MediaType!;
|
|
|
|
|
|
|
|
|
|
return await context.ParseContentAsync(contentStream, contentType, targetType, cancellationToken);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool HasContent(HttpContent httpContent) => httpContent.Headers.ContentLength > 0;
|
2022-10-25 13:52:55 +00:00
|
|
|
|
|
|
|
|
private HttpRequestMessage PrepareRequest(ActivityExecutionContext context)
|
|
|
|
|
{
|
2023-01-16 18:42:45 +00:00
|
|
|
var method = Method.TryGet(context) ?? "GET";
|
|
|
|
|
var url = Url.Get(context);
|
|
|
|
|
var request = new HttpRequestMessage(new HttpMethod(method), url);
|
|
|
|
|
var headers = RequestHeaders.TryGet(context) ?? new HttpRequestHeaders();
|
|
|
|
|
var authorization = Authorization.TryGet(context);
|
2022-10-25 13:52:55 +00:00
|
|
|
|
2023-01-16 18:42:45 +00:00
|
|
|
if (!string.IsNullOrWhiteSpace(authorization))
|
|
|
|
|
request.Headers.Authorization = AuthenticationHeaderValue.Parse(authorization);
|
2022-10-25 13:52:55 +00:00
|
|
|
|
2023-01-16 18:42:45 +00:00
|
|
|
foreach (var header in headers)
|
2022-10-25 13:52:55 +00:00
|
|
|
request.Headers.Add(header.Key, header.Value.AsEnumerable());
|
|
|
|
|
|
2023-01-16 18:42:45 +00:00
|
|
|
var contentType = ContentType.TryGet(context);
|
|
|
|
|
var content = Content.TryGet(context);
|
2023-01-23 14:57:38 +00:00
|
|
|
|
|
|
|
|
if (contentType != null && content != null)
|
|
|
|
|
{
|
|
|
|
|
var contentWriters = context.GetServices<IHttpContentWriter>();
|
|
|
|
|
var contentWriter = SelectContentWriter(contentType, contentWriters);
|
|
|
|
|
request.Content = contentWriter.GetContent(content, contentType);
|
|
|
|
|
}
|
2022-10-25 13:52:55 +00:00
|
|
|
|
|
|
|
|
return request;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-16 18:42:45 +00:00
|
|
|
private IHttpContentWriter SelectContentWriter(string? contentType, IEnumerable<IHttpContentWriter> requestContentWriters) =>
|
|
|
|
|
string.IsNullOrWhiteSpace(contentType) ? new StringHttpContentWriter() : requestContentWriters.First(w => w.SupportsContentType(contentType));
|
2022-10-27 10:05:30 +00:00
|
|
|
}
|