DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Mastering Spring: Synchronizing @Transactional and @Async Annotations With Various Propagation Strategies
  • Composing Custom Annotations in Spring
  • Java, Spring Boot, and MongoDB: Performance Analysis and Improvements
  • Smart Dependency Injection With Spring: Assignability (Part 2 of 3)

Trending

  • Understanding Java Signals
  • Microsoft Azure Synapse Analytics: Scaling Hurdles and Limitations
  • Beyond ChatGPT, AI Reasoning 2.0: Engineering AI Models With Human-Like Reasoning
  • Create Your Own AI-Powered Virtual Tutor: An Easy Tutorial
  1. DZone
  2. Coding
  3. Frameworks
  4. Doing More With Springdoc OpenAPI

Doing More With Springdoc OpenAPI

Learn how to render fully qualified names in the generated Swagger documentation and global exception handling using Controller Advice.

By 
Raghuraman Ramaswamy user avatar
Raghuraman Ramaswamy
DZone Core CORE ·
Updated Apr. 17, 24 · Tutorial
Likes (24)
Comment
Save
Tweet
Share
109.9K Views

Join the DZone community and get the full member experience.

Join For Free

Java adoption has shifted from version 1.8 to at least Java 17. Concurrently, Spring Boot has advanced from version 2.x to 3.2.2. The springdoc project has transitioned from the older library 'springdoc-openapi-ui' to 'springdoc-openapi-starter-webmvc-ui' for its functionality. These updates mean that readers relying on older articles may find themselves years behind in these technologies. The author has updated this article so that readers are using the latest versions and don't struggle with outdated information during migration.


In my previous article, we tried out a Spring Boot Open API 3-enabled REST project and explored some of its capabilities, namely:

  • Automatic JSR-303-related Swagger documentation
  • How Maven builds properties could be shown as project information in the Swagger documentation

In this continuation, we will explore two additional objectives, namely:

  • Rendering fully qualified names in the generated Swagger documentation
  • Global exception handling using Controller Advice and its related Swagger documentation

We are going to refer to Building a RESTful Web Service and springdoc-openapi v2.5.0 like last time.

Prerequisites

  • Java 17.x
  • Maven 3.x

Steps

The code is loosely based on the last article, but there are some differences/additions. We will start by listing the new code. We will then run the application. As we walk through this tutorial listing the various artifacts, we will discuss which code helped in achieving the objectives of this tutorial along with some other details.

The code is a typical Java Maven project and is laid out as shown below:

Typical Java Maven project

Listing below the different artifacts one by one. Try following the layout above.

Showing pom.xml:

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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.2.2</version>
		<relativePath ></relativePath> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>sample</artifactId>
	<version>0.0.1</version>
	<name>sample</name>
	<description>Demo project for Spring Boot with openapi 3 documentation</description>

	<properties>
		<java.version>17</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springdoc</groupId>
			<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
			<version>2.5.0</version>
		</dependency>
		
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build> 
</project>


SampleApplication.java follows:

Java
 
package sample;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;

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

	
	@Bean
	public OpenAPI customOpenAPI(@Value("${application-description}") String appDesciption, 
			@Value("${application-version}") String appVersion) {
	
		return new OpenAPI()
	      .info(new Info()
	      .title("sample application API")
	      .version(appVersion)
	      .description(appDesciption)
	      .termsOfService("http://swagger.io/terms/")
	      .license(new License().name("Apache 2.0").url("http://springdoc.org")));
	}
}


Let's now move on to the main model class, Person.java:

Java
 
package sample;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;


import org.hibernate.validator.constraints.CreditCardNumber;


public class Person {
	private long id;
	private String firstName;
	@NotNull
	@NotBlank
	@Size(max = 10)
	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;
	}
}
-


PersonController.java follows:

Java
 
package sample;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

import org.springframework.validation.annotation.Validated;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;

