mock-server / mockserver

MockServer enables easy mocking of any system you integrate with via HTTP or HTTPS with clients written in Java, JavaScript and Ruby. MockServer also includes a proxy that introspects all proxied traffic including encrypted SSL traffic and supports Port Forwarding, Web Proxying (i.e. HTTP proxy), HTTPS Tunneling Proxying (using HTTP CONNECT) and SOCKS Proxying (i.e. dynamic port forwarding).
http://mock-server.com
Apache License 2.0
4.6k stars 1.07k forks source link

Request-body incorrectly parsed #600

Closed ThomasStubbe closed 4 years ago

ThomasStubbe commented 5 years ago

This is similar to this issue: https://github.com/jamesdbloom/mockserver/issues/204

I use OkHttp to do REST-requests, and use MockServer for the tests. I've also tested it with RestTemplate from Spring with the same result.

The java:

final SomeDTO requestObject = new SomeDTO(someParams);
final String jsonObject = objectMapper.writeValueAsString(requestObject);
final MediaType MEDIA_TYPE_JSON = MediaType.get("application/json; charset=utf-8");

final RequestBody requestBody = RequestBody.create(MEDIA_TYPE_JSON, jsonObject);
final Request request = new Request.Builder().url("serverUrl").post(requestBody).build();

final Response response = client.newCall(request).execute();
final String responseJson = response.body().string();
final ResultDTO result = objectMapper.readValue(responseJson, ResultDTO.class);

The MockServer expectation:

final MockServerClient client = new MockServerClient("127.0.0.1", 1080);
client.when(request().withMethod("POST") //
                     .withPath("serverUrl") //
                     .withBody(json(correctJsonString, MatchType.ONLY_MATCHING_FIELDS))) //
      .respond(response().withStatusCode(200) //
                         .withHeaders(new Header("Content-Type", "application/json; charset=utf-8"),
                                    new Header("Cache-Control", "public, max-age=86400"))
                         .withBody(responseJson));

I get a request didn't match expectation because: body didn't match, where the difference between the bodies are:

Request:

"body" : {
  "type" : "STRING",
  "string" : "{\"id\":33611,\"prop1\":28,\"prop2\":\"value2\",\"graph\":[...]}",
  "contentType" : "text/plain; charset=utf-8"
}

Request should match:

"body" : {
  "type" : "JSON",
  "json" : "{\"prop2\":\"value2\",\"prop1\":28,\"graph\":[...]}"
}

Am I doing something wrong? Why does MockServer detects the body as STRING instead of JSON, while the content type is set correctly? Why is the contentType set to "text/plain". Why is there even a separate contentType for the body...?

jkwatson commented 5 years ago

ditto..having this same issue. Any ideas yet?

Courela commented 5 years ago

Same here. For some reason it seems that MockServer is ignoring request's Content-Type header and assumes that body is a string.

gznglor commented 5 years ago

I think I'm having the same issue.

The failure message happens in the test when I try to verify that the request has been sent properly: mockServerClient.verify(postRequest, VerificationTimes.once())

Here is how my postRequest is set up in the test...

    HttpRequest postRequest = HttpRequest.request('/my_endpoint').withMethod('POST')
        .withBody(JsonBody.json(requestBody)).withHeader('Content-Type', 'application/json')

Here is the error message. MockServer is assuming the body is a string and wraps it in a "String"...

