HttpMessageConverter 未将 Object 转换为 Json

HttpMessageConverter is not Converting Object to Json

我有一个使用 Spring MVC 框架的简单 Restful Web 服务。当我从浏览器 运行 时,我没有问题;它工作正常。我接下来做的是使用 RestTemplate 和 JUnit 创建一个 Restful 客户端。除了一种方法,我的所有方法都可以正常工作。这是将 object 添加到 collection 的方法。我可以看到我正在正确填充 object,但是当 object 到达服务器时(tomcat 8.x),object 的属性为空.

这是我的测试客户端。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import com.baml.rdhs.model.Issuer;

import junit.framework.TestCase;

//@FixMethodOrder(MethodSorters.NAME_ASCENDING)

/* This annotation is for builds 
 * This allows for the maven build and deploy
 * before running the integration tests
 */
@Category(IntegrationTest.class)
public class TestSpringRestClient extends TestCase {

    public static final String SERVER_URI = "http://localhost:8080/datahubservice";
    private static final Logger logger = LoggerFactory.getLogger(TestSpringRestClient.class);
    private static final String ticker = "IBM";
    private static Issuer issuer = null;

    protected void setUp() {
        issuer = new Issuer();
        issuer.setCountry("USA");
        issuer.setTicker("IBM");
        issuer.setIssuerType("corp");
        issuer.setIssuerName("International Business Machines");

    }

    @Test
    public static void testGetHome() {
        RestTemplate restTemplate = new RestTemplate();

        String results = null;
        results = restTemplate.getForObject(SERVER_URI + "/", String.class);

        assertNotNull(results);

        if (results != null) {
            logger.info("Inside testGetHome, returned: " + results);
        } else {
            logger.info("Inside testGetHome, home NOT FOUND!");
        }

    }

    @Test
    public static void testGetIssuerByTicker() {
        RestTemplate restTemplate = new RestTemplate();

        Issuer myIssuer = null;
        myIssuer = restTemplate.getForObject(SERVER_URI + "/issuer/" + ticker, Issuer.class);

        assertNotNull(myIssuer);

        if (myIssuer != null) {
            logger.info("Inside testGetIssuerByTicker, returned: " + myIssuer.toString());
        } else {
            logger.info("Inside testGetIssuerByTicker, ticker: " + ticker + ", NOT FOUND!");
        }

    }

    @SuppressWarnings("unchecked")
    @Test
    public static void testGetAllIssuers() {
        RestTemplate restTemplate = new RestTemplate();

        Map<String, Issuer> results = null;

        results = restTemplate.getForObject(SERVER_URI + "/issuers", Map.class);

        assertNotNull(results);

        if (results != null) {
            logger.info("Inside testGetAllIssuers, returned: " + results.toString());
        } else {
            logger.info("Inside testGetAllIssuers, results: NOT FOUND!");
        }

    }

    @Test
    public static void testDeleteIssuerByTicker() {
        RestTemplate restTemplate = new RestTemplate();

        Issuer myIssuer = null;
        myIssuer = restTemplate.getForObject(SERVER_URI + "/issuer/delete/" + ticker, Issuer.class);

        assertNotNull(myIssuer);

        if (myIssuer != null) {
            logger.info("Inside testDeleteIssuerByTicker, returned: " + myIssuer.toString());
        } else {
            logger.info("Inside testDeleteIssuerByTicker, ticker: " + ticker + ", NOT FOUND!");
        }
    }