@RestController
@Validated
public class PersonController {
	private Random ran = new Random(); 
	@RequestMapping(path = "/person", method = RequestMethod.POST)
	@io.swagger.v3.oas.annotations.parameters.RequestBody(required = true, content = @Content(examples = {
			@ExampleObject(value = INVALID_REQUEST, name = "invalidRequest", description = "Invalid Request"),
			@ExampleObject(value = VALID_REQUEST, name = "validRequest", description = "Valid Request") }))
	public Person person(@Valid @RequestBody Person person) {
		 
	     int nxt = ran.nextInt(10); 
	     if(nxt>=5)
	     {
	    	 throw new RuntimeException("Breaking logic");
	     }
		return person;
	}
	@RequestMapping(path = "/personByLastName", method = RequestMethod.GET)
	public List<Person> findByLastName(@RequestParam(name = "lastName", required = true)@NotNull
			@NotBlank
			@Size(max = 10)String lastName){
		List<Person> hardCoded= new ArrayList<>();
		Person person= new Person();
		person.setAge(20);
		person.setCreditCardNumber("4111111111111111");
		person.setEmail("abc@abc.com");
		person.setEmail1("abc1@abc.com");
		person.setFirstName("Somefirstname");
		person.setLastName(lastName);
		person.setId(1);
		hardCoded.add(person);
		return hardCoded;
		
	}
	
	private static final String VALID_REQUEST = """
			{
			  "id": 0,
			  "firstName": "string",
			  "lastName": "string",
			  "email": "abc@abc.com",
			  "email1": "abc@abc.com",
			  "age": 20,
			  "creditCardNumber": "4111111111111111"
			}""";

	private static final String INVALID_REQUEST = """
			{
			  "id": 0,
			  "firstName": "string",
			  "lastName": "string",
			  "email": "abcabc.com",
			  "email1": "abcabc.com",
			  "age": 17,
			  "creditCardNumber": "411111111111111"
			}""";
}


The main changes are:

  • The addition of a GET method
  • Use of some additional annotations like @Validated, @RequestParam, @NotNull, @NotBlank, and @Size

Note: Use of @Validated causes validations to be applied even for the GET method parameter. After the application is complete and running play around with the above annotations. Feel free to remove them and see their effect. The GET URL can always be directly invoked using http://localhost:8080/personByLastName and http://localhost:8080/personByLastName?lastName=12345678901. For now, it's a little early. Let's continue adding the remaining artifacts.

Let's discuss global exception handling using @ControllerAdvice and how we can achieve corresponding Swagger documentation.

When we come to exception handling in Spring controllers, there are so many exceptions possible. There may be various validation-related exceptions invoked even before the actual controller code is invoked. 

The controller itself may have some logic that leads to a business exception or some exception because of bad coding. Rather than handling these exceptions in each controller, we are choosing to duck the exceptions. We are choosing to centralize the exception handling be it during the controller invocation or before.

Below, produce a very rudimentary ControllerAdvice — GlobalControllerAdvice.java, which you can later expand upon and improve as needed.

Java
 
package sample;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;



@ControllerAdvice @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) 
public class GlobalControllerAdvice //extends ResponseEntityExceptionHandler  
{
	/**
	 * Note use base class if you wish to leverage its handling.
	 * Some code will need changing.
	 */
	private static final Logger logger = LoggerFactory.getLogger(GlobalControllerAdvice.class);
    
    @ExceptionHandler(Throwable.class) 
    @ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
    public ResponseEntity < Problem > problem(final Throwable e) {
        String message =e.getMessage();
		//might actually prefer to use a geeric mesasge
		
		message="Problem occured";
		UUID uuid = UUID.randomUUID();
		String logRef=uuid.toString();
		logger.error("logRef="+logRef, message, e);
		return new ResponseEntity <Problem> (new Problem(logRef, message), HttpStatus.INTERNAL_SERVER_ERROR);
    }
   
    
    
