Remove StringValuesModel
Deserialization causes problems when a value is a single string
This commit is contained in:
parent
c6ad488a0b
commit
a6d05cfce0
|
|
@ -70,8 +70,8 @@ namespace Elsa.Activities.Http
|
|||
var model = new HttpRequestModel
|
||||
{
|
||||
Path = new Uri(request.Path.ToString(), UriKind.Relative),
|
||||
QueryString = request.Query.ToDictionary(x => x.Key, x => new StringValuesModel(x.Value)),
|
||||
Headers = request.Headers.ToDictionary(x => x.Key, x => new StringValuesModel(x.Value)),
|
||||
QueryString = request.Query.ToDictionary(x => x.Key, x => x.Value.ToString()),
|
||||
Headers = request.Headers.ToDictionary(x => x.Key, x => x.Value.ToString()),
|
||||
Method = request.Method
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ using Elsa.Design;
|
|||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using HttpRequestHeaders = Elsa.Activities.Http.Models.HttpRequestHeaders;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
|
|
@ -139,7 +140,7 @@ namespace Elsa.Activities.Http
|
|||
var url = Url;
|
||||
var request = new HttpRequestMessage(new HttpMethod(Method), url);
|
||||
var authorizationHeaderValue = Authorization;
|
||||
var requestHeaders = new HeaderDictionary(RequestHeaders);
|
||||
var requestHeaders = new HeaderDictionary(RequestHeaders.ToDictionary(x => x.Key, x => new StringValues(x.Value.Split(','))));
|
||||
|
||||
if (methodSupportsBody)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ using Microsoft.Extensions.Primitives;
|
|||
|
||||
namespace Elsa.Activities.Http.Models
|
||||
{
|
||||
public class HttpRequestHeaders : Dictionary<string, StringValues>
|
||||
public class HttpRequestHeaders : Dictionary<string, string>
|
||||
{
|
||||
public string ContentType => this["content-type"];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ namespace Elsa.Activities.Http.Models
|
|||
{
|
||||
public Uri Path { get; set; } = default!;
|
||||
public string Method { get; set; } = default!;
|
||||
public IDictionary<string, StringValuesModel> QueryString { get; set; } = default!;
|
||||
public IDictionary<string, StringValuesModel> Headers { get; set; } = default!;
|
||||
public IDictionary<string, string> QueryString { get; set; } = default!;
|
||||
public IDictionary<string, string> Headers { get; set; } = default!;
|
||||
public object? Body { get; set; }
|
||||
public T GetBody<T>() => (T)Body!;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ using Microsoft.Extensions.Primitives;
|
|||
|
||||
namespace Elsa.Activities.Http.Models
|
||||
{
|
||||
public class HttpResponseHeaders : Dictionary<string, StringValues>
|
||||
public class HttpResponseHeaders : Dictionary<string, string>
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ namespace Elsa.Activities.Http.Models
|
|||
public class HttpResponseModel
|
||||
{
|
||||
public HttpStatusCode StatusCode { get; set; }
|
||||
public Dictionary<string, string[]> Headers { get; set; } = new Dictionary<string, string[]>();
|
||||
public Dictionary<string, string[]> Headers { get; set; } = new();
|
||||
public object? Content { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Elsa.Activities.Http.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a collection of zero or more string values. It is used for values such as
|
||||
/// HTTP header/query string values. These are often thought-of as key/value pairs but might
|
||||
/// have more than one value.
|
||||
/// </summary>
|
||||
public class StringValuesModel : IConvertible
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets either the first value of <see cref="Values"/> or a <see langword="null"/> reference
|
||||
/// if the values are null or empty.
|
||||
/// </summary>
|
||||
/// <returns>The first value</returns>
|
||||
public string? Value => Values?.FirstOrDefault();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of the string values for the current instance.
|
||||
/// </summary>
|
||||
/// <value>The values</value>
|
||||
public string[] Values { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an instance of <see cref="StringValuesModel"/> with an empty collection of values.
|
||||
/// </summary>
|
||||
public StringValuesModel() => Values = new string[0];
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an instance of <see cref="StringValuesModel"/> from a <see cref="StringValues"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">A model of zero or more string values.</param>
|
||||
public StringValuesModel(StringValues value) => Values = value.ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string representation of the current instance. Either null (for no, or an empty-collection of <see cref="Values"/>),
|
||||
/// a single string (for a collection of one <see cref="Values"/>) or a comma-separated string of many values.
|
||||
/// </summary>
|
||||
/// <returns>The string representation of the <see cref="Values"/>.</returns>
|
||||
public override string? ToString() => (Values?.Any() == true) ? string.Join(",", Values) : null;
|
||||
|
||||
#region IConvertible implementation
|
||||
|
||||
TypeCode IConvertible.GetTypeCode() => TypeCode.Object;
|
||||
bool IConvertible.ToBoolean(IFormatProvider? provider) => Convert.ToBoolean(ToString(), provider);
|
||||
byte IConvertible.ToByte(IFormatProvider? provider) => Convert.ToByte(ToString(), provider);
|
||||
char IConvertible.ToChar(IFormatProvider? provider) => Convert.ToChar(ToString()!, provider);
|
||||
DateTime IConvertible.ToDateTime(IFormatProvider? provider) => Convert.ToDateTime(ToString(), provider);
|
||||
decimal IConvertible.ToDecimal(IFormatProvider? provider) => Convert.ToDecimal(ToString(), provider);
|
||||
double IConvertible.ToDouble(IFormatProvider? provider) => Convert.ToDouble(ToString(), provider);
|
||||
short IConvertible.ToInt16(IFormatProvider? provider) => Convert.ToInt16(ToString(), provider);
|
||||
int IConvertible.ToInt32(IFormatProvider? provider) => Convert.ToInt32(ToString(), provider);
|
||||
long IConvertible.ToInt64(IFormatProvider? provider) => Convert.ToInt64(ToString(), provider);
|
||||
sbyte IConvertible.ToSByte(IFormatProvider? provider) => Convert.ToSByte(ToString()!, provider);
|
||||
float IConvertible.ToSingle(IFormatProvider? provider) => Convert.ToSingle(ToString(), provider);
|
||||
string IConvertible.ToString(IFormatProvider? provider) => ToString()!;
|
||||
object IConvertible.ToType(Type conversionType, IFormatProvider? provider) => Convert.ChangeType(ToString()!, conversionType, provider);
|
||||
ushort IConvertible.ToUInt16(IFormatProvider? provider) => Convert.ToUInt16(ToString(), provider);
|
||||
uint IConvertible.ToUInt32(IFormatProvider? provider) => Convert.ToUInt32(ToString(), provider);
|
||||
ulong IConvertible.ToUInt64(IFormatProvider? provider) => Convert.ToUInt64(ToString(), provider);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -12,14 +12,15 @@ namespace Elsa.Activities.Http.Parsers
|
|||
{
|
||||
public int Priority => 0;
|
||||
public string?[] SupportedContentTypes => new[] { "application/x-www-form-urlencoded" };
|
||||
|
||||
|
||||
public async Task<object?> ParseAsync(HttpRequest request, Type? targetType = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var form = await request.ReadFormAsync(cancellationToken);
|
||||
|
||||
return form.ToDictionary(
|
||||
x => x.Key,
|
||||
x => new StringValuesModel(x.Value)
|
||||
);
|
||||
x => x.Key,
|
||||
x => x.Value.ToString())
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ namespace Elsa.Samples.Server.Host.Workflows
|
|||
private void MaybeThrow(ActivityExecutionContext context)
|
||||
{
|
||||
var model = context.GetInput<HttpRequestModel>()!;
|
||||
var fault = model.QueryString.GetItem("fault")?.Value == "true";
|
||||
var fault = model.QueryString.GetItem("fault") == "true";
|
||||
|
||||
if (fault)
|
||||
throw new Exception("This is quite a serious fault!");
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ namespace Elsa.Samples.Server.Host.Workflows
|
|||
.Correlate(() => Guid.NewGuid().ToString("N"))
|
||||
.WriteHttpResponse(x => x.WithStatusCode(HttpStatusCode.OK).WithContent(context => $"Tell me your name please. Use correlation ID {context.WorkflowExecutionContext.CorrelationId}").WithContentType("text/plain"))
|
||||
.HttpEndpoint(x => x.WithPath("/signup").WithMethod(HttpMethod.Post.ToString()).WithReadContent())
|
||||
.SetVariable("Name", context => (string) context.GetInput<HttpRequestModel>().Body)
|
||||
.SetVariable("Name", context => (string) context.GetInput<HttpRequestModel>()!.Body!)
|
||||
.SetName(context => context.GetVariable<string>("Name"))
|
||||
|
||||
.WriteHttpResponse(x => x
|
||||
|
|
|
|||
|
|
@ -1,96 +0,0 @@
|
|||
using System;
|
||||
using Elsa.Activities.Http.Models;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Xunit;
|
||||
|
||||
namespace Elsa.Activities.Http
|
||||
{
|
||||
public class StringValuesModelTests
|
||||
{
|
||||
[Theory(DisplayName = "It should be possible to use Convert.ChangeType to convert an instance to string. This is primarily to support usage via Jint."), AutoMoqData]
|
||||
public void ItShouldBePossibleToConvertAStringValuesModelToString(string[] values)
|
||||
{
|
||||
var sut = new StringValuesModel(new StringValues(values));
|
||||
var result = Convert.ChangeType(sut, typeof(string));
|
||||
Assert.Equal(sut.ToString(), result);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "It should be possible to use Convert.ToInt32 to convert an instance to a number if the value is a valid string representation of a number. This is primarily to support usage via Jint.")]
|
||||
public void ItShouldBePossibleToConvertAStringValuesModelToInt32IfValid()
|
||||
{
|
||||
var sut = new StringValuesModel(new StringValues("3"));
|
||||
var result = Convert.ChangeType(sut, typeof(int));
|
||||
Assert.Equal(3, result);
|
||||
}
|
||||
|
||||
[Theory(DisplayName = "The Value property should return the first value if there is more than one."), AutoMoqData]
|
||||
public void ValueShouldReturnFirstItemIfThereAreMoreThanOne(string firstValue, string secondValue)
|
||||
{
|
||||
var sut = new StringValuesModel(new StringValues(new [] { firstValue, secondValue }));
|
||||
Assert.Equal(firstValue, sut.Value);
|
||||
}
|
||||
|
||||
[Theory(DisplayName = "The Value property should always return the first value if there is only one."), AutoMoqData]
|
||||
public void ValueShouldReturnFirstItemIfThereIsOne(string firstValue)
|
||||
{
|
||||
var sut = new StringValuesModel(new StringValues(new [] { firstValue }));
|
||||
Assert.Equal(firstValue, sut.Value);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "The Value property should return null if there are no values.")]
|
||||
public void ValueShouldReturnNullIfThereAreNone()
|
||||
{
|
||||
var sut = new StringValuesModel(new StringValues(new string[0]));
|
||||
Assert.Null(sut.Value);
|
||||
}
|
||||
|
||||
[Theory(DisplayName = "The Values property should return a length-one collection if there is a single value."), AutoMoqData]
|
||||
public void ValuesShouldReturnLengthOneCollectionIfThereIsOneValue(string firstValue)
|
||||
{
|
||||
var sut = new StringValuesModel(new StringValues(new [] { firstValue }));
|
||||
Assert.Equal(new [] {firstValue}, sut.Values);
|
||||
}
|
||||
|
||||
[Theory(DisplayName = "The Values property should return a length-two collection if there are two values."), AutoMoqData]
|
||||
public void ValuesShouldReturnLengthTwoCollectionIfThereAreTwoValues(string firstValue, string secondValue)
|
||||
{
|
||||
var sut = new StringValuesModel(new StringValues(new [] { firstValue, secondValue }));
|
||||
Assert.Equal(new [] {firstValue, secondValue}, sut.Values);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "The Values property should return an empty collection if there are no values.")]
|
||||
public void ValuesShouldReturnEmptyCollectionIfThereAreNone()
|
||||
{
|
||||
var sut = new StringValuesModel(new StringValues(new string[0]));
|
||||
Assert.Empty(sut.Values);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "The ToString method should return null if there are no values")]
|
||||
public void ToStringShouldReturnNullIfNotValues()
|
||||
{
|
||||
var sut = new StringValuesModel(new StringValues(new string[0]));
|
||||
Assert.Null(sut.ToString());
|
||||
}
|
||||
|
||||
[Theory(DisplayName = "The ToString method should return the first value if there is only one"), AutoMoqData]
|
||||
public void ToStringShouldReturnFirstValueIfThereIsOnlyOne(string firstValue)
|
||||
{
|
||||
var sut = new StringValuesModel(new StringValues(new [] { firstValue }));
|
||||
Assert.Equal(firstValue, sut.ToString());
|
||||
}
|
||||
|
||||
[Theory(DisplayName = "The ToString method should return comma-separated values if there are more than one"), AutoMoqData]
|
||||
public void ToStringShouldReturnCommaSeparatedValuesIfThereIsMoreThanOne(string firstValue, string secondValue)
|
||||
{
|
||||
var sut = new StringValuesModel(new StringValues(new [] { firstValue, secondValue }));
|
||||
Assert.Equal($"{firstValue},{secondValue}", sut.ToString());
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "The parameterless constructor should initialize Values to an empty collection")]
|
||||
public void ParameterlessCtorShouldInitializeWithEmptyCollection()
|
||||
{
|
||||
var sut = new StringValuesModel();
|
||||
Assert.Empty(sut.Values);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue