diff --git a/src/WireMock.Net/Handlers/LocalFileSystemHandler.cs b/src/WireMock.Net/Handlers/LocalFileSystemHandler.cs
index 57bedc64..2dd7e5e0 100644
--- a/src/WireMock.Net/Handlers/LocalFileSystemHandler.cs
+++ b/src/WireMock.Net/Handlers/LocalFileSystemHandler.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.IO;
+using WireMock.Util;
using WireMock.Validation;
namespace WireMock.Handlers
@@ -80,20 +81,20 @@ namespace WireMock.Handlers
public byte[] ReadResponseBodyAsFile(string path)
{
Check.NotNullOrEmpty(path, nameof(path));
-
- // In case the path is a filename, the path will be adjusted to the MappingFolder.
+ path = PathUtils.CleanPath(path);
+ // If the file exists at the given path relative to the MappingsFolder, then return that.
// Else the path will just be as-is.
- return File.ReadAllBytes(Path.GetFileName(path) == path ? Path.Combine(GetMappingFolder(), path) : path);
+ return File.ReadAllBytes(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path);
}
///
public string ReadResponseBodyAsString(string path)
{
Check.NotNullOrEmpty(path, nameof(path));
-
+ path = PathUtils.CleanPath(path);
// In case the path is a filename, the path will be adjusted to the MappingFolder.
// Else the path will just be as-is.
- return File.ReadAllText(Path.GetFileName(path) == path ? Path.Combine(GetMappingFolder(), path) : path);
+ return File.ReadAllText(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path);
}
///
diff --git a/src/WireMock.Net/Util/PathUtils.cs b/src/WireMock.Net/Util/PathUtils.cs
new file mode 100644
index 00000000..951c9d47
--- /dev/null
+++ b/src/WireMock.Net/Util/PathUtils.cs
@@ -0,0 +1,36 @@
+using System.IO;
+
+namespace WireMock.Util
+{
+ internal static class PathUtils
+ {
+ ///
+ /// Robust handling of the user defined path.
+ /// Also supports Unix and Windows platforms
+ ///
+ /// The path to clean
+ public static string CleanPath(string path)
+ {
+ return path?.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar);
+ }
+
+ ///
+ /// Removes leading directory separator chars from the filepath, which could break Path.Combine
+ ///
+ /// The path to remove the loading DirectorySeparatorChars
+ public static string RemoveLeadingDirectorySeparators(string path)
+ {
+ return path?.TrimStart(new[] { Path.DirectorySeparatorChar });
+ }
+
+ ///
+ /// Combine two paths
+ ///
+ /// The root path
+ /// The path
+ public static string Combine(string root, string path)
+ {
+ return Path.Combine(root, RemoveLeadingDirectorySeparators(path));
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyFromFileTests.cs b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyFromFileTests.cs
index 56eaefb3..b841fda3 100644
--- a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyFromFileTests.cs
+++ b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyFromFileTests.cs
@@ -1,4 +1,6 @@
using FluentAssertions;
+using System;
+using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
@@ -13,11 +15,10 @@ namespace WireMock.Net.Tests.ResponseBuilders
public class ResponseWithBodyFromFileTests
{
[Fact]
- public async Task Response_ProvideResponse_WithBodyFromFile()
+ public async Task Response_ProvideResponse_WithBodyFromFile_AbsolutePath()
{
// Arrange
var server = WireMockServer.Start();
-
string path = Path.Combine(Directory.GetCurrentDirectory(), "__admin", "mappings", "MyXmlResponse.xml");
server
@@ -36,14 +37,68 @@ namespace WireMock.Net.Tests.ResponseBuilders
);
// Act
- var response1 = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
- var response2 = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
- var response3 = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
+ var response = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
// Assert
- response1.Should().Contain("world");
- response2.Should().Contain("world");
- response3.Should().Contain("world");
+ response.Should().Contain("world");
+ }
+
+ [Fact]
+ public async Task Response_ProvideResponse_WithBodyFromFile_InSubDirectory()
+ {
+ // Arrange
+ var server = WireMockServer.Start();
+ string path = @"subdirectory/MyXmlResponse.xml";
+
+ server
+ .Given(
+ Request
+ .Create()
+ .UsingGet()
+ .WithPath("/v1/content")
+ )
+ .RespondWith(
+ Response
+ .Create()
+ .WithStatusCode(HttpStatusCode.OK)
+ .WithHeader("Content-Type", "application/xml")
+ .WithBodyFromFile(path)
+ );
+
+ // Act
+ var response = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
+
+ // Assert
+ response.Should().Contain("world");
+ }
+
+ [Fact]
+ public async Task Response_ProvideResponse_WithBodyFromFile_InAdminMappingFolder()
+ {
+ // Arrange
+ var server = WireMockServer.Start();
+ string path = @"MyXmlResponse.xml";
+
+ server
+ .Given(
+ Request
+ .Create()
+ .UsingGet()
+ .WithPath("/v1/content")
+ )
+ .RespondWith(
+ Response
+ .Create()
+ .WithStatusCode(HttpStatusCode.OK)
+ .WithHeader("Content-Type", "application/xml")
+ .WithBodyFromFile(path)
+ );
+
+ // Act
+ var response = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
+
+ // Assert
+ response.Should().Contain("world");
}
}
}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/Util/PathUtilsTests.cs b/test/WireMock.Net.Tests/Util/PathUtilsTests.cs
new file mode 100644
index 00000000..fe33da8d
--- /dev/null
+++ b/test/WireMock.Net.Tests/Util/PathUtilsTests.cs
@@ -0,0 +1,44 @@
+using NFluent;
+using System.IO;
+using WireMock.Util;
+using Xunit;
+
+namespace WireMock.Net.Tests.Util
+{
+ public class PathUtilsTests
+ {
+ [Theory]
+ [InlineData(@"subdirectory/MyXmlResponse.xml")]
+ [InlineData(@"subdirectory\MyXmlResponse.xml")]
+ public void PathUtils_CleanPath(string path)
+ {
+ // Act
+ var cleanPath = PathUtils.CleanPath(path);
+
+ // Assert
+ Check.That(cleanPath).Equals("subdirectory" + Path.DirectorySeparatorChar + "MyXmlResponse.xml");
+ }
+
+ [Theory]
+ [InlineData(null, null)]
+ [InlineData("", "")]
+ [InlineData("a", "a")]
+ [InlineData(@"/", "")]
+ [InlineData(@"//", "")]
+ [InlineData(@"//a", "a")]
+ [InlineData(@"\", "")]
+ [InlineData(@"\\", "")]
+ [InlineData(@"\\a", "a")]
+ public void PathUtils_CleanPath_RemoveLeadingDirectorySeparators(string path, string expected)
+ {
+ // Arrange
+ var cleanPath = PathUtils.CleanPath(path);
+
+ // Act
+ var withoutDirectorySeparators = PathUtils.RemoveLeadingDirectorySeparators(cleanPath);
+
+ // Assert
+ Check.That(withoutDirectorySeparators).Equals(expected);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj
index 68ec5c76..4f341c22 100644
--- a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj
+++ b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj
@@ -83,6 +83,9 @@
PreserveNewest
+
+ PreserveNewest
+
diff --git a/test/WireMock.Net.Tests/__admin/mappings/subdirectory/MyXmlResponse.xml b/test/WireMock.Net.Tests/__admin/mappings/subdirectory/MyXmlResponse.xml
new file mode 100644
index 00000000..24fd28d3
--- /dev/null
+++ b/test/WireMock.Net.Tests/__admin/mappings/subdirectory/MyXmlResponse.xml
@@ -0,0 +1,3 @@
+
+world
+
\ No newline at end of file