Exception passing list to service with REST API

Version is 7.0.9. Here’s a sample project.

Here’s the entity:

@MetaClass(name = "vividleviathan_TestEntity")
public class TestEntity extends BaseUuidEntity {
    private static final long serialVersionUID = 6323743611817286101L;

    @MetaProperty
    private List<String> data;

    public List<String> getData() {
        return data;
    }

    public void setData(List<String> data) {
        this.data = data;
    }
}

Here’s the service:

@Service(TestService.NAME)
public class TestServiceBean implements TestService {
    @Inject
    private Logger log;

    @Override
    public void doThing(TestEntity entity) {
        List<String> data = entity.getData();
        if (entity.getData() == null) {
            log.info(null);
        } else {
            log.error(String.join(" ", data.toArray(new String[0])));
        }
    }
}

Here’s the REST services config:

<services xmlns="http://schemas.haulmont.com/cuba/rest-services-v2.xsd">
    <service name="vividleviathan_TestService">
        <method name="doThing">
            <param name="entity"/>
        </method>
    </service>
</services>

I run this JavaScript from the browser:

(function() {
const apiRoot = "http://localhost:8080/app/rest/v2";
fetch(
  `${apiRoot}/oauth/token?grant_type=password&username=admin&password=admin`,
  {
    method: "POST",
    headers: {
      Authorization: "Basic Y2xpZW50OnNlY3JldA=="
    }
  }
)
  .then(response => response.json())
  .then(auth => {
    console.log(auth);
    return fetch(
      `${apiRoot}/services/vividleviathan_TestService/doThing`,
      {
        method: "POST",
        headers: {
          Authorization: `Bearer ${auth.access_token}`
        },
        body: JSON.stringify({
          entity: {
            data: ["asdf", "test", "junk"]
          }
        })
      }
    );
  })
  .then(response => {
    console.log(response);
    return response.json();
  })
    .then(wo => console.log(wo));
})();

I get this exception:

15:40:05.734 ERROR c.h.r.c.RestControllerExceptionHandler  - RestAPIException: Invalid parameter value, Invalid parameter value for entity
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException
        at com.google.gson.Gson.fromJson(Gson.java:939) ~[gson-2.8.5.jar:na]
        at com.google.gson.Gson.fromJson(Gson.java:892) ~[gson-2.8.5.jar:na]
        at com.google.gson.Gson.fromJson(Gson.java:841) ~[gson-2.8.5.jar:na]
        at com.google.gson.Gson.fromJson(Gson.java:813) ~[gson-2.8.5.jar:na]
        at com.haulmont.cuba.core.app.serialization.EntitySerialization.entityFromJson(EntitySerialization.java:124) ~[cuba-global-7.0.9.jar:7.0.9]
        at com.haulmont.restapi.common.RestParseUtils.toObject(RestParseUtils.java:98) ~[cuba-rest-api-7.0.9.jar:7.0.9]
        at com.haulmont.restapi.service.ServicesControllerManager._invokeServiceMethod(ServicesControllerManager.java:124) ~[cuba-rest-api-7.0.9.jar:7.0.9]
        at com.haulmont.restapi.service.ServicesControllerManager.invokeServiceMethodPost(ServicesControllerManager.java:90) ~[cuba-rest-api-7.0.9.jar:7.0.9]
        at com.haulmont.restapi.controllers.ServicesController.invokeServiceMethodPost(ServicesController.java:44) ~[cuba-rest-api-7.0.9.jar:7.0.9]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_192]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_192]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_192]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_192]
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) [spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908) [spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:660) [servlet-api.jar:na]
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882) [spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) [servlet-api.jar:na]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [catalina.jar:9.0.14]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:9.0.14]
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) [tomcat-websocket.jar:9.0.14]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:9.0.14]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:9.0.14]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at com.haulmont.restapi.auth.CubaRestLastSecurityFilter.doFilter(CubaRestLastSecurityFilter.java:90) [cuba-rest-api-7.0.9.jar:7.0.9]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at com.haulmont.restapi.auth.CubaAnonymousAuthenticationFilter.doFilter(CubaAnonymousAuthenticationFilter.java:116) [cuba-rest-api-7.0.9.jar:7.0.9]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:176) [spring-security-oauth2-2.3.5.RELEASE.jar:na]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:96) [spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:107) [spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:112) [spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at com.haulmont.restapi.sys.RestExceptionLoggingFilter.doFilter(RestExceptionLoggingFilter.java:42) [cuba-rest-api-7.0.9.jar:7.0.9]
        at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:112) [spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:73) [spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) [spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357) [spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270) [spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:9.0.14]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:9.0.14]
        at com.haulmont.cuba.web.sys.CubaHttpFilter.doFilter(CubaHttpFilter.java:103) [cuba-web-7.0.9.jar:7.0.9]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:9.0.14]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:9.0.14]
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199) [catalina.jar:9.0.14]
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [catalina.jar:9.0.14]
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) [catalina.jar:9.0.14]
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [catalina.jar:9.0.14]
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [catalina.jar:9.0.14]
        at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:668) [catalina.jar:9.0.14]
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [catalina.jar:9.0.14]
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [catalina.jar:9.0.14]
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) [tomcat-coyote.jar:9.0.14]
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-coyote.jar:9.0.14]
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834) [tomcat-coyote.jar:9.0.14]
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417) [tomcat-coyote.jar:9.0.14]
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-coyote.jar:9.0.14]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_192]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_192]
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-util.jar:9.0.14]
        at java.lang.Thread.run(Thread.java:748) [na:1.8.0_192]
