swagger-api / swagger-ui

Swagger UI is a collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API.
https://swagger.io
Apache License 2.0
25.96k stars 8.85k forks source link

SRVE8115W: WARNING: Cannot set status. Response already - problem on Request with array element does not generate Yaml request #10032

Open developersorli opened 1 week ago

developersorli commented 1 week ago

Q&A (please complete the following information)

Describe the bug you're encountering

We generate yaml file from code - Down to top. Here is example how to produce issue.:

Content & configuration

Steps to reproduce the behavior:

public class RequestWithArray  implements Serializable {
    protected Integer id;
..
        protected List<Address> address; // problem throw exception SRVE8115W: WARNING: Cannot set status. Response already committed.
..
//getters and setters
...
}
public class Address implements Serializable {
    protected String street;
        protected List<Name> name; // problem throw exception SRVE8115W: WARNING: Cannot set status. Response already 
..
..
//getters and setters
...
}
@SecurityRequirement(name = "basicAuth")
@Tag(name="service", description="service API")
@Path("/service")
public class Service {

    @Context
    private UriInfo uriInfo;

    @Context
    private ServletContext servletContext;

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    @Path("/createService")
    @Operation(security = @SecurityRequirement(name = "basicAuth"), summary = "Creates ....",
    responses = { @ApiResponse(responseCode = "200", description = "OK_MSG",
                    content = @Content()),
                    @ApiResponse(responseCode = "404", description = "Not found"),
                    @ApiResponse(responseCode = "401", description = "Unauthorized"),
                    @ApiResponse(responseCode = "400", description = "Bad request")})
    public Response createService(RequestWithArray request, final @QueryParam("dateCreated") String dateCreated, final @QueryParam("id") Integer id, final @QueryParam("number") String number, @Parameter(in = ParameterIn.HEADER,
            name =  "authorization",
            example = "Basic token-id",
            required = true) @HeaderParam("authorization") String authToken,  @HeaderParam("userId") String userId, @Context HttpHeaders headers) {

...
}
public class ApiServlet extends OpenApiServlet {

    @Context
    private UriInfo uri;

    // TODO move to own servlet non jaxrs project and reference from there
    // TODO cleanup and errors
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        String acceptHeader = req.getHeader(ACCEPT_HEADER);

        if (!StringUtils.isBlank(acceptHeader) && acceptHeader.toLowerCase().contains("text/html")) {

            if (req.getRequestURI().toString().toLowerCase().endsWith("html")) {
                String url = ServletUriComponentsBuilder.fromRequestUri(req).replacePath(null).build().toUriString() + req.getRequestURI().replaceFirst("([^/]+)?$", "");

                String val = "<!DOCTYPE html>\r\n" + "<html lang=\"en\">\r\n" + "  <head>\r\n" + "    <meta charset=\"utf-8\" />\r\n"
                        + "    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\r\n" + "    <meta\r\n" + "      name=\"description\"\r\n" + "      content=\"SwaggerUI\"\r\n"
                        + "    />\r\n" + "    <title>SwaggerUI</title>\r\n" + "    <link rel=\"stylesheet\" href=\"https://unpkg.com/swagger-ui-dist@5.10.5/swagger-ui.css\" />\r\n" + "  </head>\r\n"
                        + "  <body>\r\n" + "  <div id=\"swagger-ui\"></div>\r\n" + "  <script src=\"https://unpkg.com/swagger-ui-dist@5.10.5/swagger-ui-bundle.js\" crossorigin></script>\r\n"
                        + "  <script src=\"https://unpkg.com/swagger-ui-dist@5.10.5/swagger-ui-standalone-preset.js\" crossorigin></script>\r\n" + "  <script>\r\n" + "    window.onload = () => {\r\n"
                        + "      window.ui = SwaggerUIBundle({ " + "      url: '" + url + "openapi.json',\r\n" + "        dom_id: '#swagger-ui',\r\n" + "        validatorUrl : null,\r\n" + "        presets: [\r\n"
                        + "          SwaggerUIBundle.presets.apis,\r\n" + "          SwaggerUIStandalonePreset\r\n" + "        ],\r\n" + "        layout: \"StandaloneLayout\",\r\n" + "      });\r\n"
                        + "    };\r\n" + "  </script>\r\n" + "  </body>\r\n" + "</html>";
                try (PrintWriter pw = resp.getWriter()) {
                    pw.write(val);
                }

                resp.setStatus(200);
                return;
            }
        }

