Implement dynamic HTTP content writing
This commit is contained in:
parent
6dba654e9f
commit
061daa206d
|
|
@ -114,14 +114,14 @@ public class SendHttpRequest : CodeActivity<HttpResponse>
|
|||
|
||||
if (contentType != null && content != null)
|
||||
{
|
||||
var contentWriters = context.GetServices<IHttpContentWriter>();
|
||||
var contentWriters = context.GetServices<IHttpContentFactory>();
|
||||
var contentWriter = SelectContentWriter(contentType, contentWriters);
|
||||
request.Content = contentWriter.GetContent(content, contentType);
|
||||
request.Content = contentWriter.CreateHttpContent(content, contentType);
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
private IHttpContentWriter SelectContentWriter(string? contentType, IEnumerable<IHttpContentWriter> requestContentWriters) =>
|
||||
string.IsNullOrWhiteSpace(contentType) ? new StringHttpContentWriter() : requestContentWriters.First(w => w.SupportsContentType(contentType));
|
||||
private IHttpContentFactory SelectContentWriter(string? contentType, IEnumerable<IHttpContentFactory> requestContentWriters) =>
|
||||
string.IsNullOrWhiteSpace(contentType) ? new JsonContentFactory() : requestContentWriters.First(w => w.SupportsContentType(contentType));
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Http.ContentWriters;
|
||||
using Elsa.Workflows.Core.Attributes;
|
||||
using Elsa.Workflows.Core.Models;
|
||||
using Elsa.Workflows.Management.Models;
|
||||
|
|
@ -25,8 +26,8 @@ public class WriteHttpResponse : CodeActivity
|
|||
/// <summary>
|
||||
/// The content to write back.
|
||||
/// </summary>
|
||||
[Input(Description = "The content to write back.")]
|
||||
public Input<string?> Content { get; set; } = new("");
|
||||
[Input(Description = "The content to write back. String values will be sent as-is, while objects will be serialized to a JSON string. Byte arrays and streams will be sent as files.")]
|
||||
public Input<object?> Content { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The content type to use when returning the response.
|
||||
|
|
@ -78,15 +79,30 @@ public class WriteHttpResponse : CodeActivity
|
|||
|
||||
private async Task WriteResponseAsync(ActivityExecutionContext context, HttpResponse response)
|
||||
{
|
||||
// Set status code.
|
||||
response.StatusCode = (int)context.Get(StatusCode);
|
||||
|
||||
// Add headers.
|
||||
var headers = ResponseHeaders.TryGet(context) ?? new HttpResponseHeaders();
|
||||
foreach (var header in headers)
|
||||
response.Headers.Add(header.Key, header.Value);
|
||||
|
||||
// Get content and content type.
|
||||
var content = context.Get(Content);
|
||||
|
||||
if (content != null)
|
||||
await response.WriteAsync(content, context.CancellationToken);
|
||||
if (content == null)
|
||||
return;
|
||||
|
||||
var contentType = ContentType.Get(context) ?? DetermineContentType(content);
|
||||
var contentWriter = context.GetServices<IHttpContentFactory>().FirstOrDefault(x => x.SupportsContentType(contentType)) ?? new TextContentFactory();
|
||||
var httpContent = contentWriter.CreateHttpContent(content, contentType);
|
||||
|
||||
// Set content type.
|
||||
response.ContentType = httpContent.Headers.ContentType?.ToString() ?? contentType;
|
||||
|
||||
// Write content.
|
||||
await httpContent.CopyToAsync(response.Body);
|
||||
}
|
||||
|
||||
private string DetermineContentType(object? content) => content is byte[] or Stream ? "application/octet-stream" : "text/plain";
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
namespace Elsa.Http.Constants;
|
||||
|
||||
public static class MimeTypes
|
||||
{
|
||||
public const string ApplicationXml = "application/xml";
|
||||
public const string ApplicationJson = "application/json";
|
||||
public const string ApplicationWwwFormUrlEncoded = "application/x-www-form-urlencoded";
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Elsa.Http.ContentWriters;
|
||||
|
||||
/// <summary>
|
||||
/// A content writer that writes content in the application/x-www-form-urlencoded format.
|
||||
/// </summary>
|
||||
public class FormUrlEncodedHttpContentFactory : IHttpContentFactory
|
||||
{
|
||||
private readonly List<string> _supportedContentTypes = new() { "application/x-www-form-urlencoded" };
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool SupportsContentType(string contentType) => _supportedContentTypes.Contains(contentType);
|
||||
|
||||
/// <inheritdoc />
|
||||
public HttpContent CreateHttpContent(object content, string? contentType = null) => new FormUrlEncodedContent(GetContentAsDictionary(content));
|
||||
|
||||
private static Dictionary<string, string> GetContentAsDictionary(object content) =>
|
||||
(content is string or JsonObject
|
||||
? JsonSerializer.Deserialize<Dictionary<string, string>>(JsonSerializer.Serialize(content))
|
||||
: (Dictionary<string, string>)Convert.ChangeType(content, typeof(Dictionary<string, string>)))!;
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Elsa.Http.Constants;
|
||||
|
||||
namespace Elsa.Http.ContentWriters;
|
||||
|
||||
/// <summary>
|
||||
/// A content writer that writes content in the application/x-www-form-urlencoded format.
|
||||
/// </summary>
|
||||
public class FormUrlEncodedHttpContentWriter : IHttpContentWriter
|
||||
{
|
||||
private readonly List<string> _supportedContentTypes = new() {MimeTypes.ApplicationWwwFormUrlEncoded};
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool SupportsContentType(string contentType) => _supportedContentTypes.Contains(contentType);
|
||||
|
||||
/// <inheritdoc />
|
||||
public HttpContent GetContent<T>( T content, string? contentType = null) => new FormUrlEncodedContent(GetContentAsDictionary(content));
|
||||
|
||||
private static Dictionary<string, string> GetContentAsDictionary<TType>(TType body) =>
|
||||
(body is string || body is JsonObject ?
|
||||
JsonSerializer.Deserialize<Dictionary<string, string>>(JsonSerializer.Serialize(body)) :
|
||||
(Dictionary<string, string>)Convert.ChangeType(body, typeof(Dictionary<string, string>))!)!;
|
||||
}
|
||||
17
src/modules/Elsa.Http/ContentWriters/IHttpContentFactory.cs
Normal file
17
src/modules/Elsa.Http/ContentWriters/IHttpContentFactory.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
namespace Elsa.Http.ContentWriters;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a concrete <see cref="HttpContent"/> instance based on the specified content type.
|
||||
/// </summary>
|
||||
public interface IHttpContentFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this factory supports the specified content type.
|
||||
/// </summary>
|
||||
bool SupportsContentType(string contentType);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a concrete <see cref="HttpContent"/> derivative based on the specified content type.
|
||||
/// </summary>
|
||||
HttpContent CreateHttpContent(object content, string contentType);
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
namespace Elsa.Http.ContentWriters;
|
||||
|
||||
public interface IHttpContentWriter
|
||||
{
|
||||
bool SupportsContentType(string contentType);
|
||||
HttpContent GetContent<T>(T content, string? contentType = null);
|
||||
}
|
||||
27
src/modules/Elsa.Http/ContentWriters/JsonContentFactory.cs
Normal file
27
src/modules/Elsa.Http/ContentWriters/JsonContentFactory.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Net.Mime;
|
||||
using System.Text;
|
||||
|
||||
namespace Elsa.Http.ContentWriters;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="StringContent"/> object for application/json.
|
||||
/// </summary>
|
||||
public class JsonContentFactory : IHttpContentFactory
|
||||
{
|
||||
private readonly List<string> _supportedContentTypes = new() { MediaTypeNames.Application.Json };
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool SupportsContentType(string contentType) => _supportedContentTypes.Contains(contentType);
|
||||
|
||||
/// <inheritdoc />
|
||||
public HttpContent CreateHttpContent(object content, string contentType)
|
||||
{
|
||||
if (content is string s)
|
||||
return new StringContent(s, Encoding.UTF8, contentType);
|
||||
|
||||
var mediaType = MediaTypeHeaderValue.Parse(contentType);
|
||||
return JsonContent.Create(content, mediaType);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Elsa.Http.Constants;
|
||||
|
||||
namespace Elsa.Http.ContentWriters;
|
||||
|
||||
public class StringHttpContentWriter : IHttpContentWriter
|
||||
{
|
||||
private List<string> SupportedContentTypes = new() {MimeTypes.ApplicationJson, MimeTypes.ApplicationXml};
|
||||
|
||||
public bool SupportsContentType(string contentType)
|
||||
{
|
||||
return SupportedContentTypes.Contains(contentType);
|
||||
}
|
||||
|
||||
public HttpContent GetContent<T>(T content, string? contentType = null)
|
||||
{
|
||||
var serializedContent = JsonSerializer.Serialize(content);
|
||||
return new StringContent(serializedContent, Encoding. UTF8, contentType);
|
||||
}
|
||||
}
|
||||
28
src/modules/Elsa.Http/ContentWriters/TextContentFactory.cs
Normal file
28
src/modules/Elsa.Http/ContentWriters/TextContentFactory.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using System.Net.Mime;
|
||||
using System.Text;
|
||||
|
||||
namespace Elsa.Http.ContentWriters;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="StringContent"/> object for application/json, application/xml, text/json and text/xml content types.
|
||||
/// </summary>
|
||||
public class TextContentFactory : IHttpContentFactory
|
||||
{
|
||||
private readonly List<string> _supportedContentTypes = new()
|
||||
{
|
||||
MediaTypeNames.Text.Plain,
|
||||
MediaTypeNames.Text.Xml,
|
||||
MediaTypeNames.Text.RichText,
|
||||
MediaTypeNames.Text.Html,
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool SupportsContentType(string contentType) => _supportedContentTypes.Contains(contentType);
|
||||
|
||||
/// <inheritdoc />
|
||||
public HttpContent CreateHttpContent(object content, string contentType)
|
||||
{
|
||||
var text = content as string ?? "";
|
||||
return new StringContent(text, Encoding.UTF8, contentType);
|
||||
}
|
||||
}
|
||||
|
|
@ -78,14 +78,15 @@ public class HttpFeature : FeatureBase
|
|||
.AddNotificationHandlersFrom<UpdateRouteTable>()
|
||||
.AddHttpContextAccessor()
|
||||
|
||||
// Add Content Parsers.
|
||||
// Add content parsers.
|
||||
.AddSingleton<IHttpContentParser, StringHttpContentParser>()
|
||||
.AddSingleton<IHttpContentParser, JsonHttpContentParser>()
|
||||
.AddSingleton<IHttpContentParser, XmlHttpContentParser>()
|
||||
|
||||
// Add Request Content Writers.
|
||||
.AddSingleton<IHttpContentWriter, StringHttpContentWriter>()
|
||||
.AddSingleton<IHttpContentWriter, FormUrlEncodedHttpContentWriter>()
|
||||
// Add HTTP content factories.
|
||||
.AddSingleton<IHttpContentFactory, TextContentFactory>()
|
||||
.AddSingleton<IHttpContentFactory, JsonContentFactory>()
|
||||
.AddSingleton<IHttpContentFactory, FormUrlEncodedHttpContentFactory>()
|
||||
;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue