春季启动RestTemplate发布给出400错误

时间:2018-12-19 12:28:19

标签: java spring rest spring-mvc spring-boot

我在Eclipse Photon上用Java 8编写了一个简单的webService,使用RestTemplate发布(使用postForObject)包裹对象列表(称为PatentDetails)的对象(称为patentListWrapper)。我从Java客户端(称为MainWsClient)发帖,然后在服务器端的patentDetails中设置一个值,并在客户端读回patentListWrapper对象。当服务器端(程序SpringWebServiceHello)使用旧的Spring MVC 4技术,并且仅在此之后有一个jar文件(Spring-web.5.07.RELEASE.jar)时,它工作良好-serverSideExample,即web.xml和rest-servlet .xml文件控制访问点。然后,我使用带有Spring 5.07 jar的SpringBoot 2.03和Maven编写了另一个服务器端程序(PndGuidRequestWs),它具有相同的@RequestMapping方法,但没有web.xml文件,并且在application.properties文件中定义了访问点:

server.port=8082
server.servlet.path=/
#spring.mvc.servlet.path=/
#server.servlet.contextPath=/

当我使用此客户端-ARC调用此新服务器程序时,它也可以正常工作 但是当我使用相同的Java客户端和完全相同的请求(显然接受不同的url)调用它时。我收到400错误:

2018-12-18 16:56:53,861 [main] INFO  - Running MainWsClient with name = DS fileType = post3
2018-12-18 16:56:54,101 [main] DEBUG - Created POST request for "http://localhost:8082/guidRequest/xmlList"
2018-12-18 16:56:54,145 [main] DEBUG - Setting request Accept header to [application/xml, text/xml, application/json, application/*+xml, application/*+json]
2018-12-18 16:56:54,152 [main] DEBUG - Writing [com.springservice.client.PatentListWrapper@4ba2ca36] using [org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter@3444d69d]
2018-12-18 16:56:54,384 [main] DEBUG - POST request for "http://localhost:8082/guidRequest/xmlList" resulted in 400 (null); invoking error handler
2018-12-18 16:56:54,387 [main] ERROR - DS1B org.springframework.web.client.HttpClientErrorException: 400 null

不起作用的PndGuidRequestWs,服务器端具有:

@RestController
public class PndController {

@RequestMapping(value = "/guidRequest/xmlList", method = RequestMethod.POST, produces = { "application/xml" } )
public PatentListWrapper guidSearchList(@RequestBody  PatentListWrapper patentListWrapper) {

    for (PatentDetails pd : patentListWrapper.getPatentList())
    {
        pd.setGuid("guidSetOnServer3");
    }

    return patentListWrapper;
  }

}

工作的(SpringWebServiceHello)服务器端是相同的,除了:

value = "/service/greeting/xml/post2"

Java客户端具有:

public void runCode(String name , String fileType)
{

 String url;

 if (fileType.equalsIgnoreCase("post2")) {
        url = "http://localhost:8080/SpringWebServiceHello/service/greeting/xml/post2";
        // This method is identicle to postToPndGuidRequestWs() but this method works fine.
        postToSpringWebServiceHello(url);
    }else if (fileType.equalsIgnoreCase("post3")) {
        url = "http://localhost:8082/guidRequest/xmlList";      
        // This method gives 404 error          
        postToPndGuidRequestWs(url);
    }   
}

private void postToPndGuidRequestWs(String url) 
{

    PatentListWrapper patentListWrapper = new PatentListWrapper();
    PatentDetails pd = new PatentDetails("CN","108552082","A","00000000",12345,"guidIn");

    List<PatentDetails> patentList = new ArrayList<PatentDetails>();
    patentList.add(pd);
    patentListWrapper.setPatentList(patentList);

    RestTemplate restTemplate = new RestTemplate();

    /* HttpHeaders headers = new HttpHeaders();
    headers.add("header_name", "header_value");
    headers.setContentType(MediaType.APPLICATION_XML);
    HttpEntity<PatentListWrapper> request = new HttpEntity<PatentListWrapper>(patentListWrapper, headers); */

    /*List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
    Jaxb2RootElementHttpMessageConverter jaxbMessageConverter = new Jaxb2RootElementHttpMessageConverter();
    List<MediaType> mediaTypes = new ArrayList<MediaType>();
    mediaTypes.add(MediaType.APPLICATION_XML);
    jaxbMessageConverter.setSupportedMediaTypes(mediaTypes);
    messageConverters.add(jaxbMessageConverter);
    restTemplate.setMessageConverters(messageConverters);*/

    /* headers = new HttpHeaders();
    headers.setAccept(Collections.singletonList(MediaType.APPLICATION_XML));
    HttpEntity<String> entity = new HttpEntity<>("parameters", headers);*/


    try {
        patentListWrapper = restTemplate.postForObject(
                url,
                patentListWrapper,
                PatentListWrapper.class);


        logger.debug("DS1A employee obj returned. guid = " +  patentListWrapper.getPatentList().get(0).getGuid());
    }catch(Exception e) {
        logger.error("DS1B " + e);      
    }   
}

}