        String ctxId = getContextIdFromServletConfig(getServletConfig());
        OpenApiContext ctx = OpenApiContextLocator.getInstance().getOpenApiContext(ctxId);
        OpenAPI oas = ctx.read();
        Info info = new Info().title("Swagger app").description("This is a server  documentation.").termsOfService("http://swagger.io")
                // .contact(new Contact().email("@swagger.io"))
                .version("1.0");

        oas.info(info);

        List<Server> serverList = new ArrayList<Server>();
        Server s = new Server();
        s.url("http://localhost:9080/service");
        s.description("localhost");
        serverList.add(s);

        /*
         * SecurityRequirement securityItem = new SecurityRequirement(); securityItem. oas.addSecurityItem(securityItem);
         */

        if (oas != null && ctx.getOpenApiConfiguration() != null) {
            if (ctx.getOpenApiConfiguration().getFilterClass() != null) {
                try {
                    OpenAPISpecFilter filterImpl = (OpenAPISpecFilter) Class.forName(ctx.getOpenApiConfiguration().getFilterClass()).newInstance();
                    SpecFilter f = new SpecFilter();
                    oas = f.filter(oas, filterImpl, ServletUtils.getQueryParams(req.getParameterMap()), ServletUtils.getCookies(req.getCookies()), ServletUtils.getHeaders(req));
                } catch (Exception e) {
                    LOGGER.error("failed to load filter", e);
                }
            }
        }

        String type = "json";

        // String acceptHeader = req.getHeader(ACCEPT_HEADER);
        /*
         * if (!StringUtils.isBlank(acceptHeader) && acceptHeader.toLowerCase().contains("text/html")) { type = "html"; } else
         */if (!StringUtils.isBlank(acceptHeader) && acceptHeader.toLowerCase().contains(APPLICATION_YAML)) {
            type = "yaml";
        } else {
            // check URL:
            if (req.getRequestURL().toString().toLowerCase().endsWith("yaml")) {
                type = "yaml";
            }
        }

        boolean pretty = ctx.getOpenApiConfiguration() != null && Boolean.TRUE.equals(ctx.getOpenApiConfiguration().isPrettyPrint());

        resp.setStatus(200);

        if (type.equalsIgnoreCase("yaml")) {
            resp.setContentType(APPLICATION_YAML);
            try (PrintWriter pw = resp.getWriter()) {
                pw.write(pretty ? ctx.getOutputYamlMapper().writer(new DefaultPrettyPrinter()).writeValueAsString(oas) : ctx.getOutputYamlMapper().writeValueAsString(oas));
            }
        } else if (type.equalsIgnoreCase("json")) {
            resp.setContentType(APPLICATION_JSON);
            try (PrintWriter pw = resp.getWriter()) {
                pw.write(pretty ? ctx.getOutputJsonMapper().writer(new DefaultPrettyPrinter()).writeValueAsString(oas) : ctx.getOutputJsonMapper().writeValueAsString(oas));
            }
        }

    }

}

To reproduce...

  1. Go to web page swagger ui
  2. Click on click service. It Try generate request body, but web page freeze.
  3. Si error: It throws warning: SRVE8115W: WARNING: Cannot set status. Response already

Expected behavior

Expect that will generate request example

Screenshots

Additional context or thoughts

1.If you remove in class RequestWithArray line:

protected List<Address> address; 
//and getters and setters
...

and in class Address

protected List<Name> name;
//and getters and setters
...
  1. Then works, but missing functionality