Is there any way of making Spring Boot with Jersey serve static content? I've been through a series of tutorials and code samples on integrating Swagger into a Spring Boot's application. I can make it deliver the basic swagger.json, but I can't make Swagger UI work.
I can't even make it deliver a simple hello.txt static file.
The relevant parts of my pom.xml are:
<!--Spring Boot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Jersey -->
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring3</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>${jersey.version}</version>
</dependency>
<!-- Swagger -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jersey2-jaxrs</artifactId>
<version>1.5.7</version>
</dependency>
And my code:
@Configuration
@EnableAutoConfiguration
@ComponentScan({"com.xxxx"})
public class AdminApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder(AdminApplication.class)
.run(args);
}
@Bean
public ServletRegistrationBean jerseyServlet() {
ServletRegistrationBean registration = new ServletRegistrationBean(new ServletContainer(), "/*");
registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, JerseyConfig.class.getName());
return registration;
}
}
package com.xxxxxx.admin.config;
import com.xxxxxx.admin.resource.Status;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.server.spring.scope.RequestContextFilter;
import io.swagger.jaxrs.config.BeanConfig;
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(RequestContextFilter.class);
packages("com"); // TODO needs more detailed level
register(LoggingFilter.class);
// Validation
this.property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
this.property(ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, true);
configureSwagger();
}
private void configureSwagger() {
register(io.swagger.jaxrs.listing.ApiListingResource.class);
register(io.swagger.jaxrs.listing.SwaggerSerializers.class);
BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("1.0.0");
beanConfig.setSchemes(new String[]{"http"});
beanConfig.setHost("localhost:8080");
beanConfig.setBasePath("/"); // tried other things like "/api", but doesn't change anything
beanConfig.setResourcePackage("com.xxxxxx.admin");
beanConfig.setPrettyPrint(true);
beanConfig.setScan(true);
}
}
//other imports
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@Service
@Path("/status")
@Api(value = "status", description = "Check status")
public class Status {
@GET
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation("Return status")
public Response status() {
return Response.ok("Up").build();
}
}
I also tried making Jersey run as a filter (with spring.jersey.type=filter) and changing Jersey's servlet pattern as stated in this answer, but that doesn't seem to affect anything.
@ApplicationPath("/rootPath")
public class JerseyConfig extends ResourceConfig {
I have a hello.txt file under /src/main/resources/public and Swagger UI's static files under /src/main/resources/public/swagger.
As I said, my application works fine, and GET http://localhost:8080/swagger.json shows me the plain json documentation, but both http://localhost:8080/hello.txt and http://localhost:8080/swagger/index.html return 404.
I'm using Jersey 2.8 and Spring Boot 1.3.0
I also tried changing Jersey's servlet pattern
@ApplicationPath("/rootPath") public class JerseyConfig extends ResourceConfig {
The way you are configuring your app, the @ApplicationPath doesn't matter. The reason it worked for this answer you linked to, is because the Spring Boot auto configuration sets the servlet mapping when it extracts the @ApplicationPath value from your resource config.
You are currently not using the ServletRegistrationBean provided by Spring Boot, that accomplishes this. If you goal, by using your own ServletRegistrationBean, was so that you can register your ResourceConfig, you could've have done the same simply by either
ResourceConfig with @Component to make it a Spring bean, orMake it a Spring bean in your configuration class
@Bean
public ResourceConfig config() {
return new JerseyConfig();
}
Spring Boot will then inject your ResourceConfig into the JerseyAutoConfiguration, where it will get the @ApplicationPath value (if present) on the ResourceConfig, and use it to register its own ServletRegistrationBean.
You can see the JerseyAutoConfiguration to get an idea of everything you get for free, when you let Spring Boot handle the configuration.
Of if you want to keep your current SpringRegistrationBean, just change the path you are using. You are using /*, which is mentioned to be the problem in the linked answer. So just change to /rooPath/* if that's what you want.
It looks the same as a common issue when using Spring MVC. A servlet container is required per servlet specification to implement a default server with lowest priority that is able to serve static content located outside WEB-INF folder.
Unfortunately, you are mapping Jersey servlet to "/*", meaning that every URL will be submitted to Jersey, and Jersey does not know what to do with static URLs.
So what can be (easily) done?
map the Jersey servlet to a subpath (say /api) and move all you controllers there:
ServletRegistrationBean registration =
new ServletRegistrationBean(new ServletContainer(), "/api/*");
...
beanConfig.setBasePath("/api/");
and ask GET http://localhost:8080/api/swagger.json
only map the servlet to *.json URLs:
ServletRegistrationBean registration =
new ServletRegistrationBean(new ServletContainer(), "*.json");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With