ie fileType =“ post2”调用SpringWebServiceHello,fileType =“ post3”调用PndGuidRequestWs。如您所见,我已经尝试了几种注释掉的解决方案,但是没有任何效果。由于这两个服务器端程序之间的唯一真正区别是,没有人可以使用Spring Boot,而不能正常工作,则问题一定出在SpringBoot设置中,即目录结构,application.properties或pom.xml。我的pom.xml具有:

<?xml version="1.0" encoding="UTF-8"?>

http://maven.apache.org/xsd/maven-4.0.0.xsd“>     4.0.0

<groupId>com.clarivate</groupId>
<artifactId>pndguidrequestws</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>

<name>pndGuidRequestWs</name>
<description>Guid request webService</description>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.3.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <start-class>com.clarivate.pndguidrequestws.PndGuidRequestWsApplication</start-class>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>com.oracle</groupId>
        <artifactId>ojdbc6</artifactId>
        <version>11.2.0.1.0</version> 
      <!--    <scope>provided</scope> --> <!-- DS insert for unix -->
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
        <exclusions>
            <exclusion>
                <groupId>org.apache.tomcat</groupId>
                <artifactId>tomcat-jdbc</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <!-- Implementing XML Representation for Spring Boot Services -->
    <dependency>
      <groupId>com.fasterxml.jackson.dataformat</groupId>
      <artifactId>jackson-dataformat-xml</artifactId>
    </dependency>

    <!-- httpcomponents jars are Required by PndGuidGenerator -->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpcore</artifactId>
    </dependency>

    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
    </dependency>
</dependencies>


<build>
    <finalName>PndGuidRequestWs</finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId> 
            <configuration>
                  <executable>true</executable>
            </configuration> 
        </plugin>
    </plugins>      
</build>
</project>

PatentListWrapper类为:

package com.clarivate.pndguidrequestws.model;

import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class PatentListWrapper {

private List<PatentDetails> patentList;

public PatentListWrapper() {}

public List<PatentDetails> getPatentList() {
    return patentList;
}

public void setPatentList(List<PatentDetails> patentList) {
    this.patentList = patentList;
}   

}

最欢迎您提出任何建议。