    @ExceptionHandler(MethodArgumentNotValidException.class) 
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public ResponseEntity<ErrorMessage> handleMethodArgumentNotValid(MethodArgumentNotValidException ex
    		) {
        List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();
        List<ObjectError> globalErrors = ex.getBindingResult().getGlobalErrors();
        List<String> errors = new ArrayList<>(fieldErrors.size() + globalErrors.size());
        String error;
        for (FieldError fieldError : fieldErrors) {
            error = fieldError.getField() + ", " + fieldError.getDefaultMessage();
            errors.add(error);
        }
        for (ObjectError objectError : globalErrors) {
            error = objectError.getObjectName() + ", " + objectError.getDefaultMessage();
            errors.add(error);
        }
        ErrorMessage errorMessage = new ErrorMessage(errors);
      
        //Object result=ex.getBindingResult();//instead of above can allso pass the more detailed bindingResult
        return new ResponseEntity(errorMessage, HttpStatus.BAD_REQUEST);
    }
    @ExceptionHandler(ConstraintViolationException.class) 
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public ResponseEntity<ErrorMessage> handleConstraintViolatedException(ConstraintViolationException ex
    		) {
    	Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
    	
       
        List<String> errors = new ArrayList<>(constraintViolations.size() );
        String error;
        for (ConstraintViolation constraintViolation : constraintViolations) {
        	
            error =  constraintViolation.getMessage();
            errors.add(error);
        }
     
        ErrorMessage errorMessage = new ErrorMessage(errors);
        return new ResponseEntity(errorMessage, HttpStatus.BAD_REQUEST);
    }
    
    @ExceptionHandler(MissingServletRequestParameterException.class) 
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public ResponseEntity<ErrorMessage> handleMissingServletRequestParameterException(MissingServletRequestParameterException ex
    		) {
    	
       List<String> errors = new ArrayList<>( );
        String error=ex.getParameterName()+", "+ex.getMessage();
       errors.add(error);
        ErrorMessage errorMessage = new ErrorMessage(errors);
         return new ResponseEntity(errorMessage, HttpStatus.BAD_REQUEST);
    }

   
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class) 
    @ResponseStatus(code = HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    public ResponseEntity<ErrorMessage> handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex 
    		) {
        String unsupported = "Unsupported content type: " + ex.getContentType();
        String supported = "Supported content types: " + MediaType.toString(ex.getSupportedMediaTypes());
        ErrorMessage errorMessage = new ErrorMessage(unsupported, supported);
        return new ResponseEntity(errorMessage, HttpStatus.UNSUPPORTED_MEDIA_TYPE);
    }

    @ExceptionHandler(HttpMessageNotReadableException.class) 
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    public ResponseEntity<ErrorMessage> handleHttpMessageNotReadable(HttpMessageNotReadableException ex) {
        Throwable mostSpecificCause = ex.getMostSpecificCause();
        ErrorMessage errorMessage;
        if (mostSpecificCause != null) {
            String exceptionName = mostSpecificCause.getClass().getName();
            String message = mostSpecificCause.getMessage();
            errorMessage = new ErrorMessage(exceptionName, message);
        } else {
            errorMessage = new ErrorMessage(ex.getMessage());
        }
        return new ResponseEntity(errorMessage,  HttpStatus.BAD_REQUEST);
    }
   
}


The key thing to remember is to decorate the methods in this class with @org.springframework.web.bind.annotation.ResponseStatus which is the only hint that Springdoc OpenAPI needs for including the appropriate models in its controller method Swagger documentation.

For completeness, below, we will list two classes referred to in this GlobalControllerAdvice.java before we attempt to start the application.

Problem.java follows:

Java
 
package sample;

public class Problem {

	private String logRef;
	private String message;

	public Problem(String logRef, String message) {
		super();
		this.logRef = logRef;
		this.message = message;
	}

	public Problem() {
		super();

	}

