mae*_*ael 46 java rest spring json spring-mvc
我有一个项目,我使用Spring MVC + Jackson来构建REST服务.假设我有以下java实体
public class MyEntity {
private Integer id;
private boolean aBoolean;
private String aVeryBigString;
//getter & setters
}
Run Code Online (Sandbox Code Playgroud)
有时候,我只是想更新布尔值,我不认为用更大的字符串发送整个对象只是为了更新一个简单的布尔值.所以,我考虑过使用PATCH HTTP方法只发送需要更新的字段.所以,我在我的控制器中声明了以下方法:
@RequestMapping(method = RequestMethod.PATCH)
public void patch(@RequestBody MyVariable myVariable) {
//calling a service to update the entity
}
Run Code Online (Sandbox Code Playgroud)
问题是:我如何知道哪些字段需要更新?例如,如果客户端只想更新布尔值,我将得到一个空的"aVeryBigString"对象.我怎么知道用户只想更新布尔值,但不想清空字符串?
我通过构建自定义URL"解决"了这个问题.例如,以下URL:POST/myentities/1/aboolean/true将映射到只允许更新布尔值的方法.此解决方案的问题在于它不符合REST.我不希望100%兼容REST,但我不愿意提供自定义URL来更新每个字段(特别是考虑到当我想更新多个字段时它会导致问题).
另一个解决方案是将"MyEntity"拆分为多个资源并只更新这些资源,但我觉得它没有意义:"MyEntity" 是一个普通资源,它不是由其他资源组成的.
那么,有一种解决这个问题的优雅方式吗?
vin*_*ine 16
这可能很晚,但为了新手和遇到同样问题的人,让我分享一下我自己的解决方案.
在我过去的项目中,为了简单起见,我只使用原生的Java Map.它将捕获所有新值,包括客户端显式设置为null的空值.此时,很容易确定哪些java属性需要设置为null,与使用相同的POJO作为域模型不同,您将无法区分客户端设置的哪些字段为null和它们不包含在更新中,但默认情况下为null.
此外,您必须要求http请求发送要更新的记录的ID,并且不要将其包含在修补程序数据结构中.我所做的是,将URL中的ID设置为路径变量,将补丁数据设置为PATCH主体.然后使用ID,您将首先通过域模型获取记录,然后使用HashMap,您可以使用映射器服务或实用程序,用于修补相关域模型的更改.
更新
您可以使用这种通用代码为您的服务创建抽象超类,您必须使用Java Generics.这只是可能实现的一部分,我希望你能得到这个想法.使用Orika或Dozer等mapper框架也更好.
public abstract class AbstractService<Entity extends BaseEntity, DTO extends BaseDto> {
@Autowired
private MapperService mapper;
@Autowired
private BaseRepo<Entity> repo;
private Class<DTO> dtoClass;
private Class<Entity> entityCLass;
public AbstractService(){
entityCLass = (Class<Entity>) SomeReflectionTool.getGenericParameter()[0];
dtoClass = (Class<DTO>) SomeReflectionTool.getGenericParameter()[1];
}
public DTO patch(Long id, Map<String, Object> patchValues) {
Entity entity = repo.get(id);
DTO dto = mapper.map(entity, dtoClass);
mapper.map(patchValues, dto);
Entity updatedEntity = toEntity(dto);
save(updatedEntity);
return dto;
}
}
Run Code Online (Sandbox Code Playgroud)
Che*_*pir 10
正确的方法是在JSON PATCH RFC 6902中提出的方法
请求示例如下:
PATCH http://example.com/api/entity/1 HTTP/1.1
Content-Type: application/json-patch+json
[
{ "op": "replace", "path": "aBoolean", "value": true }
]
Run Code Online (Sandbox Code Playgroud)
深入研究后,我发现了一种可接受的解决方案,使用的是Spring MVC当前使用的相同方法,DomainObjectReader另请参见:JsonPatchHandler
@RepositoryRestController
public class BookCustomRepository {
private final DomainObjectReader domainObjectReader;
private final ObjectMapper mapper;
private final BookRepository repository;
@Autowired
public BookCustomRepository(BookRepository bookRepository,
ObjectMapper mapper,
PersistentEntities persistentEntities,
Associations associationLinks) {
this.repository = bookRepository;
this.mapper = mapper;
this.domainObjectReader = new DomainObjectReader(persistentEntities, associationLinks);
}
@PatchMapping(value = "/book/{id}", consumes = {MediaType.APPLICATION_JSON_UTF8_VALUE, MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<?> patch(@PathVariable String id, ServletServerHttpRequest request) throws IOException {
Book entityToPatch = repository.findById(id).orElseThrow(ResourceNotFoundException::new);
Book patched = domainObjectReader.read(request.getBody(), entityToPatch, mapper);
repository.save(patched);
return ResponseEntity.noContent().build();
}
}
Run Code Online (Sandbox Code Playgroud)
重点PATCH是您没有发送整个实体表示,所以我不理解您对空字符串的评论。您必须处理某种简单的 JSON,例如:
{ aBoolean: true }
Run Code Online (Sandbox Code Playgroud)
并将其应用到指定的资源。这个想法是,接收到的是所需资源状态和当前资源状态的差异。
你可以用Optional<>它:
public class MyEntityUpdate {
private Optional<String> aVeryBigString;
}
Run Code Online (Sandbox Code Playgroud)
这样您就可以按如下方式检查更新对象:
if(update.getAVeryBigString() != null)
entity.setAVeryBigString(update.getAVeryBigString().get());
Run Code Online (Sandbox Code Playgroud)
如果aVeryBigStringJSON 文档中没有该字段,则 POJOaVeryBigString字段将为null. 如果它在 JSON 文档中,但带有null值,则 POJO 字段将是Optional带有包装值的null。该解决方案允许您区分“无更新”和“设置为空”的情况。
mvb*_*b13 -20
您可以将布尔值更改为布尔值,并为您不想更新的所有字段分配空值。唯一一个非空值将定义客户端要更新的字段。
| 归档时间: |
|
| 查看次数: |
30125 次 |
| 最近记录: |