Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem with different Request and Response Object at Json Transform [INT-3647] #7556

Closed
spring-operator opened this issue Feb 17, 2015 · 8 comments
Assignees
Milestone

Comments

@spring-operator
Copy link
Contributor

Marcel Alburg opened INT-3647 and commented

Hello,

i use Spring Integration Http with an outbound Gateway and try to connect to an other REST Service.

For the Request and the Response, i use different POJO's.

class Request {}
class Response {}

For my gateway, i use an Interface like that.

interface ExternalRest {
  public Response callMethod(Request);
}

and my Integration Config looks like

<int:gateway id="Testgateway"
		service-interface="ExternalRest"
		default-request-channel="RestObject">
</int:gateway>

<int-http:outbound-gateway
		request-channel="RestJson" 
                url="xxx"
		http-method="POST" 
                charset="UTF-8" 
                reply-timeout="5000"
		expected-response-type="java.lang.String" 
                reply-channel="ReplyJson">
</int-http:outbound-gateway>

<int:object-to-json-transformer
		input-channel="RestObject" 
                output-channel="RestJson" 
                content-type="application/json" />

<int:json-to-object-transformer
		input-channel="ReplyJson" />

and my model is calling this service.

@Autowired
ExternalRest restService;

Request request = new Request();

Response response = restService.callMethod(request);

The problem is, that the object-to-json-transformer add the header "json__TypeId__=class Request" and the json-to-object-transformer try's to create the wrong object with the error

Unrecognized field "name" (class Request)

For this case, it's easy to change the transformer to:

<int:json-to-object-transformer
		input-channel="ReplyJson" type="Response"/>

and it's works, but i've a lot of calls with different ResponseObjects.

It is possible to make the Transformer more flexible or how can i hook me in the ConversionService for making an fallback converter.

Because in the transform, i don't get the correct return type from the gateway. The ConversionService has it in the convert method.

Thanks for you help
Marcel


No further details from INT-3647

@spring-operator
Copy link
Contributor Author

Gary Russell commented

Instead of using transformers, you should be able to use the inbuilt converters in the http gateway. In the <gateway/> add a <header/> to set the content-type to application/json and just pass your request payload to the http gateway; set the expected-response-type to your reply object.

@spring-operator
Copy link
Contributor Author

Marcel Alburg commented

HEllo,

thanks for the quick response. For my real case, the external rest API has more methods with different response objects - because of that, it's not so nice to put the expected-response-type to one response object.

p.e:

my interface can looks like this:

interface ExternalRest {
  public Response callMethod(Request);
  public Response1 callMethod2(String data);
  public Response2 helloName(String Name, Integer age);
...

@spring-operator
Copy link
Contributor Author

Marcel Alburg commented

Currently, my request looks like this.

The Request:

GenericMessage [payload={"userid":"fff8ca93-d20d-4733-9bd9-ed37169dd173", headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel@7173914b, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel@7173914b, json__TypeId__=class Request,  id=642a65ed-74d5-40f9-af5c-a0df0d75fbaf, contentType=application/json, Content-Type=application/json, timestamp=1424217426362}]

And the Response:

GenericMessage [payload={"name":"huhu"}, headers={Transfer-Encoding=chunked, replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel@7173914b, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel@7173914b, Server=Jetty, json__TypeId__=class Request, id=8707ff15-25cb-3bee-4cf6-e6df49364ea0, contentType=application/json;charset=UTF-8, http_statusCode=200, Date=1424217426000, Content-Type=application/json, timestamp=1424217426385}]

But the Response isn't a "json__TypeId__=class Request" this should be an "json__TypeId__=class Response"

@spring-operator
Copy link
Contributor Author

Gary Russell commented

The transformer needs a hint as to how to convert the object.

<int:json-to-object-transformer
		input-channel="ReplyJson" type="Response"/>

An explicit type here overrides the header type.

Even if you filter out the leftover header from the outbound conversion, it will still fail because, you can only specify one type on the transformer.

If you can somehow determine the expected response type based on the request, you could set it up with a header enricher.

Another alternative is to put your response types in a class hierarchy and set the expected type to the superclass. Jackson should be able to figure out which class to instantiate.

Alternatively, you can use a custom JsonObjectMapper in the transformer.

@spring-operator
Copy link
Contributor Author

Marcel Alburg commented

Ok, thanks. I need it in a more dynamicly way, because of that, i use a "not so clear solution" with an header reewrite. But in my oppinion its not so nice :(

<int-http:outbound-gateway
		request-channel="RestJson" 
                url="xxx"
		http-method="POST" 
                charset="UTF-8" 
                reply-timeout="5000"
		expected-response-type="java.lang.String" 
                reply-channel="ReplyJson">
    <int:default-header name="json__ReturnType"
        expression="#gatewayMethod.getGenericReturnType()" />
</int-http:outbound-gateway>

<int:chain input-channel="ReplyJson">
    <int:header-enricher default-overwrite="true">
        <int:header name="json__TypeId__" expression="headers[json__ReturnType]"></int:header>
    </int:header-enricher>
    <int:json-to-object-transformer/>
</int:chain>

@spring-operator
Copy link
Contributor Author

Artem Bilan commented

BTW, the <int-http:outbound-gateway> supports this one:

<xsd:attribute name="expected-response-type-expression" type="xsd:string">
	<xsd:annotation>
        	<xsd:documentation>
	SpEL expression to determine the type for the expected response to which the response body should be converted
	The returned value of the expression could be an instance of java.lang.Class or
	java.lang.String representing a fully qualified class name.
	This attribute cannot be provided if expected-response-type has a value
        	</xsd:documentation>
      </xsd:annotation>
</xsd:attribute>

Where the real Class for MappingJackson2HttpMessageConverter can be determined against requestMessage.
So, you always can place a desired response type to the headers and extract it at runtime for each request.

@spring-operator
Copy link
Contributor Author

Marcel Alburg commented

Hello,

ok, this is much better than my solution before. i still have to create an special header for that but i don't need an extra channel and chain for that.

My Final XML is pretty nice :)

<int:gateway id="Testgateway"
		service-interface="ExternalRest"
		default-request-channel="RestRequest">

    <int:default-header name="json__ReturnType"
		expression="#gatewayMethod.getGenericReturnType()" />
</int:gateway>

<int:chain input-channel="RestRequest">
    <int:object-to-json-transformer content-type="application/json" />
    <int-http:outbound-gateway
                url="xxx"
		http-method="POST" 
                charset="UTF-8" 
                reply-timeout="5000"
		expected-response-type-expression="headers[json__ReturnType]">
    </int-http:outbound-gateway>
</int:chain>

Thanks for give me the information. It's a good solution for using external REST Services and map the response to an own pojo.

Marcel

P.s.: I recognize that i make an mistake in my last comment. this is not the "int-http:outbound-gateway" this shoud be the "int:gateway".

@spring-operator
Copy link
Contributor Author

Artem Bilan commented

Marcel, we are glad to hear that we helped, but be sure for the future do not abuse JIRA with such a support question.
The StackOverflow is the best place for such a work. We will ask you for JIRA ticket from there if we really find that it is necessary

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants