From 26833c7ad2c57dd8c6d8d9912b6b8414f63e4b6b Mon Sep 17 00:00:00 2001 From: farcasclaudiu Date: Mon, 23 Nov 2020 10:02:52 +0200 Subject: [PATCH 1/5] improvement - multipart attachment name to be specified at the moment of execution. --- Refit/RequestBuilderImplementation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Refit/RequestBuilderImplementation.cs b/Refit/RequestBuilderImplementation.cs index a115d7302..dac36c352 100644 --- a/Refit/RequestBuilderImplementation.cs +++ b/Refit/RequestBuilderImplementation.cs @@ -623,7 +623,7 @@ Func> BuildRequestFactoryForMethod(RestMethod if (!restMethod.AttachmentNameMap.TryGetValue(i, out var attachment)) { - itemName = restMethod.QueryParameterMap[i]; + itemName = (param as StreamPart).FileName ?? restMethod.QueryParameterMap[i]; parameterName = itemName; } else From 12609ec85654b0091d7193225dfef74178f6ded6 Mon Sep 17 00:00:00 2001 From: farcasclaudiu Date: Mon, 23 Nov 2020 10:20:43 +0200 Subject: [PATCH 2/5] handle null cast --- Refit/RequestBuilderImplementation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Refit/RequestBuilderImplementation.cs b/Refit/RequestBuilderImplementation.cs index dac36c352..eb530996d 100644 --- a/Refit/RequestBuilderImplementation.cs +++ b/Refit/RequestBuilderImplementation.cs @@ -623,7 +623,7 @@ Func> BuildRequestFactoryForMethod(RestMethod if (!restMethod.AttachmentNameMap.TryGetValue(i, out var attachment)) { - itemName = (param as StreamPart).FileName ?? restMethod.QueryParameterMap[i]; + itemName = (param as StreamPart)?.FileName ?? restMethod.QueryParameterMap[i]; parameterName = itemName; } else From f552c44f08218a271fa357a0094b8d72241f52f6 Mon Sep 17 00:00:00 2001 From: farcasclaudiu Date: Mon, 23 Nov 2020 10:57:51 +0200 Subject: [PATCH 3/5] fix unit tests --- Refit.Tests/MultipartTests.cs | 35 +++++++++++++++++++++++++-- Refit/RequestBuilderImplementation.cs | 2 +- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/Refit.Tests/MultipartTests.cs b/Refit.Tests/MultipartTests.cs index f7854ba6b..b6b2a7ced 100644 --- a/Refit.Tests/MultipartTests.cs +++ b/Refit.Tests/MultipartTests.cs @@ -301,7 +301,7 @@ public async Task MultipartUploadShouldWorkWithStreamPart() Assert.Single(parts); - Assert.Equal("stream", parts[0].Headers.ContentDisposition.Name); + Assert.Equal("test-streampart.pdf", parts[0].Headers.ContentDisposition.Name); Assert.Equal("test-streampart.pdf", parts[0].Headers.ContentDisposition.FileName); Assert.Equal("application/pdf", parts[0].Headers.ContentType.MediaType); @@ -321,6 +321,37 @@ public async Task MultipartUploadShouldWorkWithStreamPart() var result = await fixture.UploadStreamPart(new StreamPart(stream, "test-streampart.pdf", "application/pdf")); } + [Fact] + public async Task MultipartUploadShouldWorkWithStreamPartWithEmptyFileName() + { + var handler = new MockHttpMessageHandler + { + Asserts = async content => + { + var parts = content.ToList(); + + Assert.Single(parts); + + Assert.Equal("stream", parts[0].Headers.ContentDisposition.Name); + Assert.Equal("stream", parts[0].Headers.ContentDisposition.FileName); + Assert.Equal("application/pdf", parts[0].Headers.ContentType.MediaType); + + using var str = await parts[0].ReadAsStreamAsync(); + using var src = GetTestFileStream("Test Files/Test.pdf"); + Assert.True(StreamsEqual(src, str)); + } + }; + + var settings = new RefitSettings() + { + HttpMessageHandlerFactory = () => handler + }; + + using var stream = GetTestFileStream("Test Files/Test.pdf"); + var fixture = RestService.For(BaseAddress, settings); + var result = await fixture.UploadStreamPart(new StreamPart(stream, string.Empty, "application/pdf")); + } + [Fact] public async Task MultipartUploadShouldWorkWithStreamPartAndQuery() { @@ -336,7 +367,7 @@ public async Task MultipartUploadShouldWorkWithStreamPartAndQuery() Assert.Single(parts); - Assert.Equal("stream", parts[0].Headers.ContentDisposition.Name); + Assert.Equal("test-streampart.pdf", parts[0].Headers.ContentDisposition.Name); Assert.Equal("test-streampart.pdf", parts[0].Headers.ContentDisposition.FileName); Assert.Equal("application/pdf", parts[0].Headers.ContentType.MediaType); diff --git a/Refit/RequestBuilderImplementation.cs b/Refit/RequestBuilderImplementation.cs index eb530996d..21521be08 100644 --- a/Refit/RequestBuilderImplementation.cs +++ b/Refit/RequestBuilderImplementation.cs @@ -623,7 +623,7 @@ Func> BuildRequestFactoryForMethod(RestMethod if (!restMethod.AttachmentNameMap.TryGetValue(i, out var attachment)) { - itemName = (param as StreamPart)?.FileName ?? restMethod.QueryParameterMap[i]; + itemName = !string.IsNullOrEmpty((param as StreamPart)?.FileName) ? (param as StreamPart)?.FileName : restMethod.QueryParameterMap[i]; parameterName = itemName; } else From dc41fa9b4594cc0cfac9b898e6c79404c4e668bb Mon Sep 17 00:00:00 2001 From: farcasclaudiu Date: Mon, 23 Nov 2020 11:04:05 +0200 Subject: [PATCH 4/5] update doc --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b07f0d0f..b0b778992 100644 --- a/README.md +++ b/README.md @@ -684,7 +684,10 @@ At this time, multipart methods support the following parameter types: - Stream - FileInfo -The parameter name will be used as the name of the field in the multipart data. This can be overridden with the `AliasAs` attribute. +By default the streamPart.FileName property will be used as the name of the field in the multipart data. +If streamPart.FileName is empty ("") the parameter name will be used as the name of the field in the multipart data. +This can be overridden with the `AliasAs` attribute. + A custom boundary can be specified with an optional string parameter to the `Multipart` attribute. If left empty, this defaults to `----MyGreatBoundary`. To specify the file name and content type for byte array (`byte[]`), `Stream` and `FileInfo` parameters, use of a wrapper class is required. From 5ee780f49f63460a04f784f39b52fb426fda6fb7 Mon Sep 17 00:00:00 2001 From: farcasclaudiu Date: Tue, 24 Nov 2020 10:52:10 +0200 Subject: [PATCH 5/5] adapt dynamic datapart naming as on #973 approach, fix unit tests, update documetation --- README.md | 8 +++++--- Refit.Tests/MultipartTests.cs | 12 ++++++------ Refit/MultipartItem.cs | 19 +++++++++++++------ Refit/RequestBuilderImplementation.cs | 4 ++-- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index b0b778992..9929a8a31 100644 --- a/README.md +++ b/README.md @@ -684,9 +684,11 @@ At this time, multipart methods support the following parameter types: - Stream - FileInfo -By default the streamPart.FileName property will be used as the name of the field in the multipart data. -If streamPart.FileName is empty ("") the parameter name will be used as the name of the field in the multipart data. -This can be overridden with the `AliasAs` attribute. +Name of the field in the multipart data priority precedence: + +* multipartItem.Name if specified and not null (optional); dynamic, allows naming form data part at execution time. +* [AliasAs] attribute (optional) that decorate the streamPart parameter in the method signature (see below); static, defined in code. +* MultipartItem parameter name (default) as defined in the method signature; static, defined in code. A custom boundary can be specified with an optional string parameter to the `Multipart` attribute. If left empty, this defaults to `----MyGreatBoundary`. diff --git a/Refit.Tests/MultipartTests.cs b/Refit.Tests/MultipartTests.cs index b6b2a7ced..62b1c63dc 100644 --- a/Refit.Tests/MultipartTests.cs +++ b/Refit.Tests/MultipartTests.cs @@ -301,7 +301,7 @@ public async Task MultipartUploadShouldWorkWithStreamPart() Assert.Single(parts); - Assert.Equal("test-streampart.pdf", parts[0].Headers.ContentDisposition.Name); + Assert.Equal("stream", parts[0].Headers.ContentDisposition.Name); Assert.Equal("test-streampart.pdf", parts[0].Headers.ContentDisposition.FileName); Assert.Equal("application/pdf", parts[0].Headers.ContentType.MediaType); @@ -322,7 +322,7 @@ public async Task MultipartUploadShouldWorkWithStreamPart() } [Fact] - public async Task MultipartUploadShouldWorkWithStreamPartWithEmptyFileName() + public async Task MultipartUploadShouldWorkWithStreamPartWithNamedMultipart() { var handler = new MockHttpMessageHandler { @@ -332,8 +332,8 @@ public async Task MultipartUploadShouldWorkWithStreamPartWithEmptyFileName() Assert.Single(parts); - Assert.Equal("stream", parts[0].Headers.ContentDisposition.Name); - Assert.Equal("stream", parts[0].Headers.ContentDisposition.FileName); + Assert.Equal("test-stream", parts[0].Headers.ContentDisposition.Name); + Assert.Equal("test-streampart.pdf", parts[0].Headers.ContentDisposition.FileName); Assert.Equal("application/pdf", parts[0].Headers.ContentType.MediaType); using var str = await parts[0].ReadAsStreamAsync(); @@ -349,7 +349,7 @@ public async Task MultipartUploadShouldWorkWithStreamPartWithEmptyFileName() using var stream = GetTestFileStream("Test Files/Test.pdf"); var fixture = RestService.For(BaseAddress, settings); - var result = await fixture.UploadStreamPart(new StreamPart(stream, string.Empty, "application/pdf")); + var result = await fixture.UploadStreamPart(new StreamPart(stream, "test-streampart.pdf", "application/pdf", "test-stream")); } [Fact] @@ -367,7 +367,7 @@ public async Task MultipartUploadShouldWorkWithStreamPartAndQuery() Assert.Single(parts); - Assert.Equal("test-streampart.pdf", parts[0].Headers.ContentDisposition.Name); + Assert.Equal("stream", parts[0].Headers.ContentDisposition.Name); Assert.Equal("test-streampart.pdf", parts[0].Headers.ContentDisposition.FileName); Assert.Equal("application/pdf", parts[0].Headers.ContentType.MediaType); diff --git a/Refit/MultipartItem.cs b/Refit/MultipartItem.cs index b66207da9..d70fa4629 100644 --- a/Refit/MultipartItem.cs +++ b/Refit/MultipartItem.cs @@ -13,6 +13,13 @@ public MultipartItem(string fileName, string contentType) ContentType = contentType; } + public MultipartItem(string fileName, string contentType, string name) : this(fileName, contentType) + { + Name = name; + } + + public string Name { get; } + public string ContentType { get; } public string FileName { get; } @@ -33,8 +40,8 @@ public HttpContent ToContent() public class StreamPart : MultipartItem { - public StreamPart(Stream value, string fileName, string contentType = null) : - base(fileName, contentType) + public StreamPart(Stream value, string fileName, string contentType = null, string name = null) : + base(fileName, contentType, name) { Value = value ?? throw new ArgumentNullException("value"); } @@ -49,8 +56,8 @@ protected override HttpContent CreateContent() public class ByteArrayPart : MultipartItem { - public ByteArrayPart(byte[] value, string fileName, string contentType = null) : - base(fileName, contentType) + public ByteArrayPart(byte[] value, string fileName, string contentType = null, string name = null) : + base(fileName, contentType, name) { Value = value ?? throw new ArgumentNullException("value"); } @@ -65,8 +72,8 @@ protected override HttpContent CreateContent() public class FileInfoPart : MultipartItem { - public FileInfoPart(FileInfo value, string fileName, string contentType = null) : - base(fileName, contentType) + public FileInfoPart(FileInfo value, string fileName, string contentType = null, string name = null) : + base(fileName, contentType, name) { Value = value ?? throw new ArgumentNullException("value"); } diff --git a/Refit/RequestBuilderImplementation.cs b/Refit/RequestBuilderImplementation.cs index 21521be08..3f4218ac5 100644 --- a/Refit/RequestBuilderImplementation.cs +++ b/Refit/RequestBuilderImplementation.cs @@ -182,7 +182,7 @@ async Task AddMultipartItemAsync(MultipartFormDataContent multiPartContent, stri if (itemValue is MultipartItem multipartItem) { var httpContent = multipartItem.ToContent(); - multiPartContent.Add(httpContent, parameterName, string.IsNullOrEmpty(multipartItem.FileName) ? fileName : multipartItem.FileName); + multiPartContent.Add(httpContent, multipartItem.Name ?? parameterName, string.IsNullOrEmpty(multipartItem.FileName) ? fileName : multipartItem.FileName); return; } @@ -623,7 +623,7 @@ Func> BuildRequestFactoryForMethod(RestMethod if (!restMethod.AttachmentNameMap.TryGetValue(i, out var attachment)) { - itemName = !string.IsNullOrEmpty((param as StreamPart)?.FileName) ? (param as StreamPart)?.FileName : restMethod.QueryParameterMap[i]; + itemName = restMethod.QueryParameterMap[i]; parameterName = itemName; } else