	public String getLogRef() {
		return logRef;
	}

	public void setLogRef(String logRef) {
		this.logRef = logRef;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
}


ErrorMessage.java also follows:

Java
 
package sample;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;


public class ErrorMessage {

    private List<String> errors;

    public ErrorMessage() {
    }

    public ErrorMessage(List<String> errors) {
        this.errors = errors;
    }

    public ErrorMessage(String error) {
        this(Collections.singletonList(error));
    }

    public ErrorMessage(String ... errors) {
        this(Arrays.asList(errors));
    }

    public List<String> getErrors() {
        return errors;
    }

    public void setErrors(List<String> errors) {
        this.errors = errors;
    }
}


Let's Not Forget application.properties

Properties files
 




xxxxxxxxxx
1


 
1
application-description=@project.description@
2
application-version=@project.version@
3
springdoc.use-fqn=true



Starting with v1.4.4 of springdoc-openapi, "springdoc.use-fqn=true" enables fully qualified names support. Earlier, to achieve the same, we had to write a custom converter. 

You might refer to this GitHub comment regarding this.

Also note: Now that we are using ControllerAdvice we no longer need server.error.include-binding-error in application.properties. Refer to previous article if needing more information. 

That completes all our code for this tutorial. Let's try it out.

Let's Try It Out

Execute the mvn clean package from the command prompt or terminal. Then, execute java -jar target\sample-0.0.1.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. We can also visit the JSON-based API-docs - http://localhost:8080/v3/api-docs. For YAML-based API-docs, use http://localhost:8080/v3/api-docs.yaml. These URLs are the defaults and can be changed if needed.

The general usage of Swagger UI is explained in the last article. In case of issues please see the "Troubleshooting" section at the bottom of this tutorial.

We will focus this tutorial on specific areas.

Now that the application should be running let's visit it.

The swagger UI landing screen — http://localhost:8080/swagger-ui.html.

Sample application API screen

Sample application API

Note the fully qualified class names.

Now let's see how Controller Advice has contributed to the Swagger documentation.

How Controller Advice has contributed to the Swagger documentation

Default response

If we were to click the schema links and expand the properties sections it would look like this.

Default response codes

Default response codes

So by adding @ResponseStatus to our ControllerAdvice methods, we can see the related schemas associated with the HTTPStatus for each of the REST controller methods documentation. How neat is that?

Note: @ControllerAdvice also has some attributes (e.g. "assignableTypes"), which allow the @ControllerAdvice to be applied more specifically on controllers than globally. Currently, that behavior is not there yet, but please expect it to be there when the springdoc-openapi folks release their next version.

With the above, we have completed the two goals we had set out to achieve.

A little manual testing follows.

The POST Method

Java
 




xxxxxxxxxx
1
10


 
1
@RequestMapping(path = "/person", method = RequestMethod.POST)
2
    public Person person(@Valid @RequestBody Person person) {
3
         
4
         int nxt = ran.nextInt(10); 
5
         if(nxt>=5)
6
         {
7
             throw new RuntimeException("Breaking logic");
8
         }
9
        return person;
10
    }



This method is designed to have automatic validations. That was tested in the last article.

This method is also designed to cause exceptions randomly just to see how the controller's advice is invoked when things go wrong.

Let's test.

Click the green POST button. Then click the Try it out button. Then click the blue Execute button.

Parameters

Parameters

Note that the validation errors are crisper compared to the last article.

JSON
 
{
  "errors": [
    "age, must be greater than or equal to 18",
    "creditCardNumber, invalid credit card number",
    "email1, must be a well-formed email address",
    "email, Please provide a valid email address"
  ]
}


The validation errors are now crisper because of the logic in the controller advice.

Let's now feed it valid input. Showing here valid input for ready reference that can be copy-pasted. 

JSON
 




xxxxxxxxxx
1


 
1
{
2
  "firstName": "string",
3
  "lastName": "string",
4
  "email": "string@email1.com",
5
  "email1": "strin@email2.comg",
6
  "age": 18,
7
  "creditCardNumber": "4111111111111111"
8
}



You an also select from the Examples dropdown as shown below and press the blue Execute button.

POST parameters

POST parameters

This will cause either of the two below responses depending on whether the random exception occurs or not:

