Different semantic of produces and consumes on client and controller side
sebplorenz opened this issue · 5 comments
As stated in the documentation at https://docs.micronaut.io/latest/guide/index.html#clientAnnotation @produces (as well as @consumes) has different semantics on client and server side. From the docs:
Be aware that some annotations, such as Produces and Consumes, have different semantics between server and client side usage. For example, @produces on a controller method (server side) indicates how the method’s return value is formatted, while @produces on a client indicates how the method’s parameters are formatted when sent to the server. While this may seem a little confusing, it is logical considering the different semantics between a server producing/consuming vs a client: a server consumes an argument and returns a response to the client, whereas a client consumes an argument and sends output to a server.
Therefore, the mapping of:
responses:
"200":
description: Ok
content:
application/ld+json:
schema:
type: object
would be expected to result in:
public interface Api {
@io.micronaut.http.annotation.Get("/api")
@io.micronaut.http.annotation.Produces({ "application/ld+json" })
Object getPrivateSelfDescription();
}
public interface ApiClient {
@io.micronaut.http.annotation.Get("/api")
@io.micronaut.http.annotation.Consumes({ "application/ld+json" })
Object getPrivateSelfDescription();
}
But it is currently mapped to:
public interface Api {
@io.micronaut.http.annotation.Get("/api")
@io.micronaut.http.annotation.Produces({ "application/ld+json" })
Object getPrivateSelfDescription();
}
public interface ApiClient {
@io.micronaut.http.annotation.Get("/api")
@io.micronaut.http.annotation.Produces({ "application/ld+json" })
Object getPrivateSelfDescription();
}
The current mapping plus the default functionality of the Micronaut HTTP client leads to a client sending the get request with an automatically created Accept: application/json header. If the server is validating the Accept Header, it answers with 406, because it can only produce ld+json.
The mapper must respect the different meanings of @produces (and @consumes) on client and server side.
I cannot reproduce that problem. When I use it exactly the same way(or maybe I overlooked a difference), the client produces the expected header (and Consumes annotation).
This spec:
responses:
'200':
description: OK
content:
application/ld+json:
schema:
$ref: '#/components/schemas/EntityList'
produces the client code:
@io.micronaut.http.annotation.Post("/entities/")
@io.micronaut.http.annotation.Consumes("application/ld+json")
@io.micronaut.http.annotation.Produces({ "application/ld+json" })
io.micronaut.http.HttpResponse<Object> createEntity(@io.micronaut.http.annotation.BodyEntityVO entityVO);
The request looks as following:
DEBUG i.m.h.client.netty.DefaultHttpClient - Sending HTTP POST to http://localhost:1026/ngsi-ld/v1/entities/
TRACE i.m.h.client.netty.DefaultHttpClient - Content-Type: application/ld+json
TRACE i.m.h.client.netty.DefaultHttpClient - Accept: application/ld+json
TRACE i.m.h.client.netty.DefaultHttpClient - host: localhost:1026
TRACE i.m.h.client.netty.DefaultHttpClient - connection: close
TRACE i.m.h.client.netty.DefaultHttpClient - content-length: 499
TRACE i.m.h.client.netty.DefaultHttpClient - Request Body
TRACE i.m.h.client.netty.DefaultHttpClient - ----
Maybe you can spot the difference, the code is here: https://github.com/wistefan/contract-service
Btw. are we working on the same stuff?:) Your problems look very familiar.
Hi Stefan,
have a look at this code snippet generated from your example:
public interface EntitiesApi {
...
@io.micronaut.http.annotation.Get("/entities/")
@io.micronaut.http.annotation.Produces({ "application/ld+json" })
io.micronaut.http.HttpResponse<EntityListVO> queryEntities(
...
If the EntitiesAPI interface is implemented by a Controller, then the annotations are correct.
But the HTTP Client should look like this:
public interface EntitiesApiClient extends EntitiesApi {
...
@io.micronaut.http.annotation.Get("/entities/")
@io.micronaut.http.annotation.Consumes({ "application/ld+json" })
io.micronaut.http.HttpResponse<EntityListVO> queryEntities(
It should use @consumes. Which it does not if I generate the code from your example.
Can you check the Accept Header that your EntitiesApiClient is sending to the Server when doing a queryEntities? I assume it will not contain application/ld+json.
Your right. This looks like a problem with the GET requests. It worked, because I tried it with a POST.
Will be fixed with version 3.0.0
Fixed with 3.x