    @Test
    public static void testAddIssuerByTicker() {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setMessageConverters(getMessageConverters());
        Issuer myIssuer = null;

         HttpHeaders headers = new HttpHeaders();
         headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
         HttpEntity<Issuer> entity = new HttpEntity<Issuer>(issuer, headers);

        logger.info("Adding issuer:" + issuer.toString());

        //myIssuer = restTemplate.postForObject(SERVER_URI + "/issuer/addIssuer", issuer, Issuer.class);
        ResponseEntity<Issuer> resource = restTemplate.exchange(SERVER_URI + "/issuer/addIssuer", HttpMethod.POST, entity, Issuer.class);
        myIssuer = resource.getBody();

        assertNotNull(myIssuer); 

        if (myIssuer != null) {
            logger.info("Inside testAddIssuerByTicker, returned: " + myIssuer.toString());
        } else {
            logger.info("Inside testAddIssuerByTicker, myIssuer: " + myIssuer + ", NOT FOUND!");
        }

    }

    private static List<HttpMessageConverter<?>> getMessageConverters() {
        List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
        converters.add(new MappingJackson2HttpMessageConverter());
        return converters;
    }
}

这是我的 RestController:

import java.text.DateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.baml.rdhs.model.Issuer;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;
import com.wordnik.swagger.annotations.ApiResponse;
import com.wordnik.swagger.annotations.ApiResponses;

/**
 * Handles requests for the application home page.
 */
@Api(value="issuers", description="Issuers API")
@Controller
public class RestController {

    /*
     * Place holder for service class
     */

    private static final Logger logger = LoggerFactory.getLogger(RestController.class);
    private Map<String, Issuer> issuers = new HashMap<String, Issuer>();

      public RestController() {
        // pre-initialize the list of issuers available ...

        issuers.put("ATEN", new Issuer("ATEN", "A10 Networks Inc", "corp", "USA"));
        issuers.put("AAPL", new Issuer("AAPL", "Apple Inc", "corp", "USA"));
        issuers.put("T", new Issuer("T", "AT&T", "corp", "USA"));
        issuers.put("CSCO", new Issuer("CSCO", "Cisco Systems, Inc.", "corp", "USA"));
        issuers.put("CTXS", new Issuer("CTXS", "Citrix Systems, Inc.", "corp", "USA"));
        issuers.put("GOOGL", new Issuer("GOOGL", "Google Inc", "corp", "USA"));
        issuers.put("IBM", new Issuer("IBM", "IBM", "corp", "USA"));
        issuers.put("JNPR", new Issuer("JNPR", "Juniper Networks, Inc.", "corp", "USA"));
        issuers.put("MSFT", new Issuer("MSFT", "Microsoft Corporation", "corp", "USA"));
        issuers.put("ORCL", new Issuer("ORCL", "Oracle Corporation", "corp", "USA"));
      }

      /**
       * Simply selects the home view to render by returning its name.
       */
      @RequestMapping(value = "/", method = RequestMethod.GET)
      @ApiOperation(value="Home screen", 
      notes="This is a test url whereby one can test the service is up and running.", response=String.class)
      @ApiResponses(value= {@ApiResponse(code=201, message="Hello world!", response=String.class),
                            @ApiResponse(code=500, message="Error reaching the web service")})
      public String home(Locale locale, Model model) {
        logger.info("Welcome home! The client locale is {}.", locale);

        Date date = new Date();
        DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

        String formattedDate = dateFormat.format(date);

        model.addAttribute("serverTime", formattedDate );

        return "status";
      }