编辑: 为了简化对象,我创建了PatentListWrapper2仅使用一个字符串成员:

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class PatentListWrapper2 {

private String name;

public  PatentListWrapper2() {}

public  PatentListWrapper2(String name) {
    this.name = name;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

}

我可以再次使用ARC客户端成功发送此xml:

<patentListWrapper2>
   <name>DSDS</name>
</patentListWrapper2>

with contentType =“ application / xml” 但是当我尝试从Java发送PatentListWrapper2时,出现了编组错误:

 2018-12-20 09:17:13,931 [main] INFO  - Running MainWsClient with name = DS fileType = post4
2018-12-20 09:17:14,166 [main] DEBUG - Created POST request for "http://localhost:8082/guidRequest/xmlList2"
2018-12-20 09:17:14,200 [main] DEBUG - Setting request Accept header to [application/xml, text/xml, application/json, application/*+xml, application/*+json]
2018-12-20 09:17:14,206 [main] DEBUG - Writing [com.springservice.client.PatentListWrapper2@517cd4b] using [org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter@6cc7b4de]
2018-12-20 09:17:14,246 [main] DEBUG - POST request for "http://localhost:8082/guidRequest/xmlList2" resulted in 200 (null)
2018-12-20 09:17:14,248 [main] DEBUG - Reading [com.springservice.client.PatentListWrapper2] as "application/xml;charset=UTF-8" using [org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter@6cc7b4de]
2018-12-20 09:17:14,255 [main] ERROR - DS2B org.springframework.web.client.RestClientException: Error while extracting response for type [class com.springservice.client.PatentListWrapper2] and content type [application/xml;charset=UTF-8]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: Could not unmarshal to [class com.springservice.client.PatentListWrapper2]: unexpected element (uri:"", local:"PatentListWrapper2"). Expected elements are <{}patentListWrapper2>; nested exception is javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"PatentListWrapper2"). Expected elements are <{}patentListWrapper2>

EDIT2我在Eclipse Tomcat上运行了pndGuidRequestWs,而不是-Run As-> Spring Boot App。服务器日志如下:

2018-12-20 11:15:45.655  WARN 236 --- [nio-8080-exec-6] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.clarivate.pndguidrequestws.model.PatentDetails` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('CN'); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.clarivate.pndguidrequestws.model.PatentDetails` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('CN') at [Source: (PushbackInputStream); line: 1, column: 98] (through reference chain: com.clarivate.pndguidrequestws.model.PatentListWrapper["patentList"]->java.util.ArrayList[0])         

2 个答案:

答案 0 :(得分:1)

您可以使用:

进行测试吗?
try {
        HttpHeaders headers = new HttpHeaders();
        //headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
        //headers.setContentType((MediaType.APPLICATION_JSON));
        // I comment the code abouve because you did not specify a consumes whitch  defines the media types that the methods of 
        //a resource class or MessageBodyReader can accept. If not specified, a container will assume that any media type is acceptable.
        HttpEntity<PatentListWrapper> request = new HttpEntity<>(patentListWrapper, headers);
        PatentListWrapper patentListWrapperResult =  = restTemplate.exchange(url, HttpMethod.POST, request,PatentListWrapper.class);


        logger.debug("DS1A employee obj returned. guid = " +  patentListWrapper.getPatentList().get(0).getGuid());
    }catch(Exception e) {
        logger.error("DS1B " + e);      
    } 

答案 1 :(得分:0)

PatentListWrapper是一个复杂的对象,而不是xml片段,因此答案是删除对xml的所有引用,即

  1. 从PatentListWrapper中删除@XmlRootElement(name =“ PatentListWrapper”)。
  2. 将jackson-*。jar添加到类路径中以进行消息转换
  3. 从以下位置更改服务器端@RequestMapping:

    @RequestMapping(value = "/xmlList", method = RequestMethod.POST , consumes = { "application/xml" }, produces = { "application/xml" }) 
    

@RequestMapping(value = "/xmlList", method = RequestMethod.POST )

这意味着ARC客户端现在即使在发送xml时也返回JSON(因为它是默认的返回类型),但这并不重要,因为它只是一个测试工具。

因此,在Spring 2中使用RestTemplate发布对象时,不需要在客户端上进行contentType设置或其他messageConverters,只需:

RestTemplate restTemplate = new RestTemplate();
MyObject myObjectReturn = restTemplate.postForObject(url,myObject,MyObject.class);

并在服务器端:

@RestController
@RequestMapping(value = "/endPoint", method = RequestMethod.POST)
public MyObject anyMethodName(@RequestBody  MyObject myObject) {
     //Do stuff to myObject
     return myObject;
}