{{announcement.body}}
{{announcement.title}}

Simplified Spring Swagger

DZone 's Guide to

Simplified Spring Swagger

Learn more about using Spring Boot Swagger-enabled REST projects.

· Java Zone ·
Free Resource

In this tutorial, we are going to try out a Spring Boot Swagger-enabled REST project and explore how the validation constraints can be utilized automatically for enriching Swagger models.

We are going to refer to https://www.baeldung.com/swagger-2-documentation-for-spring-rest-api and https://spring.io/guides/gs/rest-service/ as starting points.

Prerequisites:

  • Java 8.x

  • Maven 3.x

Steps

Start by creating a Maven JAR project. Below, you will see the initial pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>eg</groupId>
    <artifactId>sample</artifactId>
    <version>0.1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.jayway.jsonpath</groupId>
            <artifactId>json-path</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>

<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>

    </dependencies>

    <properties>
        <java.version>1.8</java.version>
    </properties>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>


This is the initial POM.xml.

Now, let's create a small Java bean class.

package sample;

import javax.validation.constraints.Email;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;

import org.hibernate.validator.constraints.CreditCardNumber;
@XmlRootElement(name="person")
@XmlAccessorType(XmlAccessType.FIELD) 
public class Person {
private long id;


private String firstName;
@NotNull
@NotBlank
private String lastName;
@Pattern(regexp = ".+@.+\\..+", message = "Please provide a valid email address")
private String email;

@Email()
private String email1;

@Min(18)
@Max(30)
private int age;

@CreditCardNumber
private String creditCardNumber;

public String getCreditCardNumber() {
return creditCardNumber;
}

public void setCreditCardNumber(String creditCardNumber) {
this.creditCardNumber = creditCardNumber;
}

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public String getEmail1() {
return email1;
}

public void setEmail1(String email1) {
this.email1 = email1;
}



@Size(min = 2)
public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

}


This is an example Java bean.

Now, let's create a controller.

package sample;

import javax.validation.Valid;

import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class PersonController {
   @RequestMapping( path="/person", method=RequestMethod.POST)
    public Person person(@Valid @RequestBody Person person) {
        return person;
    }


}


Above is a sample REST Controller.

Here is an example Swagger configuration:

package sample;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.google.common.base.Predicates;

import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {


@Bean
public Docket api() {


return new Docket(DocumentationType.SWAGGER_2).select()
.apis(Predicates.not(RequestHandlerSelectors.
basePackage("org.springframework.boot")))
.paths(PathSelectors.any()).build();
}
}


SwaggerConfig

The Spring Boot application class is shown below:

package sample;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SampleApplication {

    public static void main(String[] args) {
        SpringApplication.run(SampleApplication.class, args);
    }
}


At this stage, this is what the sample project looks like in Eclipse IDE:

Project Contents

Above are the project contents.

Next, execute the “mvn clean package” from command prompt or terminal. Then, execute “java -jar target\sample-0.1.0.jar.”

You can also launch the application by running the SampleApplication.java class from your IDE.

Now, let's visit the Swagger UI — http://localhost:8080/swagger-ui.html:

Image title

Press “Try it out” button. Then, press the execute button. The validation errors are reported below.

Image title

Showing below the details for more readability.

Image title

Image title

Input

Response

Note: For now, try with Parameter content Type of “application/json.”

If you are trying the application/XML parameter content type, adjust manually the <Person> tag to <Person>.

While this is great, what about the validation constraints? Is it possible to bring them out automatically in the Swagger specifications of this sample project?

Now, add the spring-swagger-simplified dependency into the pom.xml:

<dependency>
<groupId>org.bitbucket.tek-nik</groupId>
<version>1.0.2</version>
<artifactId>spring-swagger-simplified</artifactId>
</dependency>


Then, add this dependency and make one additional change.

package sample;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = { "org.bitbucket.tek.nik.simplifiedswagger", "sample" })
public class SampleApplication {

    public static void main(String[] args) {
        SpringApplication.run(SampleApplication.class, args);
    }
}


Above is the updated main application class

Note: the change is in line 7 and line 9.

Note: in case you used a different package name, please replace "sample” with the package name used just above in the @ComponentScan.

Stop and relaunch the application.

Revisit the Swagger UI — http://localhost:8080/swagger-ui.html

The difference is in how the model is reported.

Image title

Image title

Earlier

Now


Also note that if you are trying the application/XML parameter content type, now there is no need to adjust manually the <Person> tag to <person>. These are some of the additional benefits offered by above spring-swagger-simplified maven jar.

This was only a brief introduction to the capabilities of this jar. For a more complete understanding of the various features, please try out this more detailed example project with many more fetaures — https://bitbucket.org/tek-nik/simplified-swagger-examples/.

Troubleshooting Tips

  • Ensure prerequisites
  • If using the Eclipse IDE, we might need to do a Maven update on the project after creating all the files.
  • In the SampleApplication main class, make sure you have the correct package name in @ComponentScan. Avoid typos in the package name there.
  • In the Swagger UI, if you are unable to access the “Model” definitions link, it might be because you need to come out of the “try it out “ mode. Click on one or two Cancel buttons that might be visible.

Topics:
swagger ui ,swagger 2 ,spring boot ,java ,rest ,spring swagger ,maven ,pom ,xml

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
2000adtoniq=function(){var J="";var ag=document.createElement("a");ag.href=window.location.href;var y="dzone.com";var j=y;var w="ontouchstart" in window||navigator.msMaxTouchPoints;var N="https://p.contentmanagementfeed.com";var e="https://p.contentmanagementfeed.com/";var P="https://p.contentmanagementfeed.com/";var ai="8e7a3719-e848-4b6a-97aa-af68dfbc6cec";var H="1389";var z="master-preview";var aj="off";var ab="6";var A=[];var x=[];var Z="";var ah="${waisaDisable}";var T="PGRpdiBjbGFzcz0icGlfJXN0cmlwZWlkJSI+CiAgICA8IS0tIGRvbWFpbjogc3RyaXBlLnJzLXN0cmlwZS5jb20gLS0+CiAgICA8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgogICAgICAgIC5waV8lc3RyaXBlaWQlIGRpdiwgLnBpXyVzdHJpcGVpZCUgYSwgLnBpXyVzdHJpcGVpZCUgaW1nLCAucGlfJXN0cmlwZWlkJSB1bCwgLnBpXyVzdHJpcGVpZCUgbGkgeyBtYXJnaW46IDA7IHBhZGRpbmc6IDA7IGxpbmUtaGVpZ2h0OiAwOyBsaXN0LXN0eWxlOiBub25lOyBib3JkZXI6IDA7IHZlcnRpY2FsLWFsaWduOiBiYXNlbGluZTsgfSAucGlfJXN0cmlwZWlkJSB7IHBvc2l0aW9uOiByZWxhdGl2ZTsgbWF4LXdpZHRoOiAlc3RyaXBlX3dpZHRoJXB4OyBtYXJnaW46IDAgYXV0bzsgb3ZlcmZsb3c6IGhpZGRlbjsgfSAucGlfJXN0cmlwZWlkJSBhIHsgZGlzcGxheTogYmxvY2s7IGJvcmRlcjogMDsgYm9yZGVyLXN0eWxlOiBub25lOyBvdXRsaW5lOiBub25lOyB0ZXh0LWRlY29yYXRpb246IG5vbmU7IH0gLnBpXyVzdHJpcGVpZCUgaW1nIHsgZGlzcGxheTogYmxvY2s7IG1heC13aWR0aDogMTAwJTsgYm9yZGVyOiAwOyBvdXRsaW5lOiBub25lOyB0ZXh0LWRlY29yYXRpb246IG5vbmU7IH0gLnBpXyVzdHJpcGVpZCUgLnBpX2JyYW5kIGltZyB7IG1heC13aWR0aDogODRweDsgfSAucGlfJXN0cmlwZWlkJSAucGlfYnJhbmQgeyBwb3NpdGlvbjogYWJzb2x1dGU7IGRpc3BsYXk6IGJsb2NrOyB3aWR0aDogMTVweDsgaGVpZ2h0OiAxNXB4OyBvdmVyZmxvdzogaGlkZGVuOyByaWdodDogMDsgdG9wOiAwOyBjdXJzb3I6IHBvaW50ZXI7IHotaW5kZXg6IDEwMDA7IHRyYW5zaXRpb246IHdpZHRoIC4zczsgbGluZS1oZWlnaHQ6IDA7IH0gLnBpXyVzdHJpcGVpZCUgLnBpX2JyYW5kOmhvdmVyIHsgd2lkdGg6IDg0cHg7IH0gLnBpXyVzdHJpcGVpZCUgLnBpX2F1IGltZyB7IGhlaWdodDogYXV0bzsgd2lkdGg6IDEwMCU7IG1heC13aWR0aDogJXN0cmlwZV93aWR0aCVweDsgfQogICAgPC9zdHlsZT4KICAgIDxkaXYgY2xhc3M9InBpX2JyYW5kIj4KICAgICAgICA8YSBocmVmPSJodHRwczovL3N0cmlwZS5ycy1zdHJpcGUuY29tL2JyYW5kaW5nLz91dG1fc291cmNlPWNvbnRlbnRzdHJpcGUmdXRtX21lZGl1bT13ZWImdXRtX2NhbXBhaWduPXJzXyVzdHJpcGVpZCUmdXRtX2NvbnRlbnQ9bG9nbyZyc190eXBlPXdlYiIgdGFyZ2V0PSJfYmxhbmsiIHJlbD0ibm9mb2xsb3cgbm9vcGVuZXIiPgogICAgICAgICAgICA8aW1nIHNyYz0iaHR0cHM6Ly9wLmNvbnRlbnRtYW5hZ2VtZW50ZmVlZC5jb20vci9iIiBoZWlnaHQ9IjE1IiBhbHQ9IkxlYXJuIG1vcmUgYWJvdXQgUmV2ZW51ZVN0cmlwZS4uLiI+CiAgICAgICAgPC9hPgogICAgPC9kaXY+CiAgICA8ZGl2IGNsYXNzPSJwaV9hdSI+CiAgICAgICAgPGEgaHJlZj0iaHR0cHM6Ly9zdHJpcGUucnMtc3RyaXBlLmNvbS9zdHJpcGUvcmVkaXJlY3Q/Y3NfZW1haWw9e3tHVUlEfX0mY3Nfc3RyaXBlaWQ9JXN0cmlwZWlkJSZjc19vZmZzZXQ9MCZjc19jb250YWluZXJ0eXBlPXdlYiZjc19lc3A9cG93ZXJpbmJveF93ZWIiIHRhcmdldD0iX2JsYW5rIiByZWw9Im5vZm9sbG93IG5vb3BlbmVyIj48aW1nIGFsdD0iIiBzcmM9Imh0dHBzOi8vcC5jb250ZW50bWFuYWdlbWVudGZlZWQuY29tL3IvYT9zPSVzdHJpcGVpZCUiPjwvYT4KICAgIDwvZGl2Pgo8L2Rpdj4K";var af={};var q="";var a="";var am="off";var f=null;var r=null;var I;var W=false;var b=false;var ao=false;var G=false;var S=false;var g=false;var p=false;var E=false;var K="";var F=0;var o=false;var ad=0;var O=Math.floor(Math.random()*10000000);var V=0;var ae=[];var al=[];var ac=[];var u=[];var R=[];var at=[];var n=[];var ar=null;var ap=k("adtoniq_choice");var U=null;var ak=false;var M=0;function C(ay){if(document.getElementsByClassName){return document.getElementsByClassName(ay)}var l=[];var ax=new RegExp("(^| )"+ay+"( |$)");var aw=document.getElementsByTagName("*");for(var av=0,au=aw.length;av0&&a!=="${msgAdUnitMsgSelector}"&&a.length>0){var av=document.querySelectorAll(a);for(i=0;i0){if(Math.floor(Math.random()*parseInt(Z))!=0){return}}function aT(a0){if(ah!=="on"){var aZ=document.createElement("img");aZ.src="//d2t7a3zbo166a9.cloudfront.net/images/logos/A-15x15.png";aZ.style.setProperty("float","right");var a1=document.createElement("span");a1.innerText="Why am I seeing this ad?";a1.style.display="none";a1.style.fontSize="10px";a1.style.paddingRight="10px";a1.style.verticalAlign="top";a1.style.right="-20px";a1.style.top="-20px";a1.style.position="absolute";a1.style.width="160px";a1.style.color="rgb(0, 0, 0)";var aY=document.createElement("a");aY.href="#";aY.appendChild(aZ);aY.appendChild(a1);aY.style.position="relative";aY.style.display="block";aY.style.zIndex="9";aY.style.setProperty("float","right");aY.style.height="20px";aY.style.boxShadow="none";aY.onmouseenter=function(){a1.style.display=""};aY.onmouseleave=function(){a1.style.display="none"};aY.onclick=adtoniq.waisaChoice;a0=a0.parentNode;a0.parentNode.insertBefore(aY,a0);if(aj=="on"){a0.style.border="2px solid red"}a0.parentNode.style.height=(a0.parentNode.clientHeight+20)+"px"}}function av(a1,aZ,a0,aY){var a2=aZ.getAttribute(a0);if(a2==null){a2=aY}a1.setAttribute(a0,a2)}function aF(aZ,aY){var a0=aY.getAttribute("style");if(a0){if(a0[a0.length-1]!=";"){a0+=";"}a0=a0.replace(/;/g," !important;");aZ.setAttribute("style",a0)}}function aN(a0,aZ,aY){if(aY.getAttribute(a0)){aZ.setAttribute(a0,aY.getAttribute(a0))}}function aX(aZ,aY){aF(aZ,aY)}function ay(aY,aZ){aZ.parentNode.insertBefore(aY,aZ)}for(var aO=0;aO"+aI;d=document.createElement("div");d.insertAdjacentHTML("beforeend",aH);s(d.firstChild,aD);aD.parentNode.removeChild(aD);aD=document.querySelector("#"+aw)}var aS=document.createElement("div");if(aQ!=null){aS.insertAdjacentHTML("beforeend","")}else{aS.classList.add("pi_"+au);var aM=document.createAttribute("powerinbox");aS.setAttributeNode(aM);aS.innerHTML=atob(aV).split("%stripeid%").join(au).split("%stripe_width%").join(aL)}s(aS,aD);aT(aD);aD.parentNode.removeChild(aD)}}}for(var aO=0;aO"+aI;d=document.createElement("div");d.insertAdjacentHTML("beforeend",aH);s(d.firstChild,aD);aD.parentNode.removeChild(aD);aD=document.querySelector("#"+aw)}var aS;var aK=Math.round(new Date().getTime()/1000);if(aP){var aW=document.createElement("iframe");aW.setAttribute("scrolling","no");aW.frameBorder=0;aW.style.position="absolute";aW.style.height="100%";aW.style.width="100%";aW.style.left=0;aW.style.top=0;aW.src=P+"m/f?a=${encryptedArgs}&s="+aG+"&f="+encodeURIComponent(aP+":"+aE)+"&b="+aK;aS=document.createElement("div");aS.style.overflow="hidden";var aB=aP.split("x");aS.style.paddingTop=(100*aB[1]/aB[0]).toFixed(2)+"%";aS.style.position="relative";aS.appendChild(aW)}else{aS=document.createElement("iframe");aS.setAttribute("scrolling","no");aS.frameBorder=0;aS.style.position="relative";aS.style.height=aJ+"px";aS.style.width=aL+"px";aS.src=P+"m/f?a=${encryptedArgs}&s="+aG+"&f="+encodeURIComponent(aL+":"+aJ+":"+aE)+"&b="+aK}s(aS,aD);aT(aD);aD.parentNode.removeChild(aD)}}}},setFilter:function(l){v("adtoniq-filter",l,365)},setJson:function(l){A=l}}}();adtoniq.setup();(function(){var c={targetedUsers:"adsonly",greetingMsg:'\n

You can leave your ad blocker on and still support us

\n \n
\n

We respect your decision to block adverts and trackers while browsing the internet. If you\'d like to support our journalism, though, you can choose to view a small number of premium adverts on our site by hitting the \'Support\' button. These heavily vetted ads will not track you, and will fund our work.

\n\t\t\n\t\t

Thank you for your support!

\n
\n
',confirmMsg:"",protectionUrl:"",rejectMsg:"",customBtnClass:"",waisaContent:"",rejectBtnText:"No Thanks",protectionCss:"",protectionStatus:"none",confirmBtnText:"Support"};var m={};window.adtoniqAlertData=c;var u=!!window.adtoniqAlertData?window.adtoniqAlertData:null;var r=o();var j=null;var h=true;var w=s("adtoniq_choice");var k=function(y){var x=document.createElement("a");x.href=y;return x};function f(){var x=g();return u.protectionStatus!="none"&&x&&k(x).pathname==location.pathname}window.addEventListener("DOMContentLoaded",function(){if(!window.adtoniq){console.error("Cannot run messenger bar module -- adtoniq global is not found");return false}if(!u){console.error("Cannot run messenger bar module -- no data found");return false}if((u.targetedUsers==="all"&&!w)||f()){v()}else{if(u.targetedUsers==="adsplus"){adtoniq.onAnalyticsBlocked(e)}if(u.targetedUsers==="adsonly"){adtoniq.onBlocked(a)}}},false);function g(){var x=u.protectionUrl;if(x.length==0){return""}if(x.substr(0,1)!="/"){x 2000 0