 server response

Server response


 server response

Server response


 Without the exception

 When the exception happened

Note again the response for the HTTP Status code of 500 is formatted as per logic in the ControllerAdvice.

The GET Method

Let's now examine the GET method in our controller.

Java
 




x


 
1
@RequestMapping(path = "/personByLastName", method = RequestMethod.GET)
2
    public List<Person> findByLastName(@RequestParam(name = "lastName", required = true)@NotNull
3
            @NotBlank
4
            @Size(max = 10)String lastName){
5
        List<Person> hardCoded= new ArrayList<>();
6
        //skipping the details
7
        return hardCoded;
8
        
9
    }



Also, note that the controller class is decorated with a @Validated annotation. 

There can be a few minor limitations. I will explore one of them without spending too much time on it. As we saw in our last article, as of now it does not fully cover all the JSR 303 annotations. There are other issues also some of which are not entirely due to Springdoc OpenAPI, but rather more likely due to the Swagger UI project itself.

Let's click the blue GET button followed by the Try it out button.

/personbyLastName

/personbyLastName

Let's feed in 12345678901 for the lastName input field and press the blue Execute button.

person-controller

Person controller

There can be some improvement in how the contract expected from the validations is displayed.

As discussed earlier, the GET URL can always be directly invoked using http://localhost:8080/personByLastName and http://localhost:8080/personByLastName?lastName=12345678901. Give it a try to see what the responses are. Play with the annotations @Validated, @RequestParam, @NotNull, @NotBlank, and @Size, and see how the handling or contract changes. A Spring developer who also is a user of the generated documentation will expect the documentation to correspond to his usage of these and other annotations. 

It's possible there are some minor areas where this may not be perfect but the bigger strong vital pieces are all there. It covers OpenAPI 3, even Webflux or OAuth 2. It's only a question of time when through contributions and feedback this can keep only getting better.

Conclusion

All said and done, for a fairly recent API, Springdoc OpenAPI has achieved quite a lot very elegantly. Impressive. And it does cover most of the important scenarios thoroughly.

We achieved our objective of realizing FQNs or fully qualified names for Java classes and also saw how to integrate the documentation with global exception handling leveraging Controller Advice.

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 Swagger UI, if you are unable to access the “Schema” 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.
  • Ensure you use http://localhost:8080/swagger-ui.html for this tutorial.
  • Source Code
  • Git Clone URL, Branch: springdoc-openapi-doingmore-updated1.

This article was interesting for me. Firstly there were some useful suggestions from readers. Which fortunately were rapidly incorporated in Springdoc. Also, I had been pushing for simplification of realizing FQNs. That has also been achieved which lead me to update this article. 

Please also read my next article on Springdoc and Swagger where we extend Swagger behavior using Swagger schema extensions and communicate via its automated documentation, information regarding other annotations, and custom validation constraint annotations - information that is not captured out of the box by Swagger and Springdoc.

Editor's Note: It's so great to see when authors and readers engage with one another like this. Our purpose at DZone ultimately is to bring developers together to make better software. We're so happy this happened here and hope it continues to do so.

Spring Framework Java (programming language) Annotation Apache Maven

Opinions expressed by DZone contributors are their own.

Related

  • Mastering Spring: Synchronizing @Transactional and @Async Annotations With Various Propagation Strategies
  • Composing Custom Annotations in Spring
  • Java, Spring Boot, and MongoDB: Performance Analysis and Improvements
  • Smart Dependency Injection With Spring: Assignability (Part 2 of 3)

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!