Skip to content

Commit fe43821

Browse files
committed
Add new extension method for multipart/form-data
1 parent 2223e07 commit fe43821

File tree

5 files changed

+109
-24
lines changed

5 files changed

+109
-24
lines changed

src/Aliencube.AzureFunctions.Extensions.Common/ContentTypes.cs

+10
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,15 @@ public static class ContentTypes
2424
/// Identifies text/vnd.yaml.
2525
/// </summary>
2626
public const string TextVndYaml = "text/vnd.yaml";
27+
28+
/// <summary>
29+
/// Identifies application/x-www-form-urlencoded.
30+
/// </summary>
31+
public const string ApplicationFormUrlEncoded = "application/x-www-form-urlencoded";
32+
33+
/// <summary>
34+
/// Identifies multipart/form-data.
35+
/// </summary>
36+
public const string MultipartFormData = "multipart/form-data";
2737
}
2838
}

src/Aliencube.AzureFunctions.Extensions.Common/HttpRequestExtensions.cs

+34-15
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
using System;
2-
using System.Collections.Generic;
32
using System.IO;
3+
using System.Linq;
4+
using System.Net.Http;
45
using System.Threading.Tasks;
56

67
using Microsoft.AspNetCore.Http;
7-
using Microsoft.Extensions.Primitives;
88

99
using Newtonsoft.Json;
1010

@@ -47,29 +47,48 @@ public static async Task<T> To<T>(this HttpRequest req, SourceFrom source = Sour
4747
return result;
4848
}
4949

50-
private static async Task<T> ToFromHeader<T>(this HttpRequest req)
50+
/// <summary>
51+
/// Converts the <see cref="HttpRequest"/> to the <see cref="MultipartFormDataContent"/> instance.
52+
/// </summary>
53+
/// <param name="req"><see cref="HttpRequest"/> instance.</param>
54+
/// <returns>Returns the <see cref="MultipartFormDataContent"/> instance.</returns>
55+
public static async Task<MultipartFormDataContent> ToMultipartFormDataContent(this HttpRequest req)
5156
{
52-
if (!typeof(IEnumerable<KeyValuePair<string, StringValues>>).IsAssignableFrom(typeof(T)))
57+
if (!req.Form.Files.Any())
5358
{
54-
throw new InvalidOperationException(ExceptionMessages.InvalidType);
59+
return null;
5560
}
5661

57-
var result = (T)req.Headers;
62+
var mpfd = new MultipartFormDataContent();
63+
foreach (var file in req.Form.Files)
64+
{
65+
var content = default(ByteArrayContent);
66+
using (var ms = new MemoryStream())
67+
{
68+
await file.CopyToAsync(ms).ConfigureAwait(false);
69+
content = new ByteArrayContent(ms.ToArray());
70+
}
71+
72+
mpfd.Add(content, file.Name, file.FileName);
73+
}
5874

59-
return await Task.FromResult(result).ConfigureAwait(false);
75+
return mpfd;
6076
}
6177

62-
private static async Task<T> ToFromQuery<T>(this HttpRequest req)
78+
private static async Task<T> ToFromHeader<T>(this HttpRequest req)
6379
{
64-
var type = typeof(T);
65-
if (!typeof(IEnumerable<KeyValuePair<string, StringValues>>).IsAssignableFrom(typeof(T)))
66-
{
67-
throw new InvalidOperationException(ExceptionMessages.InvalidType);
68-
}
80+
var serialised = JsonConvert.SerializeObject(req.Headers.ToDictionary(p => p.Key, p => p.Value.ToString()));
81+
var deserialised = JsonConvert.DeserializeObject<T>(serialised);
6982

70-
var result = (T)req.Query;
83+
return await Task.FromResult(deserialised).ConfigureAwait(false);
84+
}
85+
86+
private static async Task<T> ToFromQuery<T>(this HttpRequest req)
87+
{
88+
var serialised = JsonConvert.SerializeObject(req.Query.ToDictionary(p => p.Key, p => p.Value.ToString()));
89+
var deserialised = JsonConvert.DeserializeObject<T>(serialised);
7190

72-
return await Task.FromResult(result).ConfigureAwait(false);
91+
return await Task.FromResult(deserialised).ConfigureAwait(false);
7392
}
7493

7594
private static async Task<T> ToFromBody<T>(this HttpRequest req)

test/Aliencube.AzureFunctions.Extensions.Common.Tests/HttpRequestExtensionsTests.cs

+48-9
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
using System.Collections;
33
using System.Collections.Generic;
44
using System.IO;
5-
using System.Linq;
65
using System.Text;
6+
using System.Threading;
77
using System.Threading.Tasks;
88

99
using Aliencube.AzureFunctions.Extensions.Common.Tests.Models;
@@ -48,7 +48,7 @@ public void Given_InvalidType_When_To_Invoked_Then_It_Should_Throw_Exception(Sou
4848
}
4949