Caused by: java.lang.IllegalStateException: null
        at com.google.gson.JsonArray.getAsString(JsonArray.java:226) ~[gson-2.8.5.jar:na]
        at com.haulmont.cuba.core.app.serialization.EntitySerialization$EntityDeserializer.readSimpleProperty(EntitySerialization.java:566) ~[cuba-global-7.0.9.jar:7.0.9]
        at com.haulmont.cuba.core.app.serialization.EntitySerialization$EntityDeserializer.readFields(EntitySerialization.java:526) ~[cuba-global-7.0.9.jar:7.0.9]
        at com.haulmont.cuba.core.app.serialization.EntitySerialization$EntityDeserializer.readEntity(EntitySerialization.java:484) ~[cuba-global-7.0.9.jar:7.0.9]
        at com.haulmont.cuba.core.app.serialization.EntitySerialization$EntityDeserializer.deserialize(EntitySerialization.java:385) ~[cuba-global-7.0.9.jar:7.0.9]
        at com.haulmont.cuba.core.app.serialization.EntitySerialization$EntityDeserializer.deserialize(EntitySerialization.java:375) ~[cuba-global-7.0.9.jar:7.0.9]
        at com.google.gson.internal.bind.TreeTypeAdapter.read(TreeTypeAdapter.java:69) ~[gson-2.8.5.jar:na]
        at com.google.gson.Gson.fromJson(Gson.java:927) ~[gson-2.8.5.jar:na]
        ... 89 common frames omitted

Can anyone point me at what’s going wrong here?

That was what I originally tried:

return fetch(
  `${apiRoot}/services/vividleviathan_TestService/doThing`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${auth.access_token}`
    },
    body: JSON.stringify({
      data: ["asdf", "test", "junk"]
    })
  }
);

I just get this error:

08:03:49.147 INFO  c.h.r.c.RestControllerExceptionHandler  - RestAPIException: Service method not found, vividleviathan_TestService.doThing(data)

It looks like the dev manual says that each parameter needs to be a named object in the POST body.

In case of the POST request parameters are passed in the request body. The request body must contain a JSON object, each field of this object corresponds to the service method argument.

Hi, thanks for the test project, it’s a bug in entity deserialization mechanism.

As a temporary workaround you can pass array directly as a parameter:

 body: JSON.stringify({
            data: ["asdf", "test", "junk"]
        })
public void doThing(List<String> data) {
    <service name="vividleviathan_TestService">
        <method name="doThing">
            <param name="data"/>
        </method>
    </service>
1 Like

Thanks! Any chance a fix for this will get applied retroactively to 6.10, too? That’s where I originally ran into the issue.

@emmett.miller It’s scheduled to be fixed in 6.10, please track the issue on github for further info