json RestTemplate PATCH 请求

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29447382/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 17:49:29  来源:igfitidea点击:

RestTemplate PATCH request

jsonspringresttemplate

提问by SGB

I have the following definition for PersonDTO:

我对 PersonDTO 有以下定义:

public class PersonDTO
{
    private String id
    private String firstName;
    private String lastName;
    private String maritalStatus;
}

Here is a sample record :

这是一个示例记录:

{
    "id": 1,
    "firstName": "John",
    "lastName": "Doe",
    "maritalStatus": "married"
}

Now, John Doe gets divorced. So I need to send a PATCH request to this URL:

现在,John Doe 离婚了。所以我需要向这个 URL 发送一个 PATCH 请求:

http://localhost:8080/people/1

With the following request body:

使用以下请求正文:

{
    "maritalStatus": "divorced"
}

I cannot figure out how to do it. Here is what I tried so far:

我不知道该怎么做。这是我到目前为止尝试过的:

// Create Person
PersonDTO person = new PersonDTO();
person.setMaritalStatus("Divorced");

// Create HttpEntity
final HttpEntity<ObjectNode> requestEntity = new HttpEntity<>(person);

// Create URL (for eg: localhost:8080/people/1)
final URI url = buildUri(id);

ResponseEntity<Void> responseEntity = restTemplate.exchange(url, HttpMethod.PATCH, requestEntity, Void.class);

Here are the problems with the above:

以下是上述问题:

1) As I am setting only MaritalStatus, the other fields would all be null. So if I print out the request, it will look like this:

1) 因为我只设置 MaritalStatus,所以其他字段都为空。因此,如果我打印出请求,它将如下所示:

{
    "id": null,
    "firstName": "null",
    "lastName": "null",
    "maritalStatus": "married" // I only need to update this field.
}

Does that mean that I have to a GET before I do a PATCH?

这是否意味着在我做补丁之前我必须先获取?

2) I am getting the following stack trace:

2)我得到以下堆栈跟踪:

08:48:52.717 ERROR c.n.d.t.s.PersonServiceImpl - Unexpected Exception  : 
org.springframework.web.client.ResourceAccessException: I/O error on PATCH request for "http://localhost:8080/people/1":Invalid HTTP method: PATCH; nested exception is java.net.ProtocolException: Invalid HTTP method: PATCH
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:580) ~[spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:545) ~[spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:466) ~[spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at com.sp.restclientexample..service.PersonServiceImpl.doPatch(PersonServiceImpl.java:75) ~[classes/:na]
    at com.sp.restclientexample..service.PatchTitle.itDoPatch(PatchTitle.java:53) [test-classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_20]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_20]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_20]
    at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_20]
    at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:50) [junit-4.12.jar:4.12]
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.12.jar:4.12]
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) [junit-4.12.jar:4.12]
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:73) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) [junit-4.12.jar:4.12]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:224) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:83) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE]
    at org.junit.runners.ParentRunner.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner.access
RestTemplate restTemplate = new RestTemplate();

HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setConnectTimeout(TIMEOUT);
requestFactory.setReadTimeout(TIMEOUT);

restTemplate.setRequestFactory(requestFactory);
0(ParentRunner.java:58) [junit-4.12.jar:4.12] at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12] at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE] at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:68) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE] at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12] at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163) [spring-test-4.1.6.RELEASE.jar:4.1.6.RELEASE] at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) [.cp/:na] at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:na] at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) [.cp/:na] at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675) [.cp/:na] at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) [.cp/:na] at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) [.cp/:na] Caused by: java.net.ProtocolException: Invalid HTTP method: PATCH at java.net.HttpURLConnection.setRequestMethod(HttpURLConnection.java:440) ~[na:1.8.0_20] at sun.net.www.protocol.http.HttpURLConnection.setRequestMethod(HttpURLConnection.java:517) ~[na:1.8.0_20] at org.springframework.http.client.SimpleClientHttpRequestFactory.prepareConnection(SimpleClientHttpRequestFactory.java:209) ~[spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE] at org.springframework.http.client.SimpleClientHttpRequestFactory.createRequest(SimpleClientHttpRequestFactory.java:138) ~[spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE] at org.springframework.http.client.support.HttpAccessor.createRequest(HttpAccessor.java:76) ~[spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE] at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:565) ~[spring-web-4.1.6.RELEASE.jar:4.1.6.RELEASE] ... 33 common frames omitted

Appreciate any pointers from folks who have written client applications to consume a Restful webservice using Spring's RestTemplate.

感谢编写客户端应用程序以使用 Spring 的 RestTemplate 使用 Restful web 服务的人的任何指示。

