Skip to content

Commit f9724dc

Browse files
authored
Upload files with multipart content type
1 parent 709c410 commit f9724dc

File tree

6 files changed

+235
-2
lines changed

6 files changed

+235
-2
lines changed

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<Project>
22
<PropertyGroup>
3-
<Version>0.2.5</Version>
3+
<Version>0.2.6</Version>
44
</PropertyGroup>
55
</Project>

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,31 @@ using (var client = new HttpTesterClient())
6969
}
7070
```
7171

72+
**POST File Upload request**
73+
You can upload files with `multipart/form-data` content type like shown below.
74+
There are 2 overloads of `WithMultipart`, one for uploading binary data and the other for string data.
75+
76+
```csharp
77+
using (var client = new HttpTesterClient())
78+
{
79+
var response = await client
80+
.CreateHttpRequest(new Uri("https://qatoolkitapi.azurewebsites.net"))
81+
.WithQueryParams(new Dictionary<string, string>() { { "api-version", "1" } })
82+
.WithMethod(HttpMethod.Post)
83+
.WithMultipart(image, "FileContent", "logo.png")
84+
.WithMultipart("MetaData", "My metadata.")
85+
.WithPath("/api/bicycles/1/images")
86+
.Start();
87+
88+
var msg = await response.GetResponseBodyString();
89+
90+
Assert.Equal("File name: miha.txt, length: 119305", msg);
91+
Assert.True(response.IsSuccessStatusCode);
92+
}
93+
```
94+
95+
There is double content-type safety built-in and you can not do `WithMultipart` and `WithJsonBody` in the same request.
96+
7297
#### HttpTesterClient Authentication
7398

7499
Currently `HttpTesterClient` supports:

src/QAToolKit.Engine.HttpTester.Test/HttpTesterClientTests.cs

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ public async Task HttpTesterClientPostObjectBodyWithFulUrlWithNTLMAuthorization_
431431
.CreateHttpRequest(new Uri("https://qatoolkitapi.azurewebsites.net/api/bicycles?api-version=1"))
432432
.WithJsonBody(BicycleFixture.GetCfr())
433433
.WithMethod(HttpMethod.Post)
434-
.WithNTLMAuthentication("user","pass")
434+
.WithNTLMAuthentication("user", "pass")
435435
.Start();
436436

437437
var msg = await response.GetResponseBody<dynamic>();
@@ -441,5 +441,117 @@ public async Task HttpTesterClientPostObjectBodyWithFulUrlWithNTLMAuthorization_
441441
Assert.Equal("Giant", msg.brand.ToString());
442442
}
443443
}
444+
445+
[Fact]
446+
public async Task HttpTesterClientFileUpload_Success()
447+
{
448+
using (var client = new HttpTesterClient())
449+
{
450+
byte[] image = new WebClient().DownloadData("https://qatoolkit.io/assets/logo.png");
451+
452+
var response = await client
453+
.CreateHttpRequest(new Uri("https://qatoolkitapi.azurewebsites.net"))
454+
.WithQueryParams(new Dictionary<string, string>() { { "api-version", "2" } })
455+
.WithMethod(HttpMethod.Post)
456+
.WithPath("/api/bicycles/1/images")
457+
.WithMultipart(image, "FileContent", "logo.png")
458+
.WithMultipart("FileName", "miha.txt")
459+
.Start();
460+
461+
var msg = await response.GetResponseBodyString();
462+
463+
Assert.Equal("File name: miha.txt, length: 119305", msg);
464+
Assert.True(response.IsSuccessStatusCode);
465+
}
466+
}
467+
468+
[Fact]
469+
public async Task HttpTesterClientFileUpload2_Success()
470+
{
471+
using (var client = new HttpTesterClient())
472+
{
473+
byte[] image = new WebClient().DownloadData("https://qatoolkit.io/assets/logo.png");
474+
475+
var response = await client
476+
.CreateHttpRequest(new Uri("https://qatoolkitapi.azurewebsites.net"))
477+
.WithQueryParams(new Dictionary<string, string>() { { "api-version", "2" } })
478+
.WithMethod(HttpMethod.Post)
479+
.WithPath("/api/bicycles/1/images")
480+
.WithMultipart(image, "FileContent", "logo.png")
481+
.WithMultipart("FileName", "miha.txt")
482+
.Start();
483+
484+
var msg = await response.GetResponseBodyString();
485+
486+
Assert.Equal("File name: miha.txt, length: 119305", msg);
487+
Assert.True(response.IsSuccessStatusCode);
488+
}
489+
}
490+
491+
[Fact]
492+
public async Task HttpTesterClientBrochureUpload_Success()
493+
{
494+
using (var client = new HttpTesterClient())
495+
{
496+
byte[] image = new WebClient().DownloadData("https://qatoolkit.io/assets/logo.png");
497+
498+
var response = await client
499+
.CreateHttpRequest(new Uri("https://qatoolkitapi.azurewebsites.net"))
500+
.WithQueryParams(new Dictionary<string, string>() { { "api-version", "2" } })
501+
.WithMethod(HttpMethod.Post)
502+
.WithPath("/api/bicycles/1/brochures")
503+
.WithMultipart(image, "Image.FileContent", "logo.png")
504+
.WithMultipart("Image.FileName", "miha.txt")
505+
.WithMultipart("Metadata.Year", "2000")
506+
.WithMultipart("Metadata.Name", "Brochure 2000")
507+
.Start();
508+
509+
var msg = await response.GetResponseBodyString();
510+
511+
Assert.Equal("File name: Brochure 2000, image name: miha.txt, length: 119305", msg);
512+
Assert.True(response.IsSuccessStatusCode);
513+
}
514+
}
515+
516+
[Fact]
517+
public void HttpTesterClientBrochureUploadBodyPresent_Fails()
518+
{
519+
using (var client = new HttpTesterClient())
520+
{
521+
byte[] image = new WebClient().DownloadData("https://qatoolkit.io/assets/logo.png");
522+
523+
var response = client
524+
.CreateHttpRequest(new Uri("https://qatoolkitapi.azurewebsites.net"))
525+
.WithQueryParams(new Dictionary<string, string>() { { "api-version", "2" } })
526+
.WithMethod(HttpMethod.Post)
527+
.WithPath("/api/bicycles/1/brochures")
528+
.WithJsonBody<string>("1234");
529+
530+
var exception = Assert.Throws<QAToolKitEngineHttpTesterException>(() => client.WithMultipart(image, "Image.FileContent", "logo.png"));
531+
Assert.StartsWith("Body application/json already defined on", exception.Message);
532+
}
533+
}
534+
535+
[Fact]
536+
public void HttpTesterClientBrochureUploadMultipartPresent_Fails()
537+
{
538+
using (var client = new HttpTesterClient())
539+
{
540+
byte[] image = new WebClient().DownloadData("https://qatoolkit.io/assets/logo.png");
541+
542+
var response = client
543+
.CreateHttpRequest(new Uri("https://qatoolkitapi.azurewebsites.net"))
544+
.WithQueryParams(new Dictionary<string, string>() { { "api-version", "2" } })
545+
.WithMethod(HttpMethod.Post)
546+
.WithPath("/api/bicycles/1/brochures")
547+
.WithMultipart(image, "Image.FileContent", "logo.png")
548+
.WithMultipart("Image.FileName", "miha.txt")
549+
.WithMultipart("Metadata.Year", "2000")
550+
.WithMultipart("Metadata.Name", "Brochure 2000");
551+
552+
var exception = Assert.Throws<QAToolKitEngineHttpTesterException>(() => client.WithJsonBody<string>("1234"));
553+
Assert.StartsWith("Body multipart/form-data already defined", exception.Message);
554+
}
555+
}
444556
}
445557
}

src/QAToolKit.Engine.HttpTester/Extensions/HttpResponseMessageExtensions.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,25 @@ public static async Task<T> GetResponseBody<T>(this HttpResponseMessage httpResp
2020
var bodyResponse = await httpResponseMessage.Content.ReadAsStringAsync();
2121
return JsonConvert.DeserializeObject<T>(bodyResponse);
2222
}
23+
24+
/// <summary>
25+
/// Get response as string
26+
/// </summary>
27+
/// <param name="httpResponseMessage"></param>
28+
/// <returns></returns>
29+
public static async Task<string> GetResponseBodyString(this HttpResponseMessage httpResponseMessage)
30+
{
31+
return await httpResponseMessage.Content.ReadAsStringAsync();
32+
}
33+
34+
/// <summary>
35+
/// Get response as byte array
36+
/// </summary>
37+
/// <param name="httpResponseMessage"></param>
38+
/// <returns></returns>
39+
public static async Task<byte[]> GetResponseBodyBytes(this HttpResponseMessage httpResponseMessage)
40+
{
41+
return await httpResponseMessage.Content.ReadAsByteArrayAsync();
42+
}
2343
}
2444
}

src/QAToolKit.Engine.HttpTester/HttpTesterClient.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Diagnostics;
7+
using System.IO;
78
using System.Net;
89
using System.Net.Http;
910
using System.Net.Http.Headers;
@@ -28,6 +29,7 @@ public class HttpTesterClient : IHttpTesterClient, IDisposable
2829
private HttpMethod _httpMethod;
2930
private Dictionary<string, string> _queryParameters = null;
3031
private HttpResponseMessage _responseMessage = null;
32+
private MultipartFormDataContent _multipartFormDataContent = null;
3133

3234
/// <summary>
3335
/// Measured HTTP request duration
@@ -95,6 +97,9 @@ public IHttpTesterClient WithHeaders(Dictionary<string, string> headers)
9597
/// <returns></returns>
9698
public IHttpTesterClient WithJsonBody<T>(T bodyObject)
9799
{
100+
if (_multipartFormDataContent != null)
101+
throw new QAToolKitEngineHttpTesterException("Body multipart/form-data already defined on the HTTP client. Can not add application/json content type.");
102+
98103
if (bodyObject == null)
99104
{
100105
return this;
@@ -197,6 +202,57 @@ public IHttpTesterClient WithNTLMAuthentication()
197202
return this;
198203
}
199204

205+
/// <summary>
206+
/// Create a multipart form data content and add a file content
207+
/// </summary>
208+
/// <param name="fileByteArray"></param>
209+
/// <param name="httpContentName"></param>
210+
/// <param name="fileName"></param>
211+
/// <returns></returns>
212+
public IHttpTesterClient WithMultipart(byte[] fileByteArray, string httpContentName, string fileName)
213+
{
214+
if (!string.IsNullOrEmpty(_body))
215+
throw new QAToolKitEngineHttpTesterException("Body application/json already defined on the HTTP client. Can not add multipart/form-data content type.");
216+
if (fileByteArray == null)
217+
throw new ArgumentNullException($"{nameof(fileByteArray)} is null.");
218+
if (string.IsNullOrEmpty(httpContentName))
219+
throw new ArgumentNullException($"{nameof(httpContentName)} is null.");
220+
221+
if (_multipartFormDataContent == null)
222+
{
223+
_multipartFormDataContent = new MultipartFormDataContent();
224+
}
225+
226+
_multipartFormDataContent.Add(new ByteArrayContent(fileByteArray), httpContentName, fileName);
227+
228+
return this;
229+
}
230+
231+
/// <summary>
232+
/// Add a string to a multipart form data
233+
/// </summary>
234+
/// <param name="httpContentName"></param>
235+
/// <param name="value"></param>
236+
/// <returns></returns>
237+
public IHttpTesterClient WithMultipart(string httpContentName, string value)
238+
{
239+
if (!string.IsNullOrEmpty(_body))
240+
throw new QAToolKitEngineHttpTesterException("Body application/json already defined on the HTTP client. Can not add multipart/form-data content type.");
241+
if (string.IsNullOrEmpty(httpContentName))
242+
throw new ArgumentNullException($"{nameof(httpContentName)} is null.");
243+
if (value == null)
244+
throw new ArgumentNullException($"{nameof(value)} is null.");
245+
246+
if (_multipartFormDataContent == null)
247+
{
248+
_multipartFormDataContent = new MultipartFormDataContent();
249+
}
250+
251+
_multipartFormDataContent.Add(new StringContent(value), httpContentName);
252+
253+
return this;
254+
}
255+
200256
/// <summary>
201257
/// Start the HTTP request
202258
/// </summary>
@@ -246,6 +302,11 @@ public async Task<HttpResponseMessage> Start()
246302
requestMessage.Content = new StringContent(_body, Encoding.UTF8, "application/json");
247303
}
248304

305+
if (_multipartFormDataContent != null)
306+
{
307+
requestMessage.Content = _multipartFormDataContent;
308+
}
309+
249310
_responseMessage = await HttpClient.SendAsync(requestMessage);
250311
}
251312
sw.Stop();

src/QAToolKit.Engine.HttpTester/Interfaces/IHttpTesterClient.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,21 @@ public interface IHttpTesterClient
7474
/// <returns></returns>
7575
IHttpTesterClient WithNTLMAuthentication();
7676
/// <summary>
77+
/// Upload a file
78+
/// </summary>
79+
/// <param name="fileByteArray"></param>
80+
/// <param name="httpContentName"></param>
81+
/// <param name="fileName"></param>
82+
/// <returns></returns>
83+
IHttpTesterClient WithMultipart(byte[] fileByteArray, string httpContentName, string fileName);
84+
/// <summary>
85+
/// Upload a file
86+
/// </summary>
87+
/// <param name="httpContentName"></param>
88+
/// <param name="value"></param>
89+
/// <returns></returns>
90+
IHttpTesterClient WithMultipart(string httpContentName, string value);
91+
/// <summary>
7792
/// Start the HTTP request
7893
/// </summary>
7994
/// <returns></returns>

0 commit comments

Comments
 (0)