How to add Swagger related static files to Spring Boot + Jersey app? - java

I am trying to add Swagger support to my REST API but I am confused how to add Swagger related static content (HTML, JS) files to my Spring Boot application.
I use the following dependencies:
spring-boot-starter-parent:2.0.1.RELEASE
spring-boot-starter-jersey:2.0.1.RELEASE
swagger-jersey2-jaxrs:1.5.18
This is my swagger configuration:
#Configuration
public class SwaggerConfig {
#Bean
public BeanConfig swaggerConfiguration() {
final BeanConfig beanConfig = new BeanConfig();
beanConfig.setResourcePackage("a.b.c");
beanConfig.setScan(true);
beanConfig.setPrettyPrint(true);
return beanConfig;
}
}
And the jersey configuration:
#Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(ImageResource.class);
register(io.swagger.jaxrs.listing.ApiListingResource.class);
register(io.swagger.jaxrs.listing.SwaggerSerializers.class);
}
}
This part works like a charm, when I open http://localhost:8090/swagger.json then I can see the expected Swagger JSON content.
But I do not know, how to add the Swagger related static HTML content to my application. I can see that this content is in the springfox-swagger-ui.jar and I can add it to my project as a maven dependency, but how I can unpack the content from this jar?
And what is the proper way to overwrite the default swagger.json URL with my URL in the static Swagger file in order to Swagger show my REST API immediately when I open swagger-ui.html.

<dependency>
<groupId>org.webjars</groupId>
<artifactId>swagger-ui</artifactId>
<version>${swagger-ui.version}</version>
</dependency>
Please, do not include springfox-swagger-ui.jar, it's meant to work with Spring's RestController.