5050
[DataTestMethod]
51-
[DataRow("key1", "value1")]
51+
[DataRow("x-secret-key", "hello-world")]
5252
public async Task Given_Header_When_To_Invoked_Then_It_Should_Return_Result(string key, string value)
5353
{
5454
var store = new Dictionary<string, StringValues>() { { key, value } };
@@ -57,15 +57,14 @@ public async Task Given_Header_When_To_Invoked_Then_It_Should_Return_Result(stri
5757
var req = new Mock<HttpRequest>();
5858
req.SetupGet(p => p.Headers).Returns(headers);
5959

60-
var result = await HttpRequestExtensions.To<IHeaderDictionary>(req.Object, SourceFrom.Header).ConfigureAwait(false);
60+
var result = await HttpRequestExtensions.To<FakeRequestHeader>(req.Object, SourceFrom.Header).ConfigureAwait(false);
6161

6262
result.Should().NotBeNull();
63-
result.Should().HaveCount(store.Count);
64-
result.Should().ContainKey(store.First().Key);
63+
result.SecretKey.Should().Be(value);
6564
}
6665

6766
[DataTestMethod]
68-
[DataRow("key1", "value1")]
67+
[DataRow("key", "value")]
6968
public async Task Given_Query_When_To_Invoked_Then_It_Should_Return_Result(string key, string value)
7069
{
7170
var store = new Dictionary<string, StringValues>() { { key, value } };
@@ -74,11 +73,10 @@ public async Task Given_Query_When_To_Invoked_Then_It_Should_Return_Result(strin
7473
var req = new Mock<HttpRequest>();
7574
req.SetupGet(p => p.Query).Returns(queries);
7675

77-
var result = await HttpRequestExtensions.To<IQueryCollection>(req.Object, SourceFrom.Query).ConfigureAwait(false);
76+
var result = await HttpRequestExtensions.To<FakeRequestQuery>(req.Object, SourceFrom.Query).ConfigureAwait(false);
7877

7978
result.Should().NotBeNull();
80-
result.Should().HaveCount(store.Count);
81-
result.Should().ContainKey(store.First().Key);
79+
result.Key.Should().Be(value);
8280
}
8381

8482
[DataTestMethod]
@@ -101,5 +99,46 @@ public async Task Given_Payload_When_To_Invoked_Then_It_Should_Return_Result(str
10199

102100
await stream.DisposeAsync().ConfigureAwait(false);
103101
}
102+
103+
[TestMethod]
104+
public async Task Given_Null_Payload_When_ToMultipartFormDataContent_Invoked_Then_It_Should_Return_Null()
105+
{
106+
var files = new FormFileCollection();
107+
108+
var form = new Mock<IFormCollection>();
109+
form.SetupGet(p => p.Files).Returns(files);
110+
111+
var req = new Mock<HttpRequest>();
112+
req.SetupGet(p => p.Form).Returns(form.Object);
113+
114+
var result = await HttpRequestExtensions.ToMultipartFormDataContent(req.Object).ConfigureAwait(false);
115+
116+
result.Should().BeNull();
117+
}
118+
119+
[DataTestMethod]
120+
[DataRow("hello", "world.jpg")]
121+
public async Task Given_Payload_When_ToMultipartFormDataContent_Invoked_Then_It_Should_Return_Result(string name, string filename)
122+
{
123+
var file = new Mock<IFormFile>();
124+
file.SetupGet(p => p.Name).Returns(name);
125+
file.SetupGet(p => p.FileName).Returns(filename);
126+
file.Setup(p => p.CopyToAsync(It.IsAny<Stream>(), It.IsAny<CancellationToken>())).Returns(Task.CompletedTask);
127+
128+
var files = new FormFileCollection
129+
{
130+
file.Object
131+
};
132+
133+
var form = new Mock<IFormCollection>();
134+
form.SetupGet(p => p.Files).Returns(files);
135+
136+
var req = new Mock<HttpRequest>();
137+
req.SetupGet(p => p.Form).Returns(form.Object);
138+
139+
var result = await HttpRequestExtensions.ToMultipartFormDataContent(req.Object).ConfigureAwait(false);
140+
141+
result.Should().NotBeNull();
142+
}
104143
}
105144
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using Newtonsoft.Json;
2+
3+
namespace Aliencube.AzureFunctions.Extensions.Common.Tests.Models
4+
{
5+
public class FakeRequestHeader
6+
{
7+
[JsonProperty("x-secret-key")]
8+
public string? SecretKey { get; set; }
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace Aliencube.AzureFunctions.Extensions.Common.Tests.Models
2+
{
3+
public class FakeRequestQuery
4+
{
5+
public string? Key { get; set; }
6+
}
7+
}

0 commit comments

Comments
 (0)