java.lang.AssertionError: Request not found exactly once, expected:<{
|                  "method" : "POST",
|                  "path" : "/my_endpoint",
|                  "body" : {
|                    "type" : "JSON",
|                    "json" : "{\"value\":\"key\"}"
|                  },
|                  "headers" : {
|                    "Content-Type" : [ "application/json" ]
|                  }
|                }> but was:<{
|                  "method" : "POST",
|                  "path" : "/my_endpoint",
|                  "body" : {
|                    "type" : "STRING",
|                    "string" :  "{\"value\":\"key\"}",
|                    "contentType" : "text/plain; charset=utf-8"
|                  },

It's not clear to me why my post body is getting wrapped like this. Anyone know why this is happening?

marcelmu commented 5 years ago

I can confirm this, too. But even a bit stranger: The Contenttype in the body is correctly set:

"body" : {
        "type" : "STRING",
        "string" : "{\"1533211294\":66}",
        "contentType" : "application/json; charset=utf-8"
}

But I still can't match the expectation since it records a Body of type "JSON"

FatCash commented 4 years ago

In my case the problem was that the body contained some Swedish characters åäö. It took a long time to debug though because the requestBody and expectedBody looked visually identical. I tried to set "application/json; charset=utf-8" on both sides but didn't spend more time on it, instead I just removed those characters from the test-data and everything worked.

jamesdbloom commented 4 years ago

Sorry for the delay in responding, if you are still having this issue can you please run the MockServer with TRACE level logging and add the logs here. Once I understand where the problem is I can either fix it or try to improve the log output to make it easier to understand why the match is not happening.

jamesdbloom commented 4 years ago

I have raised the log level for matcher failures and have improved slightly the log output, please specific log level of DEBUG to view the matcher errors and confirm if you are still having an issue.

jamesdbloom commented 4 years ago

@ThomasStubbe is there any update on this, are you able to attach the full DEBUG level log?

littlemaneuver commented 4 years ago

for me there is nothing except the same info log

2019-12-02 14:06:42  org.mockserver.log.MockServerEventLog  INFO  received request:

        {
          "method" : "POST",
          "path" : "...",
          "headers" : {
            "User-Agent" : [ "test" ],
            "host" : [ "172.17.0.37:1080" ],
            "accept" : [ "application/json" ],
            "content-type" : [ "application/json" ],
            "content-length" : [ "197" ],
            "Connection" : [ "close" ]
          },
          "keepAlive" : false,
          "secure" : false,
          "body" : {
            "type" : "STRING",
            "string" : "{\"provider\":\"...\"}"
          }
        }
littlemaneuver commented 4 years ago

I've used jamesdbloom/mockserver:mockserver-5.7.1 and jamesdbloom/mockserver:mockserver-5.8.0 docker images

jamesdbloom commented 4 years ago

@gznglor you request doesn't match because the headers don't match as show in the log output you provided:

java.lang.AssertionError: Request not found exactly once, expected:<{
|                  "method" : "POST",
|                  "path" : "/my_endpoint",
|                  "body" : {
|                    "type" : "JSON",
|                    "json" : "{\"value\":\"key\"}"
|                  },
|                  "headers" : {
|                    "Content-Type" : [ "application/json" ]
|                  }
|                }> but was:<{
|                  "method" : "POST",
|                  "path" : "/my_endpoint",
|                  "body" : {
|                    "type" : "STRING",
|                    "string" :  "{\"value\":\"key\"}",
|                    "contentType" : "text/plain; charset=utf-8"
|                  },

The Content-Type header is present in the expected but not in the recorded request.

jamesdbloom commented 4 years ago

@Courela MockServer does rely on the Content-Type header to do matching so that has no impact on the outcome.

@ThomasStubbe if you can provide the full log I can advise, however, I would imagine that the contents of the JSON array does not match causing the JSON to not match. Even when you specify ONLY_MATCHING_FIELDS the number of items in the array needs to be equal for the JSON to match. This is the way the JSON matching library I'm using works and is beyond my control, however, it does make sense because otherwise, it would be very difficult to know which item in the JSON array matched if the expectation and income request array where of a different length.

jamesdbloom commented 4 years ago

@antoniopuero I don't understand how that could be the only entry in the log as that value always proceeds other log statements saying whether that request matched an existing expectation (or not) or was forwarded, except for OPTIONS request for CORS. Can you please double-check there are no other log entries. Perhaps the MockServer is getting stopped by some other code (i.e. @After / @AfterClass) before any more log entries output.

jamesdbloom commented 4 years ago

@FatCash it may be worth double-checking the HTTP client making the request to MockServer has the Content-Type set. There are integration and unit tests that confirm that matches work correctly in a number of scenarios with special characters, even where the expectation and request have different character sets (as long as they are both specified). However, the typical issue is that the client making the request to MockServer (the system under test) is no sending a Content-Type header with a charset so the MockServer is unable to deserialise the bytes into characters correctly. If you provide the full logs of the scenario with the special characters I can easily confirm this.

jamesdbloom commented 4 years ago

closing as unable to reproduce and no activity on issue

feanor777 commented 3 years ago

In my case the problem was that the body contained some Swedish characters åäö.

Thank you! I had the same issue (with Japanese Characters). Realized it after switching MockServer log level to Debug and saw the body in the console.

<logger name="org.mockserver" level="DEBUG">
        <appender-ref ref="stdout"/>
</logger>

In my case I solved this issue by specifying encoding in the FeignClient. @PostMapping(value = "/send", consumes = "application/json; charset=utf-8")