Restdoc 在尝试记录子对象时抛出 org.springframework.restdocs.snippet.SnippetException
Restdoc throws org.springframework.restdocs.snippet.SnippetException when trying to document child objects
我有一个 OrderInfo class:
@ApiModel(description = "object needed to make an order")
public class OrderInfo implements Serializable {
private static final long serialVersionUID = 1L;
@JsonProperty("purchase")
private PurchaseInfo purchase = null;
@JsonProperty("paymentMode")
private PaymentMode paymentMode = null;
@JsonProperty("serialNumber")
private String serialNumber = null;
public OrderInfo purchase(PurchaseInfo purchase) {
this.purchase = purchase;
return this;
}
@ApiModelProperty(required = true, value = "coming from commercialization")
@NotNull
@Valid
public PurchaseInfo getPurchase() {
return purchase;
}
public void setPurchase(PurchaseInfo purchase) {
this.purchase = purchase;
}
public OrderInfo paymentMode(PaymentMode paymentMode) {
this.paymentMode = paymentMode;
return this;
}
@ApiModelProperty(required = true, value = "")
@NotNull
@Valid
public PaymentMode getPaymentMode() {
return paymentMode;
}
public void setPaymentMode(PaymentMode paymentMode) {
this.paymentMode = paymentMode;
}
public OrderInfo serialNumber(String serialNumber) {
this.serialNumber = serialNumber;
return this;
}
@ApiModelProperty(value = "The serial number of the registered device")
public String getSerialNumber() {
return serialNumber;
}
public void setSerialNumber(String serialNumber) {
this.serialNumber = serialNumber;
}
}
有一个 PurchaseInfo 子对象:
@ApiModel(description = "Info necessary to order a video, an episode or a season")
public class PurchaseInfo implements Serializable {
private static final long serialVersionUID = 1L;
@JsonProperty("id")
private String id = null;
@JsonProperty("pricingId")
private String pricingId = null;
@JsonProperty("price")
private Double price = null;
@JsonProperty("type")
private ArticleType type = null;
public PurchaseInfo id(String id) {
this.id = id;
return this;
}
@ApiModelProperty(required = true, value = "id of the commercialization")
@NotNull
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public PurchaseInfo pricingId(String pricingId) {
this.pricingId = pricingId;
return this;
}
@ApiModelProperty(required = true, value = "additional pricing of the commercialization")
@NotNull
public String getPricingId() {
return pricingId;
}
public void setPricingId(String pricingId) {
this.pricingId = pricingId;
}
public PurchaseInfo price(Double price) {
this.price = price;
return this;
}
@ApiModelProperty(required = true, value = "price of the commercialization")
@NotNull
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public PurchaseInfo type(ArticleType type) {
this.type = type;
return this;
}
@ApiModelProperty(required = true, value = "")
@NotNull
@Valid
public ArticleType getType() {
return type;
}
public void setType(ArticleType type) {
this.type = type;
}
}
并生成对应的FieldDescriptorclasses:
public class OrderInfoFieldDescriptor {
public static FieldDescriptor[] fdOrderInfo = new FieldDescriptor[] {
fieldWithPath("purchase").description("coming from commercialization").type(PurchaseInfo.class),
fieldWithPath("paymentMode").description("").type(PaymentMode.class),
fieldWithPath("serialNumber").description("The serial number of the registered device").type(java.lang.String.class).optional() };
public static FieldDescriptor[] fdOrderInfoList = new FieldDescriptor[] {
fieldWithPath("[].purchase").description("coming from commercialization").type(PurchaseInfo.class),
fieldWithPath("[].paymentMode").description("").type(PaymentMode.class),
fieldWithPath("[].serialNumber").description("The serial number of the registered device").type(java.lang.String.class).optional() };
}
并且:
public class PurchaseInfoFieldDescriptor {
public static FieldDescriptor[] fdPurchaseInfo = new FieldDescriptor[] {
fieldWithPath("id").description("id of the commercialization").type(java.lang.String.class),
fieldWithPath("pricingId").description("additional pricing of the commercialization").type(java.lang.String.class),
fieldWithPath("price").description("price of the commercialization").type(java.lang.Double.class),
fieldWithPath("type").description("").type(ArticleType.class) };
public static FieldDescriptor[] fdPurchaseInfoList = new FieldDescriptor[] {
fieldWithPath("[].id").description("id of the commercialization").type(java.lang.String.class),
fieldWithPath("[].pricingId").description("additional pricing of the commercialization").type(java.lang.String.class),
fieldWithPath("[].price").description("price of the commercialization").type(java.lang.Double.class),
fieldWithPath("[].type").description("").type(ArticleType.class) };
}
当调用 mockMvc.perform 以创建具有 OrderInfo.class 类型的 requestBody 和 responseBody 的 HTTP POST 时:
mockMvc.perform(postRequest)
.andDo(document("orderInfoCreate", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()),
pathParameters(parameterWithName("username").description("the name of the user")),
responseFields(OrderInfoFieldDescriptor.fdOrderInfo),
requestFields(OrderInfoFieldDescriptor.fdOrderInfo))).andExpect(status().isCreated())
.andExpect(content().json(orderInfoAsJson));
验证适用于 paymentMode 和 serialNumber 但对于 purchase 失败,但出现以下异常:
org.springframework.restdocs.snippet.SnippetException: The following parts of the payload were not documented:
{
"purchase" : {
"id" : "purchaseId",
"pricingId" : "pricingId",
"price" : 12.0,
"type" : "EPISODE"
},
"serialNumber" : "serialNumber"
}
尽管请求正文和响应正文看起来不错:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /api/users/myUserName/orders
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Host:"host:posrt", Accept:"application/json;charset=UTF-8", Cookie:"identity=cookieForTest"]
Body = {"purchase":{"id":"purchaseId","pricingId":"pricingId","price":12.0,"type":"EPISODE"},"paymentMode":"POSTPAID","serialNumber":"serialNumber"}
Session Attrs = {}
并且:
MockHttpServletResponse:
Status = 201
Error message = null
Headers = [Content-Type:"application/json;charset=UTF-8"]
Content type = application/json;charset=UTF-8
Body = {"purchase":{"id":"purchaseId","pricingId":"pricingId","price":12.0,"type":"EPISODE"},"paymentMode":"POSTPAID","serialNumber":"serialNumber"}
Forwarded URL = null
Redirected URL = null
Cookies = []
从 Spring Boot 1x 迁移到 2x 后出现此问题。
您知道问题出在哪里吗?
谢谢:)
您似乎依赖 purchase
来记录 purchase
及其包含的所有内容,即 purchase.id
、purchase.pricingId
、purchase.price
和purchase.type
。这在 REST Docs 1.1 及之前的版本中都有效,但它产生了一个问题,即人们会在记录嵌套字段的父字段时意外地错过记录嵌套字段。 this question and also in this Spring REST Docs issue 中对此进行了讨论。该问题在 REST Docs 1.2 中引入了一个变化,默认情况下记录一个字段不再记录它的所有后代。您会受到此更改的影响,因为升级到 Spring Boot 2 意味着您也已升级到 REST Docs 2。
如果您想保留现有行为并仅使用 purchase
来记录它及其所有后代,您应该将 fieldWithPath
替换为 subsectionWithPath
。如果您想记录 purchase
以下的所有内容,您应该为 purchase.id
、purchase.pricingId
、purchase.price
和 purchase.type
.
添加额外的描述符
我有一个 OrderInfo class:
@ApiModel(description = "object needed to make an order")
public class OrderInfo implements Serializable {
private static final long serialVersionUID = 1L;
@JsonProperty("purchase")
private PurchaseInfo purchase = null;
@JsonProperty("paymentMode")
private PaymentMode paymentMode = null;
@JsonProperty("serialNumber")
private String serialNumber = null;
public OrderInfo purchase(PurchaseInfo purchase) {
this.purchase = purchase;
return this;
}
@ApiModelProperty(required = true, value = "coming from commercialization")
@NotNull
@Valid
public PurchaseInfo getPurchase() {
return purchase;
}
public void setPurchase(PurchaseInfo purchase) {
this.purchase = purchase;
}
public OrderInfo paymentMode(PaymentMode paymentMode) {
this.paymentMode = paymentMode;
return this;
}
@ApiModelProperty(required = true, value = "")
@NotNull
@Valid
public PaymentMode getPaymentMode() {
return paymentMode;
}
public void setPaymentMode(PaymentMode paymentMode) {
this.paymentMode = paymentMode;
}
public OrderInfo serialNumber(String serialNumber) {
this.serialNumber = serialNumber;
return this;
}
@ApiModelProperty(value = "The serial number of the registered device")
public String getSerialNumber() {
return serialNumber;
}
public void setSerialNumber(String serialNumber) {
this.serialNumber = serialNumber;
}
}
有一个 PurchaseInfo 子对象:
@ApiModel(description = "Info necessary to order a video, an episode or a season")
public class PurchaseInfo implements Serializable {
private static final long serialVersionUID = 1L;
@JsonProperty("id")
private String id = null;
@JsonProperty("pricingId")
private String pricingId = null;
@JsonProperty("price")
private Double price = null;
@JsonProperty("type")
private ArticleType type = null;
public PurchaseInfo id(String id) {
this.id = id;
return this;
}
@ApiModelProperty(required = true, value = "id of the commercialization")
@NotNull
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public PurchaseInfo pricingId(String pricingId) {
this.pricingId = pricingId;
return this;
}
@ApiModelProperty(required = true, value = "additional pricing of the commercialization")
@NotNull
public String getPricingId() {
return pricingId;
}
public void setPricingId(String pricingId) {
this.pricingId = pricingId;
}
public PurchaseInfo price(Double price) {
this.price = price;
return this;
}
@ApiModelProperty(required = true, value = "price of the commercialization")
@NotNull
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public PurchaseInfo type(ArticleType type) {
this.type = type;
return this;
}
@ApiModelProperty(required = true, value = "")
@NotNull
@Valid
public ArticleType getType() {
return type;
}
public void setType(ArticleType type) {
this.type = type;
}
}
并生成对应的FieldDescriptorclasses:
public class OrderInfoFieldDescriptor {
public static FieldDescriptor[] fdOrderInfo = new FieldDescriptor[] {
fieldWithPath("purchase").description("coming from commercialization").type(PurchaseInfo.class),
fieldWithPath("paymentMode").description("").type(PaymentMode.class),
fieldWithPath("serialNumber").description("The serial number of the registered device").type(java.lang.String.class).optional() };
public static FieldDescriptor[] fdOrderInfoList = new FieldDescriptor[] {
fieldWithPath("[].purchase").description("coming from commercialization").type(PurchaseInfo.class),
fieldWithPath("[].paymentMode").description("").type(PaymentMode.class),
fieldWithPath("[].serialNumber").description("The serial number of the registered device").type(java.lang.String.class).optional() };
}
并且:
public class PurchaseInfoFieldDescriptor {
public static FieldDescriptor[] fdPurchaseInfo = new FieldDescriptor[] {
fieldWithPath("id").description("id of the commercialization").type(java.lang.String.class),
fieldWithPath("pricingId").description("additional pricing of the commercialization").type(java.lang.String.class),
fieldWithPath("price").description("price of the commercialization").type(java.lang.Double.class),
fieldWithPath("type").description("").type(ArticleType.class) };
public static FieldDescriptor[] fdPurchaseInfoList = new FieldDescriptor[] {
fieldWithPath("[].id").description("id of the commercialization").type(java.lang.String.class),
fieldWithPath("[].pricingId").description("additional pricing of the commercialization").type(java.lang.String.class),
fieldWithPath("[].price").description("price of the commercialization").type(java.lang.Double.class),
fieldWithPath("[].type").description("").type(ArticleType.class) };
}
当调用 mockMvc.perform 以创建具有 OrderInfo.class 类型的 requestBody 和 responseBody 的 HTTP POST 时:
mockMvc.perform(postRequest)
.andDo(document("orderInfoCreate", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()),
pathParameters(parameterWithName("username").description("the name of the user")),
responseFields(OrderInfoFieldDescriptor.fdOrderInfo),
requestFields(OrderInfoFieldDescriptor.fdOrderInfo))).andExpect(status().isCreated())
.andExpect(content().json(orderInfoAsJson));
验证适用于 paymentMode 和 serialNumber 但对于 purchase 失败,但出现以下异常:
org.springframework.restdocs.snippet.SnippetException: The following parts of the payload were not documented:
{
"purchase" : {
"id" : "purchaseId",
"pricingId" : "pricingId",
"price" : 12.0,
"type" : "EPISODE"
},
"serialNumber" : "serialNumber"
}
尽管请求正文和响应正文看起来不错:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /api/users/myUserName/orders
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Host:"host:posrt", Accept:"application/json;charset=UTF-8", Cookie:"identity=cookieForTest"]
Body = {"purchase":{"id":"purchaseId","pricingId":"pricingId","price":12.0,"type":"EPISODE"},"paymentMode":"POSTPAID","serialNumber":"serialNumber"}
Session Attrs = {}
并且:
MockHttpServletResponse:
Status = 201
Error message = null
Headers = [Content-Type:"application/json;charset=UTF-8"]
Content type = application/json;charset=UTF-8
Body = {"purchase":{"id":"purchaseId","pricingId":"pricingId","price":12.0,"type":"EPISODE"},"paymentMode":"POSTPAID","serialNumber":"serialNumber"}
Forwarded URL = null
Redirected URL = null
Cookies = []
从 Spring Boot 1x 迁移到 2x 后出现此问题。
您知道问题出在哪里吗?
谢谢:)
您似乎依赖 purchase
来记录 purchase
及其包含的所有内容,即 purchase.id
、purchase.pricingId
、purchase.price
和purchase.type
。这在 REST Docs 1.1 及之前的版本中都有效,但它产生了一个问题,即人们会在记录嵌套字段的父字段时意外地错过记录嵌套字段。 this question and also in this Spring REST Docs issue 中对此进行了讨论。该问题在 REST Docs 1.2 中引入了一个变化,默认情况下记录一个字段不再记录它的所有后代。您会受到此更改的影响,因为升级到 Spring Boot 2 意味着您也已升级到 REST Docs 2。
如果您想保留现有行为并仅使用 purchase
来记录它及其所有后代,您应该将 fieldWithPath
替换为 subsectionWithPath
。如果您想记录 purchase
以下的所有内容,您应该为 purchase.id
、purchase.pricingId
、purchase.price
和 purchase.type
.