      @RequestMapping(value="/issuers", method=RequestMethod.GET)
      @ResponseBody
      @ApiOperation(value="Fetch all Issuers", 
      notes="This returns a listing of all Issures currently held in the application.", response=Map.class)
      public Map<String, Issuer> getAllIssuers() {
        logger.info("Inside getAllIssuers() method...");

        try {
            String mapAsJson = new ObjectMapper().writeValueAsString(issuers);
            logger.info("Issuers returned: " + mapAsJson);
        } catch (JsonProcessingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return issuers;
      }

      @RequestMapping(value="/issuer/{ticker}", method=RequestMethod.GET)
      @ResponseBody
      @ApiOperation(value="One one Issuer record", 
      notes="This method searches the Map of all Issuers for one record based upon the ticker.", response=String.class)
      public Issuer getIssuerByTicker(@PathVariable("ticker") String ticker) {
        Issuer myIssuer = issuers.get(ticker); 

        if (myIssuer != null) {
          logger.info("Inside getIssuerByTicker, returned: " + myIssuer.toString());
        } else {
          logger.info("Inside getIssuerByTicker, ticker: " + ticker + ", NOT FOUND!");
        }
        return myIssuer; 
      }

      @RequestMapping(value="/issuer/delete/{ticker}", method=RequestMethod.GET)
      @ResponseBody
      public Issuer deleteIssuerByTicker(@PathVariable("ticker") String ticker) {
        Issuer myIssuer = issuers.remove(ticker); 

        if (myIssuer != null) {
          logger.info("Inside deleteIssuerByTicker, deleted: " + myIssuer.toString());
        } else {
          logger.info("Inside deleteIssuerByTicker, ticker: " + ticker + ", NOT FOUND!");
        }
        return myIssuer;
      }

      @RequestMapping(value="/issuer/create", method=RequestMethod.GET)
      public ModelAndView addIssuer() {

        return new ModelAndView("addIssuer", "command", new Issuer());
      }


      @RequestMapping(value="/issuer/addIssuer", method=RequestMethod.POST)
      @ResponseBody
      public Issuer addIssuer(@ModelAttribute("issuer") Issuer issuer) {

        logger.info("Issuer received from client" + issuer);

        if (issuer != null) {
          logger.info("Inside addIssuer, adding: " + issuer.toString());
        } else {
          logger.info("Inside addIssuer...");
        }

        if(issuer.getTicker()!= null)
          issuers.put(issuer.getTicker(), issuer);

        return issuer; 
      }


}

这是我的 object 我从测试客户端发送的:

public class Issuer implements Serializable {


      @JsonIgnore
      private static final long serialVersionUID = 4260711945094777831L;
      @JsonProperty
      private String ticker;
      @JsonProperty
      private String issuerName;
      @JsonProperty
      private String issuerType;
      @JsonProperty
      private String country;

      public Issuer() {
      }

      public Issuer(String ticker, String issuerName, String issuerType, String country) {
        setTicker(ticker);
        setIssuerName(issuerName);
        setIssuerType(issuerType);
        setCountry(country);
      }

      public String getTicker() {
        return ticker;
      }

      public void setTicker(String ticker) {
        this.ticker = ticker;
      }

      public String getIssuerName() {
        return issuerName;
      }

      public void setIssuerName(String issuerName) {
        this.issuerName = issuerName;
      }

      public String getIssuerType() {
        return issuerType;
      }

      public void setIssuerType(String issuerType) {
        this.issuerType = issuerType;
      }

      public String getCountry() {
        return country;
      }

      public void setCountry(String country) {
        this.country = country;
      }

      public String toString() {
        return "[" + getTicker() 
            + ", " + getIssuerName()
            + ", " + getIssuerType()
            + ", " + getCountry()
            + "]";
      }


}

这是我的 dispatcher-servlet:

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

  <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->

  <!-- Enables the Spring MVC @Controller programming model -->
  <annotation-driven />

  <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
  <resources mapping="/resources/**" location="/resources/" />

  <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
  <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <beans:property name="prefix" value="/WEB-INF/views/" />
    <beans:property name="suffix" value=".jsp" />
  </beans:bean>


   <!-- for processing requests with annotated controller methods and set Message Converters from the list of converters -->
    <beans:bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <beans:property name="messageConverters">
            <beans:list>
                <beans:ref bean="jsonMessageConverter"/>
            </beans:list>
        </beans:property>
    </beans:bean>

    <!-- To  convert JSON to Object and vice versa -->
    <beans:bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
      <beans:property name="prefixJson" value="false" />
      <beans:property name="supportedMediaTypes" value="application/json" />
    </beans:bean> 

