2019-11-21 07:14:01 +00:00
using System ;
2019-10-27 16:31:50 +00:00
using System.Collections.Generic ;
using System.Linq ;
using System.Net.Http ;
using System.Net.Http.Headers ;
using System.Text ;
using System.Threading.Tasks ;
using Elsa.Activities.Http.Models ;
using Elsa.Activities.Http.Services ;
2020-10-03 10:48:25 +00:00
using Elsa.ActivityResults ;
2019-10-27 16:31:50 +00:00
using Elsa.Attributes ;
using Elsa.Design ;
2021-04-03 19:58:16 +00:00
using Elsa.Expressions ;
2019-10-27 16:31:50 +00:00
using Elsa.Services ;
using Elsa.Services.Models ;
using Microsoft.AspNetCore.Http ;
2021-04-02 14:52:31 +00:00
using Microsoft.Extensions.Primitives ;
2020-01-12 19:15:57 +00:00
using HttpRequestHeaders = Elsa . Activities . Http . Models . HttpRequestHeaders ;
2019-10-27 16:31:50 +00:00
2020-01-12 19:15:57 +00:00
// ReSharper disable once CheckNamespace
namespace Elsa.Activities.Http
2019-10-27 16:31:50 +00:00
{
2020-11-14 20:58:48 +00:00
[ Action (
2019-10-27 16:31:50 +00:00
Category = "HTTP" ,
DisplayName = "Send HTTP Request" ,
Description = "Send an HTTP request." ,
2020-11-14 20:58:48 +00:00
Outcomes = new [ ] { OutcomeNames . Done }
2019-10-27 16:31:50 +00:00
) ]
public class SendHttpRequest : Activity
{
2020-10-09 17:52:54 +00:00
private readonly HttpClient _httpClient ;
private readonly IEnumerable < IHttpResponseBodyParser > _parsers ;
2019-10-27 16:31:50 +00:00
public SendHttpRequest (
IHttpClientFactory httpClientFactory ,
2019-11-03 13:33:34 +00:00
IEnumerable < IHttpResponseBodyParser > parsers )
2019-10-27 16:31:50 +00:00
{
2020-10-09 17:52:54 +00:00
_httpClient = httpClientFactory . CreateClient ( nameof ( SendHttpRequest ) ) ;
2020-10-10 15:13:52 +00:00
_parsers = parsers ;
2019-10-27 16:31:50 +00:00
}
/// <summary>
/// The URL to invoke.
/// </summary>
2021-05-29 11:43:12 +00:00
[ActivityInput(Hint = "The URL to send the HTTP request to.", SupportedSyntaxes = new[] { SyntaxNames . JavaScript , SyntaxNames . Liquid } ) ]
2021-03-29 15:24:13 +00:00
public Uri ? Url { get ; set ; }
2019-10-27 16:31:50 +00:00
/// <summary>
/// The HTTP method to use.
/// </summary>
2021-05-29 11:43:12 +00:00
[ ActivityInput (
UIHint = ActivityInputUIHints . Dropdown ,
2021-03-02 08:36:42 +00:00
Hint = "The HTTP method to use when making the request." ,
2021-04-03 19:58:16 +00:00
Options = new [ ] { "GET" , "POST" , "PUT" , "DELETE" , "PATCH" , "OPTIONS" , "HEAD" } ,
SupportedSyntaxes = new [ ] { SyntaxNames . JavaScript , SyntaxNames . Liquid }
2019-10-27 16:31:50 +00:00
) ]
2020-11-03 09:50:32 +00:00
public string? Method { get ; set ; }
2019-10-27 16:31:50 +00:00
/// <summary>
/// The body to send along with the request.
/// </summary>
2021-05-29 11:43:12 +00:00
[ActivityInput(Hint = "The HTTP content to send along with the request.", UIHint = ActivityInputUIHints.MultiLine, SupportedSyntaxes = new[] { SyntaxNames . JavaScript , SyntaxNames . Liquid } ) ]
2020-10-03 10:48:25 +00:00
public string? Content { get ; set ; }
2019-10-27 16:31:50 +00:00
/// <summary>
/// The Content Type header to send along with the request body.
/// </summary>
2021-05-29 11:43:12 +00:00
[ ActivityInput (
UIHint = ActivityInputUIHints . Dropdown ,
2021-03-02 08:36:42 +00:00
Hint = "The content type to send with the request." ,
2021-04-03 19:58:16 +00:00
Options = new [ ] { "text/plain" , "text/html" , "application/json" , "application/xml" , "application/x-www-form-urlencoded" } ,
SupportedSyntaxes = new [ ] { SyntaxNames . JavaScript , SyntaxNames . Liquid }
2019-10-27 16:31:50 +00:00
) ]
2020-11-03 09:50:32 +00:00
public string? ContentType { get ; set ; }
2019-10-27 16:31:50 +00:00
2021-06-08 18:43:59 +00:00
[ActivityInput(Hint = "The Authorization header value to send.", SupportedSyntaxes = new[] { SyntaxNames . JavaScript , SyntaxNames . Liquid } , Category = PropertyCategories . Advanced ) ]
2020-11-03 09:50:32 +00:00
public string? Authorization { get ; set ; }
2019-10-27 16:31:50 +00:00
/// <summary>
/// The headers to send along with the request.
/// </summary>
2021-06-08 18:43:59 +00:00
[ ActivityInput (
Hint = "Additional headers to send along with the request." ,
UIHint = ActivityInputUIHints . MultiLine , DefaultSyntax = SyntaxNames . Json ,
SupportedSyntaxes = new [ ] { SyntaxNames . Json , SyntaxNames . JavaScript , SyntaxNames . Liquid } ,
Category = PropertyCategories . Advanced
) ]
2021-03-02 08:36:42 +00:00
public HttpRequestHeaders RequestHeaders { get ; set ; } = new ( ) ;
2019-10-27 16:31:50 +00:00
2021-05-29 11:43:12 +00:00
[ActivityInput(Hint = "Read the content of the response.", SupportedSyntaxes = new[] { SyntaxNames . Literal , SyntaxNames . JavaScript , SyntaxNames . Liquid } ) ]
2020-10-03 10:48:25 +00:00
public bool ReadContent { get ; set ; }
2019-10-27 16:31:50 +00:00
/// <summary>
/// A list of HTTP status codes this activity can handle.
/// </summary>
2021-05-29 11:43:12 +00:00
[ ActivityInput (
2021-04-03 19:58:16 +00:00
Hint = "A list of possible HTTP status codes to handle." ,
2021-05-29 11:43:12 +00:00
UIHint = ActivityInputUIHints . MultiText ,
2021-04-03 19:58:16 +00:00
DefaultSyntax = SyntaxNames . Json ,
2021-06-06 18:54:27 +00:00
DefaultValue = new [ ] { 200 } ,
2021-04-03 19:58:16 +00:00
SupportedSyntaxes = new [ ] { SyntaxNames . Json , SyntaxNames . JavaScript , SyntaxNames . Liquid }
2019-10-27 16:31:50 +00:00
) ]
2021-05-12 08:44:37 +00:00
public ICollection < int > ? SupportedStatusCodes { get ; set ; } = new HashSet < int > ( new [ ] { 200 } ) ;
2019-10-27 16:31:50 +00:00
2021-06-13 14:24:43 +00:00
[ActivityOutput] public HttpResponseModel Response { get ; set ; }
2020-12-02 21:28:36 +00:00
protected override async ValueTask < IActivityExecutionResult > OnExecuteAsync ( ActivityExecutionContext context )
2019-10-27 16:31:50 +00:00
{
2020-10-13 20:21:47 +00:00
var request = CreateRequest ( ) ;
2020-12-02 21:28:36 +00:00
var cancellationToken = context . CancellationToken ;
2021-04-01 18:03:44 +00:00
var response = ( await _httpClient . SendAsync ( request , cancellationToken ) ) ! ;
var hasContent = response . Content ! = null ! ;
var contentType = response ? . Content ? . Headers ? . ContentType ? . MediaType ;
2019-10-27 16:31:50 +00:00
var responseModel = new HttpResponseModel
{
2021-04-01 18:03:44 +00:00
StatusCode = response ! . StatusCode ,
2019-10-27 16:31:50 +00:00
Headers = new Dictionary < string , string [ ] > (
response . Headers . ToDictionary ( x = > x . Key , x = > x . Value . ToArray ( ) )
)
} ;
if ( hasContent & & ReadContent )
{
2020-10-13 20:21:47 +00:00
var formatter = SelectContentParser ( contentType ! ) ;
2019-11-03 13:33:34 +00:00
responseModel . Content = await formatter . ParseAsync ( response , cancellationToken ) ;
2019-10-27 16:31:50 +00:00
}
2020-01-12 19:15:57 +00:00
2021-03-02 08:36:42 +00:00
var statusCode = ( int ) response . StatusCode ;
2020-01-12 19:15:57 +00:00
var statusOutcome = statusCode . ToString ( ) ;
2021-06-13 14:24:43 +00:00
var supportedStatusCodes = SupportedStatusCodes ;
var isSupportedStatusCode = supportedStatusCodes = = null | | ! supportedStatusCodes . Any ( ) | | SupportedStatusCodes ? . Contains ( statusCode ) = = true ;
2020-01-12 19:15:57 +00:00
var outcomes = new List < string > { OutcomeNames . Done , statusOutcome } ;
2020-02-15 19:32:16 +00:00
if ( ! isSupportedStatusCode )
2021-06-08 18:43:59 +00:00
outcomes . Add ( "Unsupported Status Code" ) ;
2019-10-27 16:31:50 +00:00
2021-06-13 14:24:43 +00:00
Response = responseModel ;
2021-05-21 09:51:18 +00:00
return Combine ( Output ( responseModel ) , Outcomes ( outcomes ) ) ;
2019-10-27 16:31:50 +00:00
}
2019-11-03 13:33:34 +00:00
private IHttpResponseBodyParser SelectContentParser ( string contentType )
2019-10-27 16:31:50 +00:00
{
2021-03-30 14:07:02 +00:00
string? simpleContentType = contentType ? . Split ( ';' ) . First ( ) ;
2020-10-09 17:52:54 +00:00
var formatters = _parsers . OrderByDescending ( x = > x . Priority ) . ToList ( ) ;
2019-10-27 16:31:50 +00:00
return formatters . FirstOrDefault (
2021-03-30 14:07:02 +00:00
x = > x . SupportedContentTypes . Contains ( simpleContentType , StringComparer . OrdinalIgnoreCase )
2020-10-03 10:48:25 +00:00
) ? ? formatters . Last ( ) ;
2019-10-27 16:31:50 +00:00
}
2020-10-13 20:21:47 +00:00
private HttpRequestMessage CreateRequest ( )
2019-10-27 16:31:50 +00:00
{
2020-11-03 09:50:32 +00:00
var method = Method ? ? HttpMethods . Get ;
var methodSupportsBody = GetMethodSupportsBody ( method ) ;
2020-10-03 10:48:25 +00:00
var url = Url ;
var request = new HttpRequestMessage ( new HttpMethod ( Method ) , url ) ;
var authorizationHeaderValue = Authorization ;
2021-04-02 14:52:31 +00:00
var requestHeaders = new HeaderDictionary ( RequestHeaders . ToDictionary ( x = > x . Key , x = > new StringValues ( x . Value . Split ( ',' ) ) ) ) ;
2019-10-27 16:31:50 +00:00
if ( methodSupportsBody )
{
2020-10-03 10:48:25 +00:00
var body = Content ;
var contentType = ContentType ;
2019-10-27 16:31:50 +00:00
2020-02-15 19:32:16 +00:00
if ( ! string . IsNullOrWhiteSpace ( body ) )
2019-10-27 16:31:50 +00:00
request . Content = new StringContent ( body , Encoding . UTF8 , contentType ) ;
}
if ( ! string . IsNullOrWhiteSpace ( authorizationHeaderValue ) )
request . Headers . Authorization = AuthenticationHeaderValue . Parse ( authorizationHeaderValue ) ;
foreach ( var header in requestHeaders )
request . Headers . Add ( header . Key , header . Value . AsEnumerable ( ) ) ;
return request ;
}
2019-11-24 14:25:16 +00:00
private static bool GetMethodSupportsBody ( string method )
2019-10-27 16:31:50 +00:00
{
2020-10-25 20:37:15 +00:00
var methods = new [ ] { "POST" , "PUT" , "PATCH" , "DELETE" } ;
2019-10-27 16:31:50 +00:00
return methods . Contains ( method , StringComparer . InvariantCultureIgnoreCase ) ;
}
}
2019-01-05 20:19:13 +00:00
}