For completeness, let me also state that we use SpringDataRest for our backend restful webservices.

为了完整起见,我还要声明我们将 SpringDataRest 用于我们的后端 Restful Web 服务。

SGB

SGB

回答by Tulio Gomez Rodrigues

I solved this problem just adding a new HttpRequestFactory to my restTemplate instance. Like this

我解决了这个问题,只是在我的 restTemplate 实例中添加了一个新的 HttpRequestFactory 。像这样

@Autowired
private TestRestTemplate restTemplate;

@Before
public void setup() {
    restTemplate.getRestTemplate().setRequestFactory(new HttpComponentsClientHttpRequestFactory());
}

For TestRestTemplate, add

对于 TestRestTemplate,添加

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.4.1</version>
</dependency>

PS: You will need add httpClient component in your project

PS:您需要在项目中添加 httpClient 组件

public PersonRestClient(RestTemplateBuilder restTemplateBuilder) {
  this.restTemplate = restTemplateBuilder.requestFactory(new HttpComponentsClientHttpRequestFactory()).build();
}

回答by sompnd

For cases where RestTemplateis built from a RestTemplateBuilder, constructor for the custom RestClient can be written as,

对于RestTemplate从 aRestTemplateBuilder构建的情况,自定义 RestClient 的构造函数可以写为,

public void patch(M theEntity, Integer entityId, String linkName, URI linkUri) {
    ObjectMapper objectMapper = getObjectMapperWithHalModule();
    ObjectNode linkedNode = (ObjectNode) objectMapper.valueToTree(theEntity);
    linkedNode.put(linkName, linkUri.getPath());

    HttpEntity<ObjectNode> requestEntity = new HttpEntity<>(linkedNode);

    restTemplate.exchange(uri + "/" + entityId, HttpMethod.PATCH, requestEntity, Void.class);
}

private ObjectMapper getObjectMapperWithHalModule() {
    if(objectMapperHal == null) {
        objectMapperHal = new ObjectMapper();
        objectMapperHal.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapperHal.registerModule(new Hymanson2HalModule());
    }

    return objectMapperHal;
}

Also, the org.apache.httpcomponents.httpclientdependency needs to added to pom.

此外,org.apache.httpcomponents.httpclient需要将依赖项添加到 pom.xml 中。

回答by Gus

I created a generic method to do this when there are linked resources involved:

当涉及链接资源时,我创建了一个通用方法来执行此操作:

    try {

        //This is just to avoid ssl hostname verification and to trust all, you can use simple Http client also
        CloseableHttpClient httpClient = HttpClientBuilder.create().setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build())
                .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE).build();

        HttpPatch request = new HttpPatch(REST_SERVICE_URL);
        StringEntity params = new StringEntity(JSON.toJSONString(payload), ContentType.APPLICATION_JSON);
        request.setEntity(params);
        request.addHeader(org.apache.http.HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
        request.addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
        //You can use other authorization method, like user credentials
        request.addHeader(HttpHeaders.AUTHORIZATION, OAuth2AccessToken.BEARER_TYPE + " " + accessToken);
        HttpResponse response =     httpClient.execute(request);            

        String statusCode = response.getStatusLine().getStatusCode();

    } catch (Exception ex) {
        // handle exception here
    }

Feel free to look at an implementation of this example at my full jal+json implementation

随意在我的完整 jal+json 实现中查看此示例的实现

回答by Dezso Gabos

If you have an older spring version than 3.1.0, then you don't have the PATCH method in the HttpMethods. You can still use HttpClient from apache. Here is a short example on how I did it:

如果你有一个比 3.1.0 更旧的 spring 版本,那么你在 HttpMethods 中没有 PATCH 方法。您仍然可以从 apache 使用 HttpClient。这是我如何做到的一个简短示例:

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    headers.add("Authorization", OAuth2AccessToken.BEARER_TYPE + " " + accessToken);
    final HttpEntity<String> entity = new HttpEntity<String>(JSON.toJSONString(payload), headers);
    RestTemplate restTemplate = new RestTemplate();
    try {
        ResponseEntity<String> response = restTemplate.exchange(REST_SERVICE_URL, HttpMethod.PATCH, entity, String.class);
        String statusCode =  response.getStatusCode();
    } catch (HttpClientErrorException e) {
        // handle exception here
    }

The equivalent of this, with RestTemplate would be:

与 RestTemplate 等价的将是:

##代码##

Also, make sure the payload only contains the values you need to change, and make sure you are sending the request to the right URL. (this can be in some cases something ending like /api/guest/{id} )

此外,请确保负载仅包含您需要更改的值,并确保您将请求发送到正确的 URL。(在某些情况下,这可能以 /api/guest/{id} 结尾)