当前位置: 首页 > 知识库问答 >
问题:

Spring MVC补丁方法:部分更新

穆飞龙
2023-03-14

我有一个项目,我正在使用Spring MVC Jackson构建REST服务。假设我有以下java实体

public class MyEntity {
    private Integer id;
    private boolean aBoolean;
    private String aVeryBigString;
    //getter & setters
}

有时候,我只是想更新布尔值,我不认为发送带有大字符串的整个对象只是为了更新一个简单的布尔值是个好主意。所以,我考虑过使用PATCH HTTP方法只发送需要更新的字段。所以,我在我的控制器中声明了以下方法:

@RequestMapping(method = RequestMethod.PATCH)
public void patch(@RequestBody MyVariable myVariable) {
    //calling a service to update the entity
}

问题是:我如何知道哪些字段需要更新?例如,如果客户端只想更新布尔值,我将得到一个带有空“aVeryBigString”的对象。我怎么知道用户只想更新布尔值,但不想清空字符串?

我通过构建自定义URL“解决”了这个问题。例如,以下URL:POST/myentities/1/aboolean/true将映射到一个只允许更新布尔值的方法。这个解决方案的问题是它不符合REST。我不想百分之百地遵从REST,但我对提供一个自定义URL来更新每个字段感到不舒服(尤其是当我想更新几个字段时,它会导致问题)。

另一个解决方案是将“MyEntity”拆分为多个资源,然后只更新这些资源,但我觉得这没有意义:“MyEntity”是一个普通的资源,它不是由其他资源组成的。

那么,有没有一种优雅的方法来解决这个问题?

共有3个答案

广昊昊
2023-03-14

在仔细研究之后,我发现了一个可以接受的解决方案,使用的方法与Spring MVC当前使用的方法相同

import org.springframework.data.rest.webmvc.mapping.Associations

@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();
    }

}
简景焕
2023-03-14

正确的方法是JSON补丁RFC 6902中提出的方法

一个请求示例是:

PATCH http://example.com/api/entity/1 HTTP/1.1
Content-Type: application/json-patch+json 

[
  { "op": "replace", "path": "aBoolean", "value": true }
]
韦高谊
2023-03-14

这可能已经很晚了,但为了新手和遇到相同问题的人,让我向你们分享我自己的解决方案。

在我过去的项目中,为了简单起见,我只使用本机java映射。它将捕获所有新值,包括客户端显式设置为null的null值。此时,很容易确定哪些java属性需要设置为null,这与使用与域模型相同的POJO时不同,您将无法区分哪些字段由客户端设置为null,哪些字段只是不包括在更新中,但默认情况下为null。

此外,您必须要求超文本传输协议请求发送您要更新的记录的ID,并且不要将其包含在补丁html" target="_blank">数据结构中。我所做的是将URL中的ID设置为路径变量,将补丁数据设置为PATCH正文。然后使用ID,您将首先通过域模型获取记录,然后使用HashMap,您可以使用映射器服务或实用程序将更改修补到相关域模型。

使现代化

你可以用这种泛型代码为你的服务创建一个抽象超类,你必须使用Java泛型。这只是可能实现的一部分,我希望你能理解。此外,最好使用映射器框架,如Orika或Dozer。

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;
    }
}
 类似资料:
  • 我有一个包含一些图像实体,我想使用HttpPatch方法更新它。 要创建新样式,我使用以下方法: 现在我正在尝试创建一个方法来使用HttpPatch方法更新这个样式。我试过了,但是没有选择在Swagger上上传文件: 这是StyleFiles类: 我正在考虑制作两个单独的endpoint,一个用于更新文件,另一个用于更新实体本身。但我不想那样做。

  • Git 中的一些命令是以引入的变更即提交这样的概念为中心的,这样一系列的提交,就是一系列的补丁。 这些命令以这样的方式来管理你的分支。 git cherry-pick git cherry-pick 命令用来获得在单个提交中引入的变更,然后尝试将作为一个新的提交引入到你当前分支上。 从一个分支单独一个或者两个提交而不是合并整个分支的所有变更是非常有用的。 在 变基与拣选工作流 一节中描述和演示了

  • 当我尝试点击具有方法的请求时,我收到以下错误:PATCH 使用JMeter版本:-apache-JMeter-5.4.1 如果我能得到专家的帮助来解决他们的问题,那将会很有帮助。 谢谢

  • 通过检测打补丁要替换文件的版本来实现跳过已安装补丁,在nsis,也就是要用到GetFileVersion这个函数来实现: !include "LogicLib.nsh" ; 安装程序初始定义常量 !define PRODUCT_NAME "office 2003 sp3后续补丁集" !define PRODUCT_VERSION "1.0" !define PRODUCT_PUBLISHER "

  • 我使用的是spring 3.2.4,我读到spring MVC 3.2.x明确支持补丁HTTP方法: http://docs.spring.io/spring/docs/3.2.0.rc1/reference/html/new-in-3.2.html 然而,当我在Tomcat(7.0.41)中部署应用程序时,我在使用补丁方法时系统地得到一个501错误。 为什么这行不通?有没有办法让它奏效?我是否应

  • 使用JSONAPI1.0标准设计API,没有PUT方法。只有用于创建资源的POST方法和用于部分更新的修补程序。我们有这样的用例:用户可以向服务器发送请求,如果资源不存在,则必须创建资源,否则必须更新资源。RFC将这种方法描述为PUT。接下来引用提到的RFC5789标准补丁有信息: “如果Request-URI没有指向现有资源,服务器可能会创建一个新资源,具体取决于补丁文档类型(是否可以在逻辑上修