You must have solved it now but it might help others so here's the complete procedure as I was also looking for a tutorial.
I am using Swagger V2 with Spring Boot 2 and it's straightforward 3 step process.
Step 1: Add required dependencies in pom.xml file. The second dependency is optional use it only if you need Swagger UI.
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
Step 2: Add configuration class
#Configuration
#EnableSwagger2
public class SwaggerConfig {
public static final Contact DEFAULT_CONTACT = new Contact("Usama Amjad", "https://stackoverflow.com/users/4704510/usamaamjad", "hello#email.com");
public static final ApiInfo DEFAULT_API_INFO = new ApiInfo("Article API", "Article API documentation sample", "1.0", "urn:tos",
DEFAULT_CONTACT, "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList<VendorExtension>());
#Bean
public Docket api() {
Set<String> producesAndConsumes = new HashSet<>();
producesAndConsumes.add("application/json");
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(DEFAULT_API_INFO)
.produces(producesAndConsumes)
.consumes(producesAndConsumes);
}
}
Step 3: Setup complete and now you need to document APIs in controllers
#ApiOperation(value = "Returns a list Articles for a given Author", response = Article.class, responseContainer = "List")
#ApiResponses(value = { #ApiResponse(code = 200, message = "Success"),
#ApiResponse(code = 404, message = "The resource you were trying to reach is not found") })
#GetMapping(path = "/articles/users/{userId}")
public List<Article> getArticlesByUser() {
// Do your code
}
Usage:
Swagger UI: You can access it via http://localhost:8080/swagger-ui.html
Postman: You can also access your Documentation JSON from http://localhost:8080/v2/api-docs and just copy paste it in Postman to use with it.

Related

Contract testing messages in a Flux in Spring Cloud Stream gives IllegalArgumentException Message must not be null

I'm trying to contract test a Flux of events emitted from an endpoint using Spring Cloud Stream and Spring Cloud Contract. However, I'm getting an IllegalArgumentException with detailMessage Message must not be null when running my test. Or in other words I'm not receiving any messages on my message receiver. My spring cloud version is Hoxton. I've added the following dependencies in my pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-kafka</artifactId>
</dependency>
<!--TEST -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-test-support</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-contract-verifier</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream</artifactId>
<type>test-jar</type>
<scope>test</scope>
<classifier>test-binder</classifier>
</dependency>
I've configured a new binding following the samples here. Is working properly when I run the app and I send a curl -X POST localhost:8080 request.
application.properties
stubrunner.stream.enabled=true
spring.cloud.stream.bindings.uppercase-in-0.destination=test
spring.cloud.stream.bindings.uppercase-in-0.group=testGroup
spring.cloud.stream.bindings.uppercase-out-0.destination=hood
spring.cloud.stream.bindings.consume-in-0.destination=hood
spring.cloud.stream.bindings.consume-out-0.destination=downtown
spring.cloud.stream.bindings.supplier-out-0.destination=test
spring.cloud.function.definition=uppercase;consume;supplier
Controller
#PostMapping(value = "/")
public void handlePost() {
Faker faker = new Faker();
Message<String> message = MessageBuilder.withPayload(faker.chuckNorris().fact()).build();
// supplier.get();
EventSupplier.processor.onNext(message);
}
Bean definition
#Configuration
public class EventSupplier {
public static final EmitterProcessor<Message<String>> processor = EmitterProcessor.create();
#Bean
// public Supplier<String> supplier() {
public Supplier<Flux<Message<String>>> supplier() {
// return () -> "";
return () -> processor;
}
}
Contracts Base test
#ActiveProfiles("test")
#SpringBootTest
#RunWith(SpringRunner.class)
#AutoConfigureMessageVerifier
#EnableBinding(KafkaStreamerApplication.class)
public abstract class KafkaStreamerApplicationTests {
#Autowired
private DummyController dummyController;
public void supply() {
dummyController.handlePost();
}
}
Contract
org.springframework.cloud.contract.spec.Contract.make {
label 'some_label'
input {
triggeredBy("supply()")
}
outputMessage {
sentTo('supplier-out-0')
body(
anyNonBlankString()
)
headers {
messagingContentType(applicationJson())
}
}
}
I've tried creating contracts for function and consumer and both worked. I've also tried using a supplier of type String instead of Flux<Message<String>> and worked too. You can look at the commented lines at the code above.
While debugging I've seen that in StreamMessageCollectorMessageReceiver I'm getting a DirectWithAttributesChannel instance when retrieving the MessageChannel bean at receive("supplier-out-0", 5, "SECONDS") method. Also while looking into the documentation I've found a MessageChannel implementation that, to me, looks like a candidate to be the one should have been instantiated FluxMessageChannel.
My guess is that spring cloud contract is waiting for a message to be sent into the queue directly(DirectWithAttributesChannel) but since the flux is already in the queue and I'm just updating the flux content, it means that not message is pushed into the queue(FluxMessageChannel?) so no message is received(null) and that's why I'm getting an IllegalArgumentException with details Message must not be null error when running my tests.
Is there something missing in my configuration? Can this scenario be tested by contracts? Is a bad approach to test this by using contracts?

BeanConfig (or similar?) in Swagger 2.0 (OpenApi 3.0)

I am currently migrating our API docs (which were Swagger 1.5) to Swagger 2.0 (OpenApi 3.0)
The API docs are Swagger docs which get generated with java annotations using maven packages swagger-annotations and swagger-jaxrs. I have already updated the pom.xml with new versions so it looks like:
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2</artifactId>
<version>2.0.6</version>
</dependency>
And also all the old annotations are replaced with the new ones (which change quite a lot) and looks fine.
The thing is we were using a BeanConfig to define the docs general config and auto-scan all the REST resources so the documentation got generated automatically at /swagger.json.
The problem is I can't find the "new way" of doing such thing as creating a BeanConfig and auto-scan the resources so everything gets generated at /swagger.json or /openapi.json (maybe now is something like OpenAPIDefinition?)
If somebody could point me to the right direction I would be very grateful...
After some research, I could find some documentation about it in their Github for JAX-RS application, so the result is something similar to what I was doing but now instead of using a BeanConfig, it uses OpenAPI and Info:
#ApplicationPath("/sample")
public class MyApplication extends Application {
public MyApplication(#Context ServletConfig servletConfig) {
super();
OpenAPI oas = new OpenAPI();
Info info = new Info()
.title("Swagger Sample App bootstrap code")
.description("This is a sample server Petstore server. You can find out more about Swagger " +
"at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, " +
"you can use the api key `special-key` to test the authorization filters.")
.termsOfService("http://swagger.io/terms/")
.contact(new Contact()
.email("apiteam#swagger.io"))
.license(new License()
.name("Apache 2.0")
.url("http://www.apache.org/licenses/LICENSE-2.0.html"));
oas.info(info);
SwaggerConfiguration oasConfig = new SwaggerConfiguration()
.openAPI(oas)
.prettyPrint(true)
.resourcePackages(Stream.of("io.swagger.sample.resource").collect(Collectors.toSet()));
try {
new JaxrsOpenApiContextBuilder()
.servletConfig(servletConfig)
.application(this)
.openApiConfiguration(oasConfig)
.buildContext(true);
} catch (OpenApiConfigurationException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
Though OP has answered their own question, but adding a few more details for people like me who landed on this post as I wanted to migrate from swagger 1.x to swagger 2.0 (openAPI 3) and needed complete config.
(This example is for embedded jetty)
// Jetty configuration
// ContextHandlerCollection contexts
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/api");
context.addFilter(GzipFilter.class, "/*", EnumSet.allOf(DispatcherType.class));
ResourceConfig resourceConfig = new ResourceConfig(ImmutableSet.<Class<?>>builder()
.add(MyRestService.class)
.build());
resourceConfig.registerClasses(OpenApiResource.class,AcceptHeaderOpenApiResource.class); // for swagger, this will cerate openapi.json at <host>/api/openapi.json
context.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/*");
contexts.addHandler(context);
If you need to change default swagger config, that can be done by what OP has described in their answer:
OpenAPI oas = new OpenAPI();
Info info = new Info()
.title("Swagger Sample App bootstrap code")
.description("This is a sample server Petstore server. You can find out more about Swagger " +
"at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, " +
"you can use the api key `special-key` to test the authorization filters.")
.termsOfService("http://swagger.io/terms/")
.contact(new Contact()
.email("apiteam#swagger.io"))
.license(new License()
.name("Apache 2.0")
.url("http://www.apache.org/licenses/LICENSE-2.0.html"));
oas.info(info);
SwaggerConfiguration oasConfig = new SwaggerConfiguration()
.openAPI(oas)
.prettyPrint(true)
.resourcePackages(Stream.of("io.swagger.sample.resource").collect(Collectors.toSet()));
try {
new JaxrsOpenApiContextBuilder()
.servletConfig(servletConfig)
.application(this)
.openApiConfiguration(oasConfig)
.buildContext(true);
} catch (OpenApiConfigurationException e) {
throw new RuntimeException(e.getMessage(), e);
}
There is a much simpler solution for the above requirement.
import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Contact;
import io.swagger.v3.oas.annotations.info.Info;
import org.glassfish.jersey.server.ResourceConfig;
#OpenAPIDefinition(
info =
#Info(
title = "Sample rest service",
version = "1.0.0",
description = "Sample rest service",
contact =
#Contact(
url = "https://jira2.cerner.com/projects/Dey",
name = "ADey")))
public class SampleRestApplication extends ResourceConfig {
public SampleRestApplication() {
register(OpenApiResource.class);
}
}
your service will load your API spec at /openApi.yaml|json.
Another variant on the same theme. You can package up your openAPI config generation logic into a stand-alone class like so:
#Provider
public class SwaggerInfoBlackMagic implements Feature {
#Context ServletConfig config;
#Context Application app;
#Override
public boolean configure(FeatureContext context) {
//The aim here is to force construction of a (convincing) OpenApiContext before swagger does!
//This has been lifted from BaseOpenApiResource
String ctxId = getContextIdFromServletConfig(config);
try {
OpenApiContext ctx = new JaxrsOpenApiContextBuilder()
.servletConfig(config)
.application(app)
//Might need more of these depending on your setup..
//.resourcePackages(resourcePackages)
//.configLocation(configLocation)
.openApiConfiguration(getOpenApi())
.ctxId(ctxId)
.buildContext(true); //this also stores the instance statically
} catch (OpenApiConfigurationException e) {
throw new RuntimeException(e);
}
return true;
}
private OpenAPIConfiguration getOpenApi() {...}
}
Then whenever you need it you can simply add:
jersey().register(SwaggerInfoBlackMagic.class);
It's the same as the above but slightly tidier.

Swagger 2.8.0 unable to generate api-docs

I am unable to get a valid json in my spring boot application, when i call the swagger /v2/api-docs endpoint.
Chrome error message:
This page contains the following errors: error on line 1 at
column 1330: xmlParseEntityRef: no name Below is a rendering of the
page up to the first error.
With the developer tools i see that the swagger.json is wrapped in xml tags,
also the content type is set to application/xhtml+xml.
The response look like this:
<Json>{"swagger":"2.0",..............</Json>
I am using Spring Boot 2.0.0.RELEASE, Spring 5.0.4.RELEASE and
for XML mapping the jackson-dataformat-xml dependency version 2.9.4.
Is there a way that i can send application/json as content type or configure the jackson dependency, that spring loads it not as the first in classpath?
Or is there any other way to fix it?
For Jackson we only used the import, no seperate configuration.
Swagger Configuration:
#SpringBootApplication (exclude = {SecurityAutoConfiguration.class})
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
#ConditionalOnWebApplication
#EnableSwagger2
public static class SwaggerConfig {
#Bean
public Docket springfoxDocket() {
StringVendorExtension vendorExtension = new StringVendorExtension("x-wso2-security", Wso2Config.serialize());
List<VendorExtension> vendorExtensions = new ArrayList<>();
vendorExtensions.add(vendorExtension);
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com"))
.build()
.protocols(protocolSet())
.apiInfo(apiInfo())
.extensions(vendorExtensions)
.directModelSubstitute(LocalDateTime.class, Date.class)
.useDefaultResponseMessages(false).enableUrlTemplating(false)
;
}
private ApiInfo apiInfo() {
return new ApiInfo(
"Event",
"Creates, deletes, reads events",
"2.0",
"",
null,
null, null, Collections.emptyList());
}
private Set<String> protocolSet() {
Set<String> protocolsSet = new HashSet<>();
protocolsSet.add("http");
protocolsSet.add("https");
return protocolsSet;
}
}
}
Since you have jackson-dataformat-xml dependency spring boot will default the content type to application/xml.
If you want application\jsonas content type then configure restTemplate to do so.
Please follow the question Swagger 2 accept xml instead of json

Can't get json from Swagger + Jersey

I have RESTful service based on Jersey 1.18.1 and I want to show my API via Swagger.
Firstly I have to get JSON. I read this instruction: Swagger Core Jersey 1.X Project Setup 1.5. Swagger allows to set up a configuration different methods and I decided to use custom Application subclass. I did everything step by step but I can't get JSON which I have to use for swagger-ui.
What I did:
My custom Application
#ApplicationPath("api/v1")
public class DiscountsApp extends Application{
public DiscountsApp() {
BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("1.0.2");
beanConfig.setSchemes(new String[]{"http"});
beanConfig.setHost("localhost:8002");
beanConfig.setBasePath("swaggerapi");
beanConfig.setResourcePackage("alexiuscrow.diploma.endpoints");
beanConfig.setScan(true);
}
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet();
resources.add(ShopsResources.class);
//...
resources.add(com.wordnik.swagger.jaxrs.listing.ApiListingResource.class);
resources.add(com.wordnik.swagger.jaxrs.listing.SwaggerSerializers.class);
return resources;
}
}
ShopsResources
#Path("/shops")
#Api(value="/shops", description="Shops")
public class ShopsResources {
#GET
#Produces(MediaType.APPLICATION_JSON)
#ApiOperation(value = "List shops", httpMethod = "GET",
notes = "List nearest or locality shops",
response = Shops.class, responseContainer = "List")
public String getShops(
#ApiParam( value = "Radius", required = false)
#QueryParam("radius") String radiusParam,
#ApiParam( value = "Latitude", required = true)
#QueryParam("lat") String latParam,
#ApiParam( value = "Longitude", required = true)
#QueryParam("lng") String lngParam) throws SQLException{
//The list of Shops objects is serialized to string
//using the custom GSON serializer and I know
//that there is the better method of the solution of this task.
}
}
}
Some dependencies from pom.xml
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.18.1</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-servlet</artifactId>
<version>1.18.1</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-bundle</artifactId>
<version>1.18.1</version>
</dependency>
<dependency>
<groupId>com.wordnik</groupId>
<artifactId>swagger-jersey-jaxrs</artifactId>
<version>1.5.1-M2</version>
</dependency>
After deploy application to Tomcat I tried to get http://localhost:8002/swaggerapi but I've got no result.
I didn't find the swagger.json in root of my application (/tomcat8/webapps/app).
What's wrong?
How can I get JSON with my API?
I did not correctly build the url.
Correct:
http://{host}:{port}/{context root of application}/{path from #ApplicationPath}/swagger.json
In my case: http://localhost:8080/app/api/v1/swagger.json
Thx to Ron.
adding a relative path worked for me (this is using .netcore 1.1)
app.UseSwaggerUI(s => {
s.RoutePrefix = "help";
s.SwaggerEndpoint("../swagger/v1/swagger.json", "MySite");
s.InjectStylesheet("../css/swagger.min.css");
});

How to return a XML response from a POST rquest with Spring MVC?

I am making a POST request which sends a JSON. The Controller picks up the JSON, processes the JSON and I want the controller to return some data in XML format.
How can I do that with a POST request?
#RequestMapping( value = Controller.RESOURCE_PATH + ".xml", headers = "Accept=application/json", produces = "*/*" )
public String exportXml( #RequestBody String requestJson ) throws IOException
{
JSONObject json = JSONObject.fromObject( requestJson );
Option option = new Option();
option.processJson( json );
return "";
}
There are many ways to achieve this. One is to use MarshallingView and XStreamMarshaller
Firstly add following jars to your classpath (maven dependencies):
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.4</version>
</dependency>
Then configure a Marshaller on your spring xml configuration
<bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller"/>
Assuming you have following bean you want to Marshal (ie: display as XML)
public class MyMessage {
private String message;
// getters & setters
}
In your controller class inject org.springframework.oxm.Marshaller and have your handler method return a MarshallingView like this:
#Controller
public class MyController {
#Autowired private Marshaller marshaller;
#RequestMapping("/helloxml")
public MarshallingView helloxml(Model model) {
MyMessage msg = new MyMessage();
msg.setMessage("hello world");
model.addAttribute("msg", msg);
MarshallingView marshallingView = new MarshallingView(marshaller);
marshallingView.setModelKey("msg"); // set what model attribute to display as xml
return marshallingView;
}
}
The above setup will give you xml like this when /helloxml is requested
<com.gerrydevstory.xmlview.MyMessage>
<message>hello world</message>
</com.gerrydevstory.xmlview.MyMessage>
Of course this isn't a very good setup if you deal with many XML marshalling. You should leverage view resolvers configuration in this case.
Also the name of XML element can be aliased too to shorten it. Check out the XStream documentation
Finally, keep in mind XStream is just one of many marshaller supported by Spring, also consider JAXB, Castor, Jibx etc.

Categories

Resources