0

i created a small json example... all worked quite fine, i'am just not able to send complex multiparameter to a service-method. With just one parameter it works... at the moment i'm not sure that my json call is wrong or something in my source/config is wrong.

First of all my lib-Dependencies:

  • org.springframework.spring-webmvc 4.0.6.RELEASE
  • org.springframework.spring-context 4.0.0.RELEASE
  • com.fasterxml.jackson.core.jackson-databind 2.2.3

Here is my spring-config:

<?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 -->

     <!-- Configure to plugin JSON as request and response in method handler -->
    <annotation-driven>
        <message-converters>

        <beans:bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <beans:property name="objectMapper">
                <beans:bean class="com.test.webservice.EnvironmentObjectMapper" />
            </beans:property>
        </beans:bean>

        </message-converters>
    </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>

    <!-- only components from this package can be wired by spring --> 
    <context:component-scan base-package="com.test.*" />

</beans:beans>

Here is the used ObjectMapper:

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

public class EnvironmentObjectMapper extends ObjectMapper{

    /**
     * 
     */
    private static final long serialVersionUID = -4831947927759735004L;

    public EnvironmentObjectMapper() {
        this.setVisibilityChecker(
                getSerializationConfig().
                getDefaultVisibilityChecker().
                withFieldVisibility(JsonAutoDetect.Visibility.ANY).
                withGetterVisibility(JsonAutoDetect.Visibility.NONE).
                withSetterVisibility(JsonAutoDetect.Visibility.NONE).
                withCreatorVisibility(JsonAutoDetect.Visibility.NONE).
                withIsGetterVisibility(JsonAutoDetect.Visibility.NONE));
        this.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
    }

}

Here is a Simple-Test Code:

@RequestMapping(value = "/rest/test1", method = RequestMethod.POST)
    public void test(@RequestBody String name, @RequestBody String name2){
        System.out.println("Test - name: "+ name + " name2: " + name2);
    }

To call this method i use the Firefox RESTClient. Header is set to Content-Type:application/json

Body is set to: {"name":"karl", "name2:"fritz"}

I get this response:

    Status Code: 400 Bad Request
    Cache-Control: must-revalidate,no-cache,no-store
    Content-Length: 1379
    Content-Type: text/html;charset=ISO-8859-1
    Server: Jetty(7.2.0.v20101020)

and at the server this:

15:32:30.588 [qtp1879112077-17] DEBUG o.s.web.servlet.DispatcherServlet - DispatcherServlet with name 'appServlet' processing POST request for [/rest/test1]
15:32:30.588 [qtp1879112077-17] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Looking up handler method for path /rest/test1
15:32:30.588 [qtp1879112077-17] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Returning handler method [public void com.test.webservice.JustAServiceController.test(java.lang.String,java.lang.String)]
15:32:30.588 [qtp1879112077-17] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'justAServiceController'
15:32:30.588 [qtp1879112077-17] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Reading [class java.lang.String] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@3d467064]
15:32:30.588 [qtp1879112077-17] DEBUG o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver - Resolving exception from handler [public void com.test.webservice.JustAServiceController.test(java.lang.String,java.lang.String)]: org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: org.eclipse.jetty.server.HttpInput@309efc1f; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: org.eclipse.jetty.server.HttpInput@309efc1f; line: 1, column: 1]
15:32:30.588 [qtp1879112077-17] DEBUG o.s.w.s.m.a.ResponseStatusExceptionResolver - Resolving exception from handler [public void com.test.webservice.JustAServiceController.test(java.lang.String,java.lang.String)]: org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: org.eclipse.jetty.server.HttpInput@309efc1f; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: org.eclipse.jetty.server.HttpInput@309efc1f; line: 1, column: 1]
15:32:30.588 [qtp1879112077-17] DEBUG o.s.w.s.m.s.DefaultHandlerExceptionResolver - Resolving exception from handler [public void com.test.webservice.JustAServiceController.test(java.lang.String,java.lang.String)]: org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: org.eclipse.jetty.server.HttpInput@309efc1f; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: org.eclipse.jetty.server.HttpInput@309efc1f; line: 1, column: 1]
15:32:30.588 [qtp1879112077-17] DEBUG o.s.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'appServlet': assuming HandlerAdapter completed request handling
15:32:30.588 [qtp1879112077-17] DEBUG o.s.web.servlet.DispatcherServlet - Successfully completed request

I have tried many different variants to set the parameters into the request body. But nothing worked. Anybody an idea what i did wrong? Thank you very much in advance.

user3227576
  • 484
  • 6
  • 21

1 Answers1

0

You cannot have multiple @RequestBody annotations. @RequestBody annotated parameter is expected to hold the entire body of the request and bind to one object.

You should use different approach such as

  • Introduce a wrapper object that encapsulates your strings, and change your signature. e.g

Wrapper Object

class MyWrapper{
    String name, name2;
    //ToDo: Create constructors

      String getname(){
         return name; 
      }
      String getName2(){
         return name2;
      } 
    //ToDo: create Setters
}

Your test code

   @RequestMapping(value = "/rest/test1", method = RequestMethod.POST)
    public void test(@RequestBody MyWrapper wrapper){
            System.out.println("Test - name: "+ wrapper.getName() + " name2: " + wrapper.getName2());
     }

Other options would be to pass the data with the URI path, or define custom annotations (which is more complex) see Passing multiple variables in @RequestBody to a Spring MVC controller using Ajax

Community
  • 1
  • 1
Simon Savai
  • 182
  • 1
  • 12
  • Ahhh... Thank you very much. So i have to write wrappers or my own annotation+argumentResolver which handles the wrapping for me... the second solution seems to be better... because otherwise i have to create more and more wrapper (in worst case for every method a wrapper). For the client it doesn't make any difference, right? – user3227576 Dec 02 '14 at 17:41
  • You are right, it doesn't make a difference. The wrapper would be appropriate if the data you are posting represents entities being managed on the server side. e.g where you are posting data to be stored in a database,then the server might encapsulate a database record as an object, which you use as a wrapper – Simon Savai Dec 02 '14 at 17:58
  • how about you accepting or upvoting this answer if it is useful? that could help another person having a similar issue... – Simon Savai Dec 02 '14 at 19:24
  • I'd love to upvote your answer, but i don't have enough reputation to do this. I already tried this after your first post. I also don't know how to accept the answer, i already tried to do this... – user3227576 Dec 02 '14 at 19:50