如何为Spring HATEOAS HAL Forms嵌套类配置属性与选项?
问题描述
JSON响应中,名为files的属性在HAL Forms的_templates配置里未包含自身内部属性与选项,该属性对应RegisterProductRequest类中嵌套的AddProductFileRequest类。此前处理DeliveryType枚举时,已通过HalFormsConfiguration类成功添加选项,现在需要为files属性配置其内部属性及对应选项。
当前JSON响应
{ "id": 3, "name": "Esse é o novo nome do prodaa", "description": "description", "price": 1200.00, "quantity": 3, "active": true, "sku": "321", "tags": [ "tag1", "tag2" ], "promotion": { "price": 500.00, "start": "2000-03-03T12:00:00", "finish": "2025-03-03T17:00:00", "available": true }, "files": [ { "id": 4, "type": "IMAGE", "position": 0, "file_data": { "file_name": "Default.jpeg" } }, { "id": 5, "type": "IMAGE", "position": 1, "file_data": { "file_name": "Default.jpeg" } } ], "nameFirstImage": "Default.jpeg", "bar_code": "31231", "category_id": 1, "package_info": { "format": "ROLL", "height": 2.0, "width": 20.5, "length": 5.0, "weight": 24.0 }, "deliveries_types": [], "_links": { "self": { "href": "http://localhost:8080/public/products/3" }, "all_products": { "href": "http://localhost:8080/public/products" } }, "_templates": { "default": { "method": "POST", "properties": [ { "name": "active", "required": true }, { "name": "bar_code", "type": "text" }, { "name": "category_id", "required": true, "type": "number" }, { "name": "delivery_types", "required": true, "min": 1, "max": 2, "type": "checkbox", "options": { "inline": [ "CORREIOS_SEDEX", "CORREIOS_PAC", "EXPRESS", "PICK_UP_IN_STORE", "DIGITAL" ] } }, { "name": "description", "regex": "^(?=\\s*\\S).*$", "required": true, "type": "textarea" }, { "name": "files", "required": true }, { "name": "name", "regex": "^(?=\\s*\\S).*$", "required": true, "type": "text" }, { "name": "package_info" }, { "name": "price", "required": true, "type": "number" }, { "name": "promotion" }, { "name": "quantity", "required": true, "type": "number" }, { "name": "sku", "type": "text" }, { "name": "tags" } ], "target": "http://localhost:8080/admin/products" } } }
相关Java类
RegisterProductRequest.java
@Getter @Setter @NoArgsConstructor @AllArgsConstructor public class RegisterProductRequest { @NotBlank private String name; @NotBlank @InputType(InputTypeHF.TEXTAREA) private String description; @NotNull @Positive private BigDecimal price; @NotNull private Integer quantity; @NotNull private Boolean active; @JsonProperty("bar_code") private String barCode; private String sku; private String[] tags; private ProductPromotionRequest promotion; @JsonProperty("package_info") private PackageInfo packageInfo; @NotNull @JsonProperty("category_id") private Long categoryId; @NotNull @JsonProperty("delivery_types") @Size(max = 2, min = 1) @InputType(InputTypeHF.CHECKBOX) private Set<DeliveryType> deliveryTypes; @NotNull private Set<AddProductFileRequest> files; }
AddProductFileRequest.java
@Getter @Setter @NoArgsConstructor @AllArgsConstructor public class AddProductFileRequest { private ProductFileType type; @JsonProperty("file_name") private String fileName; @JsonProperty("url_video") private String urlVideo; @NotNull private Byte position; }
ProductFileType.java
public enum ProductFileType { IMAGE, VIDEO }
当前配置类
@Configuration @EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL_FORMS) public class HateoasConfiguration { @Bean HalFormsConfiguration halFormsConfiguration() { return new HalFormsConfiguration() .customize(new ObjectMapper()) .withOptions(RegisterProductRequest.class, "delivery_types", metadata -> HalFormsOptions.inline(DeliveryType.values())); } }
预期效果
{ "id": 3, "name": "Product Name", ... "files": [ { "type": "IMAGE", "file_name": "Default.jpeg", "url_video": "http://example.com/video", "position": 0 }, { "type": "VIDEO", "file_name": "Video.mp4", "url_video": "http://example.com/video", "position": 1 } ], ... "_templates": { "default": { "method": "POST", "properties": [ ... { "name": "files", "required": true, "properties": [ { "name": "type", "required": true, "type": "select", "options": { "inline": [ "IMAGE", "VIDEO", "DOCUMENT" ] } }, { "name": "file_name", "required": true, "type": "text" }, { "name": "url_video", "type": "text" }, { "name": "position", "required": true, "type": "number" } ] } ... ] } } }
解决方案
修改HateoasConfiguration类,通过withProperties方法为files属性定义内部元数据,同时为type字段添加枚举选项:
@Configuration @EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL_FORMS) public class HateoasConfiguration { @Bean HalFormsConfiguration halFormsConfiguration() { return new HalFormsConfiguration() .customize(new ObjectMapper()) // 原有delivery_types配置 .withOptions(RegisterProductRequest.class, "delivery_types", metadata -> HalFormsOptions.inline(DeliveryType.values())) // 新增files属性的内部配置 .withProperties(RegisterProductRequest.class, "files", metadata -> { // 定义type字段 HalFormsProperty typeProperty = HalFormsProperty.builder() .name("type") .required(true) .type("select") .options(HalFormsOptions.inline(ProductFileType.values())) .build(); // 定义file_name字段 HalFormsProperty fileNameProperty = HalFormsProperty.builder() .name("file_name") .required(true) .type("text") .build(); // 定义url_video字段 HalFormsProperty urlVideoProperty = HalFormsProperty.builder() .name("url_video") .type("text") .build(); // 定义position字段 HalFormsProperty positionProperty = HalFormsProperty.builder() .name("position") .required(true) .type("number") .build(); return List.of(typeProperty, fileNameProperty, urlVideoProperty, positionProperty); }); } }
说明
- 使用
withProperties方法针对RegisterProductRequest的files属性,添加其内部嵌套的AddProductFileRequest类对应的字段配置。 - 每个字段通过
HalFormsProperty.builder()设置名称、必填性、输入类型,其中type字段关联ProductFileType枚举的选项。 - 如果需要添加
DOCUMENT枚举值,直接在ProductFileType枚举中新增即可,配置会自动同步。
内容的提问来源于stack exchange,提问作者Daniel Schiavo
相关产品推荐
相关产品推荐