     <context:component-scan base-package="com.baml.rdhs" />


</beans:beans> 

如有任何建议,我们将不胜感激。

拉斯

更新:

我 re-ran 测试,这是日志输出:

[main] INFO com.baml.rdhs.test.integration.TestSpringRestClient - 添加发行人:[IBM, International Business Machines, corp, USA]

[main] INFO com.baml.rdhs.test.util.LoggerInterceptor ------------- 发送请求 --------------

[主要] 信息 com.baml.rdhs.test.util.LoggerInterceptor - 与 body ----- : {"ticker":"IBM","issuerName":"International Business Machines","issuerType":"corp","country":"USA"} --------

[主要] 信息 com.baml.rdhs.test.util.LoggerInterceptor - 和 headers --- : {Accept=[application/json, application/*+json], Content-Type=[application/json], Content-Length=[99]}

[main] INFO com.baml.rdhs.test.util.LoggerInterceptor - 响应 headers 是:{Server=[Apache-Coyote/1.1], Content-Type=[application/json], Transfer-Encoding=[分块],日期=[2015 年 12 月 15 日星期二 16:45:12 GMT]}

[main] INFO com.baml.rdhs.test.integration.TestSpringRestClient - 在 testAddIssuerByTicker 内部,返回:[null, null, null, null]

你应该在做POST请求时设置content-typeheader。

 @Test
    public static void testAddIssuerByTicker() {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setMessageConverters(getMessageConverters());
        Issuer myIssuer = null;

         HttpHeaders headers = new HttpHeaders();
         headers.setContentType(MediaType.APPLICATION_JSON);
         HttpEntity<Issuer> entity = new HttpEntity<Issuer>(issuer, headers);

        logger.info("Adding issuer:" + issuer.toString());

        //myIssuer = restTemplate.postForObject(SERVER_URI + "/issuer/addIssuer", issuer, Issuer.class);
        ResponseEntity<Issuer> resource = restTemplate.exchange(SERVER_URI + "/issuer/addIssuer", HttpMethod.POST, entity, Issuer.class);
        myIssuer = resource.getBody();

        assertNotNull(myIssuer); 

        if (myIssuer != null) {
            logger.info("Inside testAddIssuerByTicker, returned: " + myIssuer.toString());
        } else {
            logger.info("Inside testAddIssuerByTicker, myIssuer: " + myIssuer + ", NOT FOUND!");
        }

    }

调试更新:

我建议调试 RestTemplate:

发送的内容
public class LoggerInterceptor implements ClientHttpRequestInterceptor {

    private static final Logger logger = LoggerFactory.getLogger(LoggerInterceptor.class);

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body,
            ClientHttpRequestExecution execution) throws IOException {
        logger.info("------------- sending request ------------- " );
        if(!request.getMethod().equals(HttpMethod.GET)){
            logger.info("with body ----- : \n " + new String(body) + " --------");
        }
        logger.info("\n and headers --- :\n "+ request.getHeaders());
        ClientHttpResponse response = execution.execute(request, body);
        logger.info("response headers are : " +  response.getHeaders());
        return response;
    }
}

并将拦截器添加到 RestTemplate:

public RestTemplate getTemplate() {
        RestTemplate template = new RestTemplate();
        ClientHttpRequestFactory requestFactory = 
                new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory());
        template.setRequestFactory(requestFactory);

        List<ClientHttpRequestInterceptor> interceptors = new LinkedList<ClientHttpRequestInterceptor>();

        LoggerInterceptor logger = new LoggerInterceptor();
        interceptors.add(logger);
        template.setInterceptors(interceptors);
        return template;
    }

并且当您手动创建时 RestTemplate 替换为 getTemplate() 并使用调试信息更新您的问题。

更新

终于看到问题出在哪里了。您的 addIssuer() 方法中不需要 @ModelAttribute。将其替换为@RequestBody,这样它将从请求 body 中读取 object。 @modelattribute 在您将模型暴露给 Web 视图时使用。