mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-18 07:00:04 +02:00
Fix BodyParser to correctly check for json (#1297)
* Fix BodyParser to correctly check for json * JsonUtils
This commit is contained in:
@@ -8,18 +8,8 @@ namespace WireMock.Util;
|
|||||||
// ReSharper disable once InconsistentNaming
|
// ReSharper disable once InconsistentNaming
|
||||||
public static class IBodyDataExtensions
|
public static class IBodyDataExtensions
|
||||||
{
|
{
|
||||||
public static BodyType GetBodyType(this IBodyData bodyData)
|
public static BodyType GetDetectedBodyType(this IBodyData bodyData)
|
||||||
{
|
{
|
||||||
if (bodyData.DetectedBodyTypeFromContentType is not null and not BodyType.None)
|
return bodyData.DetectedBodyType ?? BodyType.None;
|
||||||
{
|
|
||||||
return bodyData.DetectedBodyTypeFromContentType.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bodyData.DetectedBodyType is not null and not BodyType.None)
|
|
||||||
{
|
|
||||||
return bodyData.DetectedBodyType.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return BodyType.None;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,7 @@ internal static class HttpRequestMessageHelper
|
|||||||
}
|
}
|
||||||
|
|
||||||
var bodyData = requestMessage.BodyData;
|
var bodyData = requestMessage.BodyData;
|
||||||
httpRequestMessage.Content = bodyData?.GetBodyType() switch
|
httpRequestMessage.Content = bodyData?.DetectedBodyType switch
|
||||||
{
|
{
|
||||||
BodyType.Bytes => ByteArrayContentHelper.Create(bodyData.BodyAsBytes!, contentType),
|
BodyType.Bytes => ByteArrayContentHelper.Create(bodyData.BodyAsBytes!, contentType),
|
||||||
BodyType.Json => StringContentHelper.Create(JsonConvert.SerializeObject(bodyData.BodyAsJson), contentType),
|
BodyType.Json => StringContentHelper.Create(JsonConvert.SerializeObject(bodyData.BodyAsJson), contentType),
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ namespace WireMock.Owin.Mappers
|
|||||||
}
|
}
|
||||||
|
|
||||||
var bodyData = responseMessage.BodyData;
|
var bodyData = responseMessage.BodyData;
|
||||||
if (bodyData?.GetBodyType() == BodyType.SseString)
|
if (bodyData?.GetDetectedBodyType() == BodyType.SseString)
|
||||||
{
|
{
|
||||||
await HandleSseStringAsync(responseMessage, response, bodyData);
|
await HandleSseStringAsync(responseMessage, response, bodyData);
|
||||||
return;
|
return;
|
||||||
@@ -166,7 +166,7 @@ namespace WireMock.Owin.Mappers
|
|||||||
private async Task<byte[]?> GetNormalBodyAsync(IResponseMessage responseMessage)
|
private async Task<byte[]?> GetNormalBodyAsync(IResponseMessage responseMessage)
|
||||||
{
|
{
|
||||||
var bodyData = responseMessage.BodyData;
|
var bodyData = responseMessage.BodyData;
|
||||||
switch (bodyData?.GetBodyType())
|
switch (bodyData?.GetDetectedBodyType())
|
||||||
{
|
{
|
||||||
case BodyType.String:
|
case BodyType.String:
|
||||||
case BodyType.FormUrlEncoded:
|
case BodyType.FormUrlEncoded:
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ internal static class BodyParser
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If string is not null or empty, try to deserialize the string to a JObject
|
// If string is not null or empty, try to deserialize the string to a JObject
|
||||||
if (settings.DeserializeJson && !string.IsNullOrEmpty(data.BodyAsString))
|
if (settings.DeserializeJson && JsonUtils.IsJson(data.BodyAsString))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -197,7 +197,7 @@ internal static class BodyParser
|
|||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<(string? ContentType, byte[] Bytes)> ReadBytesAsync(Stream stream, string? contentEncoding = null, bool decompressGZipAndDeflate = true)
|
private static async Task<(string? ContentType, byte[] Bytes)> ReadBytesAsync(Stream stream, string? contentEncoding = null, bool decompressGZipAndDeflate = true)
|
||||||
{
|
{
|
||||||
using var memoryStream = new MemoryStream();
|
using var memoryStream = new MemoryStream();
|
||||||
|
|||||||
@@ -12,17 +12,23 @@ namespace WireMock.Util;
|
|||||||
|
|
||||||
internal static class JsonUtils
|
internal static class JsonUtils
|
||||||
{
|
{
|
||||||
public static bool TryParseAsJObject(string? strInput, [NotNullWhen(true)] out JObject? value)
|
public static bool IsJson(string? value)
|
||||||
{
|
{
|
||||||
value = null;
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
|
||||||
if (strInput == null || string.IsNullOrWhiteSpace(strInput))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
strInput = strInput.Trim();
|
value = value!.Trim();
|
||||||
if ((!strInput.StartsWith("{") || !strInput.EndsWith("}")) && (!strInput.StartsWith("[") || !strInput.EndsWith("]")))
|
|
||||||
|
return (value.StartsWith("{") && value.EndsWith("}")) || (value.StartsWith("[") && value.EndsWith("]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryParseAsJObject(string? strInput, [NotNullWhen(true)] out JObject? value)
|
||||||
|
{
|
||||||
|
value = null;
|
||||||
|
|
||||||
|
if (!IsJson(strInput))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -30,7 +36,7 @@ internal static class JsonUtils
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Try to convert this string into a JToken
|
// Try to convert this string into a JToken
|
||||||
value = JObject.Parse(strInput);
|
value = JObject.Parse(strInput!);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public class HttpRequestMessageHelperTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task HttpRequestMessageHelper_Create_Bytes()
|
public async Task HttpRequestMessageHelper_Create_Bytes_Without_ContentTypeHeader()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
var body = new BodyData
|
var body = new BodyData
|
||||||
@@ -40,27 +40,25 @@ public class HttpRequestMessageHelperTests
|
|||||||
BodyAsBytes = Encoding.UTF8.GetBytes("hi"),
|
BodyAsBytes = Encoding.UTF8.GetBytes("hi"),
|
||||||
DetectedBodyType = BodyType.Bytes
|
DetectedBodyType = BodyType.Bytes
|
||||||
};
|
};
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", ClientIp, body);
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Check.That(await message.Content.ReadAsByteArrayAsync().ConfigureAwait(false)).ContainsExactly(Encoding.UTF8.GetBytes("hi"));
|
Check.That(await message.Content!.ReadAsByteArrayAsync().ConfigureAwait(false)).ContainsExactly(Encoding.UTF8.GetBytes("hi"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task HttpRequestMessageHelper_Create_TextPlain()
|
public async Task HttpRequestMessageHelper_Create_TextPlain_Without_ContentTypeHeader()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
var body = new BodyData
|
var body = new BodyData
|
||||||
{
|
{
|
||||||
BodyAsString = "0123", // or 83 in decimal
|
BodyAsString = "0123", // or 83 in decimal
|
||||||
BodyAsJson = 83,
|
DetectedBodyType = BodyType.String
|
||||||
DetectedBodyType = BodyType.Json,
|
|
||||||
DetectedBodyTypeFromContentType = BodyType.String
|
|
||||||
};
|
};
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", ClientIp, body);
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
||||||
@@ -78,7 +76,7 @@ public class HttpRequestMessageHelperTests
|
|||||||
BodyAsJson = new { x = 42 },
|
BodyAsJson = new { x = 42 },
|
||||||
DetectedBodyType = BodyType.Json
|
DetectedBodyType = BodyType.Json
|
||||||
};
|
};
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", ClientIp, body);
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
||||||
@@ -97,13 +95,13 @@ public class HttpRequestMessageHelperTests
|
|||||||
BodyAsJson = new { x = 42 },
|
BodyAsJson = new { x = 42 },
|
||||||
DetectedBodyType = BodyType.Json
|
DetectedBodyType = BodyType.Json
|
||||||
};
|
};
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", ClientIp, body, headers);
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body, headers);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Check.That(await message.Content.ReadAsStringAsync().ConfigureAwait(false)).Equals("{\"x\":42}");
|
Check.That(await message.Content!.ReadAsStringAsync().ConfigureAwait(false)).Equals("{\"x\":42}");
|
||||||
Check.That(message.Content.Headers.GetValues("Content-Type")).ContainsExactly("application/json");
|
Check.That(message.Content.Headers.GetValues("Content-Type")).ContainsExactly("application/json");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,16 +115,38 @@ public class HttpRequestMessageHelperTests
|
|||||||
BodyAsJson = new { x = 42 },
|
BodyAsJson = new { x = 42 },
|
||||||
DetectedBodyType = BodyType.Json
|
DetectedBodyType = BodyType.Json
|
||||||
};
|
};
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", ClientIp, body, headers);
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body, headers);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Check.That(await message.Content.ReadAsStringAsync().ConfigureAwait(false)).Equals("{\"x\":42}");
|
Check.That(await message.Content!.ReadAsStringAsync().ConfigureAwait(false)).Equals("{\"x\":42}");
|
||||||
Check.That(message.Content.Headers.GetValues("Content-Type")).ContainsExactly("application/json; charset=utf-8");
|
Check.That(message.Content.Headers.GetValues("Content-Type")).ContainsExactly("application/json; charset=utf-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task HttpRequestMessageHelper_Create_Json_With_ContentType_MultiPartFormData()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var headers = new Dictionary<string, string[]> { { "Content-Type", ["multipart/form-data"] } };
|
||||||
|
var body = new BodyData
|
||||||
|
{
|
||||||
|
BodyAsJson = new { x = 42 },
|
||||||
|
DetectedBodyTypeFromContentType = BodyType.MultiPart,
|
||||||
|
DetectedBodyType = BodyType.Json
|
||||||
|
};
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, body, headers);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Check.That(await message.Content!.ReadAsStringAsync().ConfigureAwait(false)).Equals("{\"x\":42}");
|
||||||
|
Check.That(message.Content.Headers.GetValues("Content-Type")).ContainsExactly("multipart/form-data");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void HttpRequestMessageHelper_Create_String_With_ContentType_ApplicationXml()
|
public void HttpRequestMessageHelper_Create_String_With_ContentType_ApplicationXml()
|
||||||
{
|
{
|
||||||
@@ -143,7 +163,7 @@ public class HttpRequestMessageHelperTests
|
|||||||
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Check.That(message.Content.Headers.GetValues("Content-Type")).ContainsExactly("application/xml");
|
Check.That(message.Content!.Headers.GetValues("Content-Type")).ContainsExactly("application/xml");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -162,7 +182,7 @@ public class HttpRequestMessageHelperTests
|
|||||||
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Check.That(message.Content.Headers.GetValues("Content-Type")).ContainsExactly("application/xml; charset=UTF-8");
|
Check.That(message.Content!.Headers.GetValues("Content-Type")).ContainsExactly("application/xml; charset=UTF-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -181,7 +201,7 @@ public class HttpRequestMessageHelperTests
|
|||||||
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Check.That(message.Content.Headers.GetValues("Content-Type")).ContainsExactly("application/xml; charset=Ascii");
|
Check.That(message.Content!.Headers.GetValues("Content-Type")).ContainsExactly("application/xml; charset=Ascii");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -222,7 +242,7 @@ public class HttpRequestMessageHelperTests
|
|||||||
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
var message = HttpRequestMessageHelper.Create(request, "http://url");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Check.That(await message.Content.ReadAsStringAsync().ConfigureAwait(false)).Equals(body);
|
Check.That(await message.Content!.ReadAsStringAsync().ConfigureAwait(false)).Equals(body);
|
||||||
Check.That(message.Content.Headers.GetValues("Content-Type")).ContainsExactly("multipart/form-data");
|
Check.That(message.Content.Headers.GetValues("Content-Type")).ContainsExactly("multipart/form-data");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using NFluent;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
using NFluent;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@@ -67,7 +66,7 @@ public class BodyParserTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(new byte[] { 34, 97, 34 }, BodyType.Json)]
|
[InlineData(new byte[] { 123, 32, 34, 120, 34, 58, 32, 49, 50, 51, 32, 125 }, BodyType.Json)]
|
||||||
[InlineData(new byte[] { 97 }, BodyType.String)]
|
[InlineData(new byte[] { 97 }, BodyType.String)]
|
||||||
[InlineData(new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }, BodyType.Bytes)]
|
[InlineData(new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }, BodyType.Bytes)]
|
||||||
public async Task BodyParser_Parse_DetectedBodyType(byte[] content, BodyType detectedBodyType)
|
public async Task BodyParser_Parse_DetectedBodyType(byte[] content, BodyType detectedBodyType)
|
||||||
@@ -88,7 +87,7 @@ public class BodyParserTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(new byte[] { 34, 97, 34 }, BodyType.String)]
|
[InlineData(new byte[] { 123, 32, 34, 120, 34, 58, 32, 49, 50, 51, 32, 125 }, BodyType.String)]
|
||||||
[InlineData(new byte[] { 97 }, BodyType.String)]
|
[InlineData(new byte[] { 97 }, BodyType.String)]
|
||||||
[InlineData(new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }, BodyType.Bytes)]
|
[InlineData(new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }, BodyType.Bytes)]
|
||||||
public async Task BodyParser_Parse_DetectedBodyTypeNoJsonParsing(byte[] content, BodyType detectedBodyType)
|
public async Task BodyParser_Parse_DetectedBodyTypeNoJsonParsing(byte[] content, BodyType detectedBodyType)
|
||||||
@@ -112,26 +111,28 @@ public class BodyParserTests
|
|||||||
public async Task BodyParser_Parse_WithUTF8EncodingAndContentTypeMultipart_DetectedBodyTypeEqualsString()
|
public async Task BodyParser_Parse_WithUTF8EncodingAndContentTypeMultipart_DetectedBodyTypeEqualsString()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
string contentType = "multipart/form-data";
|
var contentType = "multipart/form-data";
|
||||||
string body = @"
|
var body =
|
||||||
|
"""
|
||||||
|
|
||||||
-----------------------------9051914041544843365972754266
|
-----------------------------9051914041544843365972754266
|
||||||
Content-Disposition: form-data; name=""text""
|
Content-Disposition: form-data; name="text"
|
||||||
|
|
||||||
text default
|
text default
|
||||||
-----------------------------9051914041544843365972754266
|
-----------------------------9051914041544843365972754266
|
||||||
Content-Disposition: form-data; name=""file1""; filename=""a.txt""
|
Content-Disposition: form-data; name="file1"; filename="a.txt"
|
||||||
Content-Type: text/plain
|
Content-Type: text/plain
|
||||||
|
|
||||||
Content of a txt
|
Content of a txt
|
||||||
|
|
||||||
-----------------------------9051914041544843365972754266
|
-----------------------------9051914041544843365972754266
|
||||||
Content-Disposition: form-data; name=""file2""; filename=""a.html""
|
Content-Disposition: form-data; name="file2"; filename="a.html"
|
||||||
Content-Type: text/html
|
Content-Type: text/html
|
||||||
|
|
||||||
<!DOCTYPE html><title>Content of a.html.</title>
|
<!DOCTYPE html><title>Content of a.html.</title>
|
||||||
|
|
||||||
-----------------------------9051914041544843365972754266--";
|
-----------------------------9051914041544843365972754266--
|
||||||
|
""";
|
||||||
|
|
||||||
var bodyParserSettings = new BodyParserSettings
|
var bodyParserSettings = new BodyParserSettings
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user