RestTemplate PATCH请求

SGB*_*SGB 26 spring json resttemplate

我对PersonDTO有以下定义:

public class PersonDTO
{
    private String id
    private String firstName;
    private String lastName;
    private String maritalStatus;
}
Run Code Online (Sandbox Code Playgroud)

这是一个示例记录:

{
    "id": 1,
    "firstName": "John",
    "lastName": "Doe",
    "maritalStatus": "married"
}
Run Code Online (Sandbox Code Playgroud)

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

http://localhost:8080/people/1
Run Code Online (Sandbox Code Playgroud)

使用以下请求正文:

{
    "maritalStatus": "divorced"
}
Run Code Online (Sandbox Code Playgroud)

我无法弄清楚该怎么做.这是我到目前为止尝试的内容:

// 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);
Run Code Online (Sandbox Code Playgroud)

以下是上述问题:

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

{
    "id": null,
    "firstName": "null",
    "lastName": "null",
    "maritalStatus": "married" // I only need to update this field.
}
Run Code Online (Sandbox Code Playgroud)

这是否意味着我在进行PATCH之前必须进行GET操作?

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$1.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$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner$1.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$000(ParentRunner.java:58) [junit-4.12.jar:4.12]
    at org.junit.runners.ParentRunner$2.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
Run Code Online (Sandbox Code Playgroud)

感谢使用Spring的RestTemplate编写客户端应用程序来使用Restful Web服务的人们的任何指针.

为了完整起见,我还要说明我们将SpringDataRest用于后端的restful webservices.

SGB

小智 67

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

RestTemplate restTemplate = new RestTemplate();

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

restTemplate.setRequestFactory(requestFactory);
Run Code Online (Sandbox Code Playgroud)

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

@Autowired
private TestRestTemplate restTemplate;

@Before
public void setup() {
    restTemplate.getRestTemplate().setRequestFactory(new HttpComponentsClientHttpRequestFactory());
}
Run Code Online (Sandbox Code Playgroud)

  • 为了保存一些行,你可以在一行`new RestTemplate(new HttpComponentsClientHttpRequestFactory())中进行RestTemplate初始化. (7认同)
  • @adebasi 你知道为什么需要这样做吗? (2认同)
  • 我认为使用“ this”意味着必须设置“ HttpComponentsClientHttpRequestFactory”。此评论(https://jira.spring.io/browse/SPR-7985?focusedCommentId=80924&amp;page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-80924)回答了您的问题。 (2认同)
  • 2021 年仍然是个问题,WTF。但感谢您的解决方案:) (2认同)

ami*_*mir 9

对我来说通过添加以下行解决:

restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
Run Code Online (Sandbox Code Playgroud)


som*_*pnd 7

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

public PersonRestClient(RestTemplateBuilder restTemplateBuilder) {
  this.restTemplate = restTemplateBuilder.requestFactory(new HttpComponentsClientHttpRequestFactory()).build();
}
Run Code Online (Sandbox Code Playgroud)

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

  • 如果您使用 spring-boot v2.3,那么如果您将依赖项添加到项目中,它会自动配置。 (3认同)
  • 对于 springboot 2 其 `.requestFactory(HttpComponentsClientHttpRequestFactory.class)` (2认同)

小智 7

我在java文件中添加了以下代码。这对我有用。

String url="Your API URL";
RestTemplate restTemplate = new RestTemplate();
HttpClient httpClient = HttpClientBuilder.create().build();
restTemplate.setRequestFactory(new 
HttpComponentsClientHttpRequestFactory(httpClient));    
HttpHeaders reqHeaders = new HttpHeaders();
reqHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> requestEntity = new HttpEntity<String>(requestJson, reqHeaders);
ResponseEntity<String> responseEntity=restTemplate.exchange(url, HttpMethod.PATCH, 
requestEntity, String.class);
Run Code Online (Sandbox Code Playgroud)

另外,需要在 pom.xml 文件中添加以下依赖项。

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud)


gst*_*low 5

Spring Boot 3的解决方案:

我的 Spring Boot 3 遇到了同样的问题,但两种解决方案都没有帮助我。

最终我意识到这个答案是有效的,但图书馆应该是

  Implementation("org.apache.httpcomponents.client5:httpclient5:5.2.1")
Run Code Online (Sandbox Code Playgroud)

甚至(让 Spring Boot 选择它喜欢的版本)

首选选项:

Implementation("org.apache.httpcomponents.client5:httpclient5")
Run Code Online (Sandbox Code Playgroud)

代替:

implementation("org.apache.httpcomponents:httpclient:4.4.1")
Run Code Online (Sandbox Code Playgroud)