Swagger UI causing HTTP 406 Not Acceptable respons

2019-06-21 13:37发布

问题:

I have a REST API published with Jersey and documented with Swagger, I also have a Swagger UI installation consuming that API.

Almost all my operations produce application/json and work as expected, except for one GET operation that produces: 'text/plain;charset=utf-8'

When I try to call the service from the Swagger UI, the server logs a javax.ws.rs.NotAcceptableException and returns a 406 response. If I call the same service from a REST client it works as expected.

@GET
@Path("/text")
@Produces(MediaType.TEXT_PLAIN + ";charset=utf-8")
@ApiOperation(value= "Return text")
public Response getText(@QueryParam("user") String user) {
    return Response.ok(textService.getTextForUser(user)).build();
}

If I change to @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8") then it works fine, but I don't want to set a wrong content type.

The problem seems to be that Swagger UI is wrongly setting the Accept headers to application/json as can be seen by observing the request:

GET /supertext/text?user=1
...
Accept: application/json

When using the rest client the Accept header are:

GET /supertext/text?user=1
...
Accept: */*

Why is Swagger UI not setting the Accept headers properly?

Can this be configured?

回答1:

It seems that swagger ui sets the accept header to application/json when it finds that the @Produces annotation contains a single value, otherwise it renders a drop-down list in the ui to choose from the available content types.

In swagger-ui.js:

opts.responseContentType = $("div select[name=responseContentType]", $(this.el)).val();

When the drop-down list doesn't exist, the property becomes undefined.

Later in the code, the response content type is set to application/json if the property is null or undefined:

In swagger.js:

if (this.type === "POST" || this.type === "GET" || this.type === "PATCH") {
    if (this.opts.responseContentType) {
      responseContentType = this.opts.responseContentType;
    } else {
      responseContentType = "application/json";
    }
  }

So my solution was to modify the code in swagger-ui.js to make sure that the correct content-type was set, by exploring the produces array and choosing the first element as the response content type:

In swagger-ui.js replace the line:

opts.responseContentType = $("div select[name=responseContentType]", $(this.el)).val();

With:

if($("div select[name=responseContentType]", $(this.el)).val() === undefined) { 
    opts.responseContentType = opts.parent.model.produces[0];
}

else {
    opts.responseContentType = $("div select[name=responseContentType]", $(this.el)).val();
}