Skip to content

Commit

Permalink
Allow multiple value processing for Form and QueryString values. (#4736)
Browse files Browse the repository at this point in the history
* Allow multiple value processing for Form and QueryString values.

* refactor HTTPEndpoint

* add multi-part form handling

* cleanup

* reoder form handling code
  • Loading branch information
mohdali authored Jan 21, 2024
1 parent 5edda57 commit 00331f7
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 99 deletions.
78 changes: 45 additions & 33 deletions src/modules/Elsa.Http/Activities/HttpEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@ private async Task HandleRequestAsync(ActivityExecutionContext context, HttpCont
var routeData = GetRouteData(httpContext, path);

var routeDictionary = routeData.Values.ToDictionary(route => route.Key, route => route.Value!);
var queryStringDictionary = httpContext.Request.Query.ToDictionary<KeyValuePair<string, StringValues>, string, object>(queryString => queryString.Key, queryString => queryString.Value[0]!);
var headersDictionary = httpContext.Request.Headers.ToDictionary<KeyValuePair<string, StringValues>, string, object>(header => header.Key, header => header.Value[0]!);
var queryStringDictionary = httpContext.Request.Query.ToObjectDictionary();
var headersDictionary = httpContext.Request.Headers.ToObjectDictionary();

context.Set(RouteData, routeDictionary);
context.Set(QueryStringData, queryStringDictionary);
Expand All @@ -225,48 +225,60 @@ private async Task HandleRequestAsync(ActivityExecutionContext context, HttpCont
return;
}

// Read content, if any.
try
// Handle Form Fields
if (request.HasFormContentType)
{
var content = await ParseContentAsync(context, request);
ParsedContent.Set(context, content);
}
catch (JsonException e)
{
await HandleInvalidJsonPayloadAsync(context, httpContext, e);
throw;
}

// Read files, if any.
var files = ReadFilesAsync(context, request);
var formFields = request.Form.ToObjectDictionary();

if (files.Any())
{
if (!ValidateFileSizes(context, httpContext, files))
{
await HandleFileSizeTooLargeAsync(context, httpContext);
return;
}
ParsedContent.Set(context, formFields);

if (!ValidateFileExtensionWhitelist(context, httpContext, files))
// Read files, if any.
var files = ReadFilesAsync(context, request);

if (files.Any())
{
await HandleInvalidFileExtensionWhitelistAsync(context, httpContext);
return;
if (!ValidateFileSizes(context, httpContext, files))
{
await HandleFileSizeTooLargeAsync(context, httpContext);
return;
}

if (!ValidateFileExtensionWhitelist(context, httpContext, files))
{
await HandleInvalidFileExtensionWhitelistAsync(context, httpContext);
return;
}

if (!ValidateFileExtensionBlacklist(context, httpContext, files))
{
await HandleInvalidFileExtensionBlacklistAsync(context, httpContext);
return;
}

if (!ValidateFileMimeTypes(context, httpContext, files))
{
await HandleInvalidFileMimeTypesAsync(context, httpContext);
return;
}

Files.Set(context, files.ToArray());
}

if (!ValidateFileExtensionBlacklist(context, httpContext, files))
}
else
{
// Parse Non-Form content.
try
{
await HandleInvalidFileExtensionBlacklistAsync(context, httpContext);
return;
var content = await ParseContentAsync(context, request);
ParsedContent.Set(context, content);
}

if (!ValidateFileMimeTypes(context, httpContext, files))
catch (JsonException e)
{
await HandleInvalidFileMimeTypesAsync(context, httpContext);
return;
await HandleInvalidJsonPayloadAsync(context, httpContext, e);
throw;
}

Files.Set(context, files.ToArray());
}

// Complete.
Expand Down
28 changes: 28 additions & 0 deletions src/modules/Elsa.Http/Extensions/StringValueExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Elsa.Extensions;

/// <summary>
/// Contains extension methods for the <see cref="StringValues"/> Enumerable dictonaries.
/// </summary>
public static class StringValueExtensions
{
/// <summary>
/// Convert the collection to the desired dictionary type.
/// </summary>
/// <param name="collection"></param>
/// <returns></returns>
public static Dictionary<string, object> ToObjectDictionary(this IEnumerable<KeyValuePair<string, StringValues>> collection)
{
return collection.ToDictionary<KeyValuePair<string, StringValues>, string, object>(
item => item.Key,
item => item.Value.Count <= 1 ?
item.Value[0]!
: item.Value.ToArray());
}
}
10 changes: 4 additions & 6 deletions src/modules/Elsa.Http/Features/HttpFeature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,10 @@ public override void Apply()
.AddRequestHandler<ValidateWorkflowRequestHandler, ValidateWorkflowRequest, ValidateWorkflowResponse>()
.AddNotificationHandler<UpdateRouteTable>()

// Content parsers.
.AddScoped<IHttpContentParser, StringHttpContentParser>()
.AddScoped<IHttpContentParser, JsonHttpContentParser>()
.AddScoped<IHttpContentParser, XmlHttpContentParser>()
.AddScoped<IHttpContentParser, FormHttpContentParser>()

// Content parsers.
.AddSingleton<IHttpContentParser, JsonHttpContentParser>()
.AddSingleton<IHttpContentParser, XmlHttpContentParser>()

// HTTP content factories.
.AddScoped<IHttpContentFactory, TextContentFactory>()
.AddScoped<IHttpContentFactory, JsonContentFactory>()
Expand Down
28 changes: 0 additions & 28 deletions src/modules/Elsa.Http/Parsers/FormHttpContentParser.cs

This file was deleted.

24 changes: 0 additions & 24 deletions src/modules/Elsa.Http/Parsers/StringHttpContentParser.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ public async Task<string> SerializeAsync(WorkflowState workflowState, Cancellati
{
var options = GetSerializerOptions();
var serializingWorkflowState = new SerializingWorkflowState(options);
await _notificationSender.SendAsync(serializingWorkflowState, cancellationToken);
await _notificationSender.SendAsync(serializingWorkflowState, cancellationToken);

return JsonSerializer.Serialize(workflowState, options);
}

Expand All @@ -46,8 +46,8 @@ public async Task<byte[]> SerializeToUtfBytesAsync(WorkflowState workflowState,
{
var options = GetSerializerOptions();
var serializingWorkflowState = new SerializingWorkflowState(options);
await _notificationSender.SendAsync(serializingWorkflowState, cancellationToken);
await _notificationSender.SendAsync(serializingWorkflowState, cancellationToken);

return JsonSerializer.SerializeToUtf8Bytes(workflowState, options);
}

Expand All @@ -56,8 +56,8 @@ public async Task<JsonElement> SerializeToElementAsync(WorkflowState workflowSta
{
var options = GetSerializerOptions();
var serializingWorkflowState = new SerializingWorkflowState(options);
await _notificationSender.SendAsync(serializingWorkflowState, cancellationToken);
await _notificationSender.SendAsync(serializingWorkflowState, cancellationToken);

return JsonSerializer.SerializeToElement(workflowState, options);
}

Expand Down Expand Up @@ -110,8 +110,8 @@ private JsonSerializerOptions GetSerializerOptions()
options.Converters.Add(JsonMetadataServices.TimeSpanConverter);
options.Converters.Add(new PolymorphicObjectConverterFactory());
options.Converters.Add(new TypeJsonConverter(_wellKnownTypeRegistry));
options.Converters.Add(new VariableConverterFactory(_wellKnownTypeRegistry, _loggerFactory));
options.Converters.Add(new VariableConverterFactory(_wellKnownTypeRegistry, _loggerFactory));

return options;
}
}

0 comments on commit 00331f7

Please sign in to comment.