Swagger UI causing HTTP 406 Not Acceptable response for operations producing content types other than json

12.5k views Asked by At

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?

2

There are 2 answers

2
raspacorp On BEST ANSWER

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();
}
0
Mohit Kanwar On

I was also getting the same issue, but the solution was something different.

The problem lied in an unrelated controller, whose mapping was not defined correctly.

On starting the spring-boot application, I was able to see a log something like as follows :

([]) Mapping with class UnrelatedController

Whenever I loaded the swagger UI, a request swagger's API was being made, however response for this API was being displayed, which did not match what swagger was expecting.

Hence the error 406

The solution was to correct the mapping of the UnrelatedController, and everything worked as before. Thanks to Git History!