我是第一次尝试Spring-Cloud-Contract。我试图让我的客户端自动发现合同存根,但即使我的合同指定了内容类型的' application / json'对于响应,我从WireMock获得的内容类型为“应用程序/八位字节”。我做错了什么?
我的服务中有一个简单的方法,它从/status
端点返回这样的模型:
{
"name": string,
"status": string
}
我的合同如下:
import org.springframework.cloud.contract.spec.Contract
Contract.make {
request {
method('GET')
headers {
contentType(applicationJson())
}
url("/status")
}
response {
status OK()
body(
name: "Demo",
status: "RUNNING"
)
headers {
contentType(applicationJson())
}
}
}
在我的客户端中,我有一个使用Spring RestTemplate
来查询此端点的类:
@Component
public class StatusClient {
private final RestTemplate restTemplate;
public StatusClient(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public Status getStatus() {
return this.restTemplate
.exchange("http://localhost:8080/status", HttpMethod.GET, null, Status.class)
.getBody();
}
}
@Data
class Status implements Serializable {
private String name;
private String status;
}
我的单元测试使用@AutoConfigureStubRunner
从本地存储库中提取最新版本的合同,并根据合同的响应进行断言(例如,name = Demo,status = RUNNING)。
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureStubRunner(ids = {"com.example:contract-demo:+:8080"}, stubsMode = StubRunnerProperties.StubsMode.LOCAL)
public class StatusClientTests {
@Autowired
private StatusClient client;
@Test
public void testThatStatusReturnsSuccessfully() {
Status result = this.client.getStatus();
assertEquals("Demo", result.getName());
assertEquals("RUNNING", result.getStatus());
}
}
当我运行测试时,WireMock按预期报告收到的合同:
2018-05-31 11:36:49.919 INFO 14212 --- [tp1255723887-26] WireMock : Request received: 127.0.0.1 - GET /status User-Agent: [Java/1.8.0_161] Connection: [keep-alive] Host: [localhost:8080] Accept: [application/json, application/*+json] Matched response definition: { "status" : 200, "body" : "{\"name\":\"Demo\",\"status\":\"RUNNING\"}", "headers" : { "contentType" : "application/json" }, "transformers" : [ "response-template" ] } Response: HTTP/1.1 200 contentType: [application/json]
但是当RestTemplate尝试反序列化它时,它会抛出异常,因为响应内容类型实际上是" application / octet"一旦它遇到提取数据的方法:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class com.example.contractclientdemo.Status] and content type [application/octet-stream]
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:119)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:991)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:974)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:725)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:680)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:600)
at com.example.contractclientdemo.StatusClient.getStatus(StatusClient.java:18)
我使用Finchley.RC2作为Spring云版本,spring-cloud-starter-contract-stub-runner
是我唯一的测试依赖项,而不是spring-boot-starter-test
。
我知道WireMock返回了错误的内容类型,因为我深入调试了Spring中的HttpMessageConverterExtractor类,以及getContentType
方法在查询时返回的内容。
为什么WireMock会返回错误的内容类型,尽管它会在日志中报告正确的内容类型?如何让它正确返回application/json
以便我可以反序列化我的简单消息?
答案 0 :(得分:0)
我认为您应该将其作为WireMock中的问题提交。此外,您没有在请求中显式设置application / json内容类型标头。也许这是一个问题?也不应该将内容类型作为响应存根中的标题名称吗?
答案 1 :(得分:0)
我遇到了与您完全相同的问题。我通过添加
解决了headers {
header 'Content-Type': 'application/json;charset=UTF-8'
}
响应。 您似乎在回应中,尽管用另一种方式编写,但确实解决了我的问题。因此,这与它有关。
在进行更改之前,curl没有显示Content-Type响应标头:
curl -v -H“接受:application / json” localhost:6565 / products / ABC
*正在尝试127.0.0.1 ...
* TCP_NODELAY设置
*连接到localhost(127.0.0.1)端口6565(#0)
GET / products / ABC HTTP / 1.1
主机:localhost:6565
用户代理:curl / 7.58.0
接受:application / jsonHTTP / 1.1 200确定
传输编码:分块
服务器:Jetty(9.2.z-SNAPSHOT)与主机localhost保持完整的连接#0
{
"price": {
"currencyCode": "EUR",
"value": "100.50"
},
"name": "Fake product"
}
完成更改后,curl返回了此内容,RestTemplate对其进行了反序列化。
HTTP / 1.1 200确定
内容类型:application / json; charset = UTF-8
传输编码:分块
服务器:Jetty(9.2.z-SNAPSHOT)
这是我的工作合同:
import org.springframework.cloud.contract.spec.Contract
Contract.make {
description "should return product information"
request{
method GET()
url("/products/ABC")
}
response {
status 200
headers {
header 'Content-Type': 'application/json;charset=UTF-8'
}
body([
name: 'Fake product',
price:[
currencyCode: 'EUR',
value: 100.50
]
])
}
}
希望这会有所帮助