feign
feign copied to clipboard
Unable to send empty JSON object with `@Body`
Both approaches don't seem to work using feign 12.3 with okhttp and GSON encoder/decoder:
@Headers({"Accept: application/json", "Content-Type: application/json"})
public interface Api {
@RequestLine("POST /empty")
@Body("%7B%7D")
void postEncoded();
@RequestLine("POST /empty")
@Body("{}")
void postUnencoded();
}
The first sends it verbatim, which can't be parsed by the server, and the second triggers the expansion logic, creating an exception:
java.lang.IllegalArgumentException: an expression is required.
at feign.template.Expressions.create(Expressions.java:68) ~[feign-core-12.3.jar:?]
at feign.template.Template.parseFragment(Template.java:218) ~[feign-core-12.3.jar:?]
at feign.template.Template.parseTemplate(Template.java:202) ~[feign-core-12.3.jar:?]
at feign.template.Template.<init>(Template.java:61) ~[feign-core-12.3.jar:?]
at feign.template.BodyTemplate.<init>(BodyTemplate.java:54) ~[feign-core-12.3.jar:?]
at feign.template.BodyTemplate.create(BodyTemplate.java:50) ~[feign-core-12.3.jar:?]
at feign.RequestTemplate.bodyTemplate(RequestTemplate.java:909) ~[feign-core-12.3.jar:?]
at feign.Contract$Default.lambda$new$2(Contract.java:285) ~[feign-core-12.3.jar:?]
at feign.DeclarativeContract$GuardedAnnotationProcessor.process(DeclarativeContract.java:253) ~[feign-core-12.3.jar:?]
at feign.DeclarativeContract.lambda$processAnnotationOnMethod$7(DeclarativeContract.java:93) ~[feign-core-12.3.jar:?]
at java.util.ArrayList.forEach(ArrayList.java:1511) ~[?:?]
at feign.DeclarativeContract.processAnnotationOnMethod(DeclarativeContract.java:93) ~[feign-core-12.3.jar:?]
at feign.Contract$BaseContract.parseAndValidateMetadata(Contract.java:110) ~[feign-core-12.3.jar:?]
at feign.Contract$BaseContract.parseAndValidateMetadata(Contract.java:65) ~[feign-core-12.3.jar:?]
at feign.DeclarativeContract.parseAndValidateMetadata(DeclarativeContract.java:38) ~[feign-core-12.3.jar:?]
at feign.ReflectiveFeign$ParseHandlersByName.apply(ReflectiveFeign.java:134) ~[feign-core-12.3.jar:?]
at feign.ReflectiveFeign.newInstance(ReflectiveFeign.java:56) ~[feign-core-12.3.jar:?]
at feign.ReflectiveFeign.newInstance(ReflectiveFeign.java:48) ~[feign-core-12.3.jar:?]
at feign.Feign$Builder.target(Feign.java:196) ~[feign-core-12.3.jar:?]
at feign.Feign$Builder.target(Feign.java:192) ~[feign-core-12.3.jar:?]
According to the javadoc, I would have expected the first variant to work?
https://github.com/OpenFeign/feign/blob/ae16dda47ee7fcb84d4d1cc843842fb7314598bd/core/src/main/java/feign/Body.java#L33
The first method will post the pct-encoded values verbatim, as you've discovered, because the we do not decode already encoded data. The second will not work as well, since the body will be parsed like an expression.
You may need to look into providing a custom Encoder
instance that can generate the empty JSON document instead of using the @Body
annotation. Review the documentation on the @Body annotation for more hints. You may need to explicitly set a Content-Type
header.
The first sends it verbatim, which can't be parsed by the server
The request should be parsed by a server that accepts pct-encoding
, which should be every HTTP compliant server out there, so this may be something to look into.
The first method will post the pct-encoded values verbatim, as you've discovered, because the we do not decode already encoded data. The second will not work as well, since the body will be parsed like an expression.
You may need to look into providing a custom
Encoder
instance that can generate the empty JSON document instead of using the@Body
annotation.
So there is no way to simply provide a non-expression based JSON document using the @Body
annotation?
You may need to explicitly set a
Content-Type
header.
The @Headers
annotation is present at the type level:
@Headers({"Accept: application/json", "Content-Type: application/json"})
public interface Api {
The request should be parsed by a server that accepts
pct-encoding
, which should be every HTTP compliant server out there, so this may be something to look into.
At least the API i'm calling doesn't support this and I can't change that. But isn't this limited to application/x-www-form-urlencoded
?
It would be nice to have an optional parameter for the @Body
annotation to force expression parsing. e.g
@Body("%7B\"user_name\": \"{user_name}\", \"password\": \"{password}\"%7D")
void json(@Param("user_name") String user, @Param("password") String password);
@Body("%7B\"user_name\": \"denominator\", \"password\": \"secret\"%7D", forceExpression = true)
void json(@Param("user_name") String user, @Param("password") String password);