Contact us : contact@digitechgenai.com
REST APIs
Question 31. How do you create a REST API in Spring Boot?
A REST API (Representational State Transfer Application Programming Interface) allows different systems to communicate over HTTP by exposing endpoints. These endpoints are accessible via standard HTTP methods like GET
, POST
, PUT
, and DELETE
. REST APIs are widely used because they are lightweight, scalable, and platform-independent.
Step 1: Setting Up Spring Boot Project
- Create a Spring Boot Project:
- Go to Spring Initializr.
- Select the following:
- Project: Maven
- Language: Java
- Spring Boot Version: Choose the latest stable version.
- Dependencies:
- Spring Web (for building web applications)
- Spring Boot DevTools (for hot-reloading during development)
- H2 Database (optional, for in-memory testing)
- Click Generate to download the project.
- Import the Project:
- Open the project in your favorite IDE (IntelliJ IDEA, Eclipse, or VS Code).
- Ensure that all dependencies are resolved by running
mvn install
or using the IDE’s build tools.
Step 2: Define Project Structure
Organizing your code is critical for maintainability. Your project structure will look something like this:
src/main/java/com/example/demo
├── controller
├── service
├── repository
├── model
Each package serves a distinct purpose:
- Controller: Handles HTTP requests and responses.
- Service: Contains business logic.
- Repository: Interfaces with the database.
- Model: Represents the data structure.
Step 3: Create a REST API Example
Let’s build a simple REST API for managing a list of “Books”.
Step 3.1: Create the Book
Model
The model represents the structure of your data.
package com.example.demo.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String author;
public Book() {}
public Book(String title, String author) {
this.title = title;
this.author = author;
}
// Getters and Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
Step 3.2: Create the Repository
The repository is responsible for database interactions. Spring Data JPA simplifies this by providing.JpaRepository
package com.example.demo.repository;
import com.example.demo.model.Book;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface BookRepository extends JpaRepository<Book, Long> {
}
Step 3.3: Create the Service
The service contains the business logic for your application.
package com.example.demo.service;
import com.example.demo.model.Book;
import com.example.demo.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BookService {
@Autowired
private BookRepository bookRepository;
public List<Book> getAllBooks() {
return bookRepository.findAll();
}
public Book getBookById(Long id) {
return bookRepository.findById(id).orElseThrow(() -> new RuntimeException("Book not found"));
}
public Book saveBook(Book book) {
return bookRepository.save(book);
}
public void deleteBook(Long id) {
bookRepository.deleteById(id);
}
}
Step 3.4: Create the Controller
The controller handles HTTP requests and maps them to the service layer.
package com.example.demo.controller;
import com.example.demo.model.Book;
import com.example.demo.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/books")
public class BookController {
@Autowired
private BookService bookService;
@GetMapping
public List<Book> getAllBooks() {
return bookService.getAllBooks();
}
@GetMapping("/{id}")
public Book getBookById(@PathVariable Long id) {
return bookService.getBookById(id);
}
@PostMapping
public Book createBook(@RequestBody Book book) {
return bookService.saveBook(book);
}
@DeleteMapping("/{id}")
public void deleteBook(@PathVariable Long id) {
bookService.deleteBook(id);
}
}
Step 4: Test the REST API
- Run the Application:
- Right-click on your main class (e.g.,
DemoApplication
) and select Run.
- Right-click on your main class (e.g.,
- Access the Endpoints:
- Use tools like Postman or cURL to test the endpoints:
GET /api/books
: Fetch all books.GET /api/books/{id}
: Fetch a specific book by ID.POST /api/books
: Create a new book (pass JSON data in the body).DELETE /api/books/{id}
: Delete a book by ID.
- Use tools like Postman or cURL to test the endpoints:
- Example JSON for Creating a Book:
{
"title": "Spring Boot in Action",
"author": "Craig Walls"
}
Step 5: Advanced Concepts
1. Validation: Use @Valid
and @NotNull
annotations to validate input data. For example:
@NotNull(message = "Title is required")
private String title;
2. Pagination: Add pagination and sorting to your API using Spring Data JPA. For example:
@GetMapping
public Page<Book> getBooks(Pageable pageable) {
return bookRepository.findAll(pageable);
}
3. Error Handling: Implement global exception handling using. @ControllerAdvice
For instance:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<String> handleRuntimeException(RuntimeException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}
4. Security: Secure your API using Spring Security and OAuth2. This ensures that only authorized users can access specific endpoints.
5. Caching: Improve performance by adding caching mechanisms such as Redis or Ehcache.
Conclusion
By following this tutorial, you’ve learned how to create a REST API in Spring Boot. From setting up the project to handling HTTP requests, you’ve built a fully functional API for managing books.
Question 32. What is the difference between @GetMapping, @PostMapping, @PutMapping, and @DeleteMapping?
Spring Boot provides several annotations to handle HTTP requests in RESTful web services. Among these, @GetMapping
, @PostMapping
, @PutMapping
, and @DeleteMapping
are some of the most commonly used. Each of these annotations corresponds to a specific HTTP method and serves a unique purpose in handling requests.
Let’s break down each annotation in detail with examples
1. @GetMapping
The @GetMapping
annotation is used to handle HTTP GET
requests. These requests are typically used to retrieve data from the server without making any changes to the underlying data.
Example: Fetching a list of books
@RestController
@RequestMapping("/api/books")
public class BookController {
@GetMapping
public List<String> getAllBooks() {
return List.of("Book 1", "Book 2", "Book 3");
}
@GetMapping("/{id}")
public String getBookById(@PathVariable int id) {
return "Book " + id;
}
}
Explanation:
- The first
@GetMapping
maps to/api/books
and returns a list of books. - The second
@GetMapping
maps to/api/books/{id}
and retrieves a specific book based on its ID.
2. @PostMapping
The @PostMapping
annotation is used to handle HTTP POST
requests. These requests are typically used to create new resources on the server.
Example: Adding a new book
@RestController
@RequestMapping("/api/books")
public class BookController {
@PostMapping
public String addBook(@RequestBody String bookName) {
return "Book added: " + bookName;
}
}
Explanation:
- The
@PostMapping
maps to/api/books
and allows the client to send a new book name in the request body. - The server processes the request and responds with a confirmation message.
3. @PutMapping
The @PutMapping
annotation is used to handle HTTP PUT
requests. These requests are typically used to update an existing resource.
Example: Updating a book’s details
@RestController
@RequestMapping("/api/books")
public class BookController {
@PutMapping("/{id}")
public String updateBook(@PathVariable int id, @RequestBody String updatedBookName) {
return "Book " + id + " updated to: " + updatedBookName;
}
}
Explanation:
- The
@PutMapping
map to/api/books/{id}
and allows the client to update the name of a specific book by providing its ID and new name in the request body.
4. @DeleteMapping
The @DeleteMapping
annotation is used to handle HTTP DELETE
requests. These requests are typically used to delete a resource from the server.
Example: Deleting a book
@RestController
@RequestMapping("/api/books")
public class BookController {
@DeleteMapping("/{id}")
public String deleteBook(@PathVariable int id) {
return "Book " + id + " deleted.";
}
}
Explanation:
- The
@DeleteMapping
maps to/api/books/{id}
and allows the client to delete a specific book by providing its ID. - The server processes the request and confirms the deletion.
Key Differences
Annotation | HTTP Method | Purpose | Example Use Case |
---|---|---|---|
@GetMapping | GET | Retrieve data | Fetching a list of books |
@PostMapping | POST | Create new resources | Adding a new book |
@PutMapping | PUT | Update existing resources | Modifying user books |
@DeleteMapping | DELETE | Delete resources | Removing a book from the database |
Conclusion
The @GetMapping
, @PostMapping
, @PutMapping
, and @DeleteMapping
annotations in Spring Boot simplify the development of RESTful APIs by providing a clear and concise way to map HTTP methods to controller methods. Understanding their differences and appropriate use cases is crucial for building efficient and maintainable web services.
Question 33. How do you handle exceptions in a Spring Boot REST API?
Handling exceptions effectively is an important part of developing robust and user-friendly REST APIs. Spring Boot provides multiple ways to handle exceptions, ranging from basic exception handling to advanced global exception handling.
1. Why Handle Exceptions?
When building REST APIs, exceptions can occur due to various reasons, such as invalid inputs, database errors, or resource unavailability. Without proper handling, these exceptions can expose sensitive information or confuse users with unclear error messages. Exception handling ensures that your API returns meaningful and secure error responses.
2. Basic Exception Handling Using try-catch Blocks
For simple scenarios, you can use try-catch
blocks to handle exceptions locally within a method.
Example: Handling an exception in a controller
@RestController
@RequestMapping("/api/books")
public class BookController {
@GetMapping("/{id}")
public ResponseEntity<String> getBookById(@PathVariable int id) {
try {
// Simulate fetching a book
if (id <= 0) {
throw new IllegalArgumentException("Invalid book ID");
}
return ResponseEntity.ok("Book " + id);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
}
Explanation:
- The
try-catch
block handles specific exceptions, like.IllegalArgumentException
- Returns a
400 Bad Request
response with a meaningful error message.
3. Using @ExceptionHandler for Specific Exceptions
Spring Boot allows you to define methods to handle specific exceptions using the @ExceptionHandler
annotation.
Example: Handling specific exceptions
@RestController
@RequestMapping("/api/books")
public class BookController {
@GetMapping("/{id}")
public ResponseEntity<String> getBookById(@PathVariable int id) {
try {
// Simulate fetching a book
if (id <= 0) {
throw new IllegalArgumentException("Invalid book ID");
}
return ResponseEntity.ok("Book " + id);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
}
Explanation:
- The
@ExceptionHandler
method handlesIllegalArgumentException
globally for the controller. - Returns a
400 Bad Request
response with the exception message.
5. Global Exception Handling with @ControllerAdvice
For larger applications, you can centralize exception handling using the @ControllerAdvice
annotation. This approach applies exception handling logic across all controllers.
Example: Global exception handler
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleGenericException(Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("An unexpected error occurred: " + e.getMessage());
}
}
Explanation:
@ControllerAdvice
applies exception handling to all controllers.- Handles both specific (
IllegalArgumentException
) and generic (Exception
) exceptions. - Ensures consistent error responses across the application
5. Custom Exception Handling
For better clarity and maintainability, you can define custom exception classes.
Example: Custom exception and handler
// Custom exception
public class BookNotFoundException extends RuntimeException {
public BookNotFoundException(String message) {
super(message);
}
}
// Controller
@RestController
@RequestMapping("/api/books")
public class BookController {
@GetMapping("/{id}")
public String getBookById(@PathVariable int id) {
if (id != 1) {
throw new BookNotFoundException("Book not found with ID: " + id);
}
return "Book " + id;
}
}
// Global handler
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BookNotFoundException.class)
public ResponseEntity<String> handleBookNotFoundException(BookNotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage());
}
}
Explanation:
- This
BookNotFoundException
is a custom exception extending.RuntimeException
- The global exception handler ensures a
404 Not Found
response with a clear message.
6. Returning Standardized Error Responses
For a professional API, it’s important to return standardized error responses. You can create an error response model to achieve this.
Example: Standardized error response
// Error response model
public class ErrorResponse {
private String message;
private int status;
private LocalDateTime timestamp;
public ErrorResponse(String message, int status) {
this.message = message;
this.status = status;
this.timestamp = LocalDateTime.now();
}
// Getters and setters
}
// Global handler
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BookNotFoundException.class)
public ResponseEntity<ErrorResponse> handleBookNotFoundException(BookNotFoundException e) {
ErrorResponse errorResponse = new ErrorResponse(e.getMessage(), HttpStatus.NOT_FOUND.value());
return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
}
}
Explanation:
- The
ErrorResponse
model includes fields for the message, status, and timestamp. - The global handler uses this model to return a consistent error structure.
Conclusion
Exception handling in Spring Boot is flexible and powerful.Spring Boot provides all the tools you need to build reliable and user-friendly APIs. By adopting best practices such as standardized error responses, you can ensure a consistent and professional experience for API consumers.
Question 34. What is the use of ResponseEntity in Spring Boot?
In Spring Boot, the ResponseEntity
class is a powerful way to customize the HTTP response. It represents the entire HTTP response, including the status code, headers, and body. Using ResponseEntity
, you can have full control over the HTTP response returned to the client, making it an essential tool for building robust REST APIs.
Why Use ResponseEntity?
- Customizable Responses: Allows you to set HTTP status codes, headers, and body.
- Error Handling: Enables sending specific error messages with appropriate status codes.
- Flexibility: You can return any type of data (JSON, XML, plain text) in the response body.
- Standardized API: Helps create APIs that adhere to RESTful principles.
Basic Usage of ResponseEntity
Example 1: Returning a Simple Response
@RestController
@RequestMapping("/api")
public class DemoController {
@GetMapping("/greet")
public ResponseEntity<String> greetUser() {
return ResponseEntity.ok("Hello, Welcome to Spring Boot!");
}
}
Explanation:
- The
ResponseEntity.ok()
method is a shorthand for creating a response with the HTTP status200 OK
. - The response body contains the string “Hello, Welcome to Spring Boot!”.
Adding HTTP Headers
Example 2: Setting Custom Headers
@RestController
@RequestMapping("/api")
public class DemoController {
@GetMapping("/headers")
public ResponseEntity<String> customHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.add("Custom-Header", "CustomHeaderValue");
return ResponseEntity.ok()
.headers(headers)
.body("Response with custom headers");
}
}
Explanation:
HttpHeaders
is used to create and add custom headers.- The
ResponseEntity.ok()
method is used with theheaders()
method to include the custom headers in the response.
Returning Different HTTP Status Codes
Example 3: Returning 201 Created
for Resource Creation
@RestController
@RequestMapping("/api")
public class DemoController {
@PostMapping("/create")
public ResponseEntity<String> createResource() {
return ResponseEntity.status(HttpStatus.CREATED)
.body("Resource created successfully!");
}
}
Explanation:
ResponseEntity.status()
is used to set a specific HTTP status code, in this case,201 Created
.- The response body confirms the creation of the resource.
Error Handling with ResponseEntity
Example 4: Returning an Error Response
@RestController
@RequestMapping("/api")
public class DemoController {
@GetMapping("/error")
public ResponseEntity<String> handleError() {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body("Invalid request parameters");
}
}
Explanation:
HttpStatus.BAD_REQUEST
is used to return an HTTP400 Bad Request
status code.- The response body provides additional details about the error.
Using ResponseEntity with Objects
Example 5: Returning JSON Data
@RestController
@RequestMapping("/api")
public class DemoController {
@GetMapping("/user")
public ResponseEntity<User> getUser() {
User user = new User(1, "John Doe", "john.doe@example.com");
return ResponseEntity.ok(user);
}
}
class User {
private int id;
private String name;
private String email;
// Constructor, Getters, and Setters
public User(int id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
// Getters and Setters
}
Explanation:
- The
User
object is serialized into JSON and sent as the response body. - The
ResponseEntity.ok()
method ensures the HTTP status is200 OK
.
Advanced Usage: Builder Pattern
ResponseEntity
supports a builder pattern for more complex responses.
Example 6: Combining Status, Headers, and Body
@RestController
@RequestMapping("/api")
public class DemoController {
@GetMapping("/complex")
public ResponseEntity<String> complexResponse() {
return ResponseEntity.status(HttpStatus.ACCEPTED)
.header("Custom-Header", "HeaderValue")
.body("Complex response with status, headers, and body");
}
}
Explanation:
- Combines HTTP status, headers, and body in a single response.
Key Benefits of ResponseEntity
- Comprehensive Control: You can control every aspect of the HTTP response.
- RESTful Compliance: Helps build APIs that follow REST principles by explicitly setting status codes and response details.
- Error Management: Simplifies returning appropriate error responses.
Conclusion
The ResponseEntity
class is a versatile and powerful tool in Spring Boot for handling HTTP responses. By using it effectively, you can build APIs that are robust, flexible. Whether you’re setting custom headers, returning error messages, or sending JSON data, ResponseEntity
makes it straightforward to manage responses in your application.
Question 35. How do you secure a REST API in Spring Boot?
Securing a REST API in Spring Boot is a critical step in ensuring that your application is protected from unauthorized access and malicious attacks. Spring Boot provides robust tools for implementing security, primarily through Spring Security, which is a powerful and customizable framework.
This tutorial will guide you step-by-step, from the basics to advanced concepts, on how to secure your REST API in Spring Boot.
Why Secure a REST API?
- Prevent Unauthorized Access: Ensure only authenticated users can access your API.
- Protect Sensitive Data: Encrypt and secure data transmitted between clients and servers.
- Maintain Integrity: Prevent tampering with data during communication.
- Ensure Accountability: Log and track user activities for compliance and auditing.
Step 1: Add Spring Security to Your Project
To secure your REST API, you need to include Spring Security in your project. Add the following dependency to your pom.xml
if you’re using Maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
For Gradle, add this to your build.gradle
:
implementation 'org.springframework.boot:spring-boot-starter-security'
Step 2: Configure Basic Authentication
By default, Spring Security enables basic authentication. Once you add the dependency, your application will require a username and password for every endpoint.
Default Credentials
Spring Boot generates a default username (user
) and a random password, which is displayed in the console when the application starts. Look for a line like this:
Using generated security password: <random-password>
Step 3: Customizing the Username and Password
To customize the default username and password, add the following properties to your application.properties
file:
spring.security.user.name=admin
spring.security.user.password=admin123
Now, your API will require these credentials for authentication.
Step 4: Secure Specific Endpoints
You can configure which endpoints require authentication and which are publicly accessible. Create a SecurityConfig
class to customize Spring Security settings:
Example: Configuring Endpoint Security
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeHttpRequests()
.requestMatchers("/api/public/**").permitAll() // Public endpoints
.anyRequest().authenticated() // All other endpoints require authentication
.and()
.httpBasic(); // Enables basic authentication
return http.build();
}
}
Explanation:
csrf().disable()
: Disables CSRF protection (use cautiously; enable it if needed).requestMatchers("/api/public/**").permitAll()
: Allows public access to specific endpoints.anyRequest().authenticated()
: Requires authentication for all other endpoints.httpBasic()
: Enables basic authentication.
Step 5: Use Role-Based Authorization
You can assign roles to users and restrict access based on roles.
Example: Role-Based Authorization
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeHttpRequests()
.requestMatchers("/api/admin/**").hasRole("ADMIN") // Only ADMIN role can access
.requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN") // USER or ADMIN roles can access
.anyRequest().authenticated()
.and()
.httpBasic();
return http.build();
}
Step 6: Implement JWT Authentication (Advanced)
For stateless authentication, you can use JSON Web Tokens (JWT). JWT allows secure, stateless communication between clients and servers.
Add Dependencies
Add the following dependencies to your pom.xml
:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
Generate JWT Tokens
Create a utility class to generate and validate JWT tokens:
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
public class JwtUtil {
private final String SECRET_KEY = "mysecretkey";
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10 hours
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public String extractUsername(String token) {
return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody().getSubject();
}
}
Secure Endpoints with JWT
Update your SecurityConfig
to include JWT filters.
Step 7: Testing Your Secure API
Use tools like Postman or cURL to test your secured endpoints.
Testing Basic Authentication
curl -u admin:admin123 http://localhost:8080/api/private
Testing JWT Authentication
- Obtain a JWT token by calling the login endpoint.
- Use the token in the
Authorization
header for subsequent requests.
Conclusion
Securing a REST API in Spring Boot involves several layers, from basic authentication to advanced JWT-based stateless authentication. By following the steps outlined in this tutorial, you can ensure that your API is protected and adheres to best practices for security.
Question 36. What is CORS, and how do you enable it in Spring Boot?
CORS (Cross-Origin Resource Sharing) is a security feature implemented by web browsers to prevent unauthorized access to resources on a server from a different domain. When a client (e.g., a web application running on http://example.com
) tries to access resources from a server hosted on another domain (e.g., http://api.example.com
), the browser enforces CORS policies to determine whether the request is allowed.
Why is CORS Important?
- Security: Prevents unauthorized access to resources from malicious domains.
- Control: Allows servers to define which origins (domains) are permitted to access their resources.
- Flexibility: Enables legitimate cross-origin requests, such as those required by Single Page Applications (SPAs) consuming APIs.
How Does CORS Work?
When a browser sends a cross-origin request, it performs a CORS preflight check for certain types of requests. This involves sending an HTTP OPTIONS
request to the server to verify:
- The allowed HTTP methods (
GET
,POST
, etc.). - The allowed headers (
Authorization
,Content-Type
, etc.). - Whether credentials (cookies or HTTP authentication) can be included.
If the server responds with appropriate CORS headers, the browser allows the request. Otherwise, it blocks the request.
Enabling CORS in Spring Boot
Spring Boot makes it simple to configure CORS for your REST APIs. Let’s explore various ways to enable CORS, starting from basic configurations to more advanced use cases.
1. Enable CORS Globally
To enable CORS for all endpoints in your application, you can define a CorsConfiguration
bean.
Example: Global CORS Configuration
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class GlobalCorsConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("http://example.com"); // Allow specific origin
config.addAllowedOrigin("http://another-domain.com"); // Allow another origin
config.addAllowedMethod("*"); // Allow all HTTP methods (GET, POST, PUT, DELETE, etc.)
config.addAllowedHeader("*"); // Allow all headers
config.setAllowCredentials(true); // Allow cookies or credentials
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config); // Apply to all endpoints
return new CorsFilter(source);
}
}
Explanation:
addAllowedOrigin
: Specifies which domains can access the API.addAllowedMethod
: Specifies which HTTP methods are allowed (e.g.,GET
,POST
).addAllowedHeader
: Specifies which headers the client can include in the request.setAllowCredentials
: Enables cookies or authentication headers in cross-origin requests.
2. Enable CORS for Specific Endpoints
You can configure CORS at the controller or method level using the @CrossOrigin
annotation.
Example: CORS for Specific Endpoints
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class ApiController {
@CrossOrigin(origins = "http://example.com", methods = {RequestMethod.GET, RequestMethod.POST})
@GetMapping("/data")
public String getData() {
return "CORS-enabled endpoint";
}
}
Explanation:
@CrossOrigin
: Enables CORS for the specific endpoint.origins
: Specifies the allowed domains.methods
: Specifies the allowed HTTP methods.
3. Enable CORS Using Spring Security
If your application uses Spring Security, you need to explicitly configure CORS in the security configuration.
Example: CORS with Spring Security
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.cors().and()
.csrf().disable()
.authorizeHttpRequests()
.anyRequest().authenticated();
return http.build();
}
}
Additionally, define a CorsConfigurationSource
bean:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.cors.CorsConfigurationSource;
@Configuration
public class CorsConfig {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("http://example.com");
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
}
5.Testing CORS
To test your CORS configuration:
- Use tools like Postman to make cross-origin requests.
- Open the browser console and check for CORS errors when making requests from a different domain.
Example: Using cURL
curl -X GET -H "Origin: http://example.com" http://localhost:8080/api/data
Common Issues and Troubleshooting
- CORS Preflight Requests Failing:
- Ensure the server responds to
OPTIONS
requests. - Configure
addAllowedMethod("OPTIONS")
.
- Ensure the server responds to
- Credentials Not Working:
- Set
setAllowCredentials(true)
on the server. - Ensure the client includes credentials in the request.
- Set
- Wildcard in Allowed Origins:
- Avoid using
"*"
foraddAllowedOrigin
ifsetAllowCredentials(true)
is enabled.
- Avoid using
Conclusion
CORS is an essential aspect of securing and enabling cross-origin communication in modern web applications. By leveraging Spring Boot’s flexible configuration options, you can easily control which domains can access your API, ensuring both security and usability.
Whether you’re a beginner or an advanced developer, understanding and implementing CORS is a critical skill for building robust REST APIs.
Question 37. How do you test REST APIs in Spring Boot?
Testing REST APIs in Spring Boot is essential to ensure that your application behaves as expected and meets all functional requirements. Spring Boot provides a robust testing framework, making it easier to write and execute tests for your RESTful APIs. Lets explore testing REST APIs from beginner -level concepts to advanced techniques.
1. Understanding the Testing Framework in Spring Boot
Spring Boot supports several testing tools and libraries, including:
- JUnit: The most popular Java testing framework.
- Spring Boot Test: Provides features to test Spring Boot applications.
- MockMvc: A class provided by Spring to test controllers without starting the full application.
- RestTemplate: Useful for integration testing by making actual HTTP calls.
- Testcontainers: For advanced integration testing with real databases.
2. Types of Tests for REST APIs
- Unit Testing: Focuses on testing individual components (e.g., controllers or services) in isolation.
- Integration Testing: Verifies that the components work together, often involving the database or external services.
- End-to-End Testing: Simulates real-world usage by testing the entire application stack.
3. Setting Up Your Testing Environment
Before writing tests, ensure you have the necessary dependencies in your pom.xml
file (for Maven projects):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Note : This dependency includes JUnit, Mockito, and other testing utilities.
4. Writing Unit Tests for Controllers
Unit tests ensure that your REST API endpoints behave correctly
Example – Unit test using MockMvc
Controller Code
@RestController
@RequestMapping("/api/books")
public class BookController {
@GetMapping("/{id}")
public ResponseEntity<String> getBookById(@PathVariable int id) {
if (id == 1) {
return ResponseEntity.ok("Spring Boot with DigiTech GenAI");
} else {
return ResponseEntity.notFound().build();
}
}
}
Unit Test Code
@WebMvcTest(BookController.class)
public class BookControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testGetBookById_Found() throws Exception {
mockMvc.perform(get("/api/books/1"))
.andExpect(status().isOk())
.andExpect(content().string("Spring Boot with DigiTech GenAI"));
}
@Test
public void testGetBookById_NotFound() throws Exception {
mockMvc.perform(get("/api/books/2"))
.andExpect(status().isNotFound());
}
}
Explanation:
@WebMvcTest
: Loads only the web layer for testing.MockMvc
: Simulates HTTP requests and verifies responses.perform()
: Executes a mock HTTP request.andExpect()
: Verifies the response status and content.
5. Writing Integration Tests
Integration tests validate the interaction between different layers, such as controllers and databases.
Controller Code with Database
@RestController
@RequestMapping("/api/books")
public class BookController {
@Autowired
private BookService bookService;
@GetMapping("/{id}")
public ResponseEntity<Book> getBookById(@PathVariable Long id) {
return bookService.getBookById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
}
Integration Test Code
@SpringBootTest
@AutoConfigureMockMvc
public class BookControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private BookRepository bookRepository;
@BeforeEach
public void setup() {
bookRepository.save(new Book(1L, "Spring Boot with DigiTech GenAI"));
}
@AfterEach
public void cleanup() {
bookRepository.deleteAll();
}
@Test
public void testGetBookById_Found() throws Exception {
mockMvc.perform(get("/api/books/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.title").value("Spring Boot with DigiTech GenAI"));
}
}
Explanation:
@SpringBootTest
: Loads the entire application context.@AutoConfigureMockMvc
: ConfiguresMockMvc
for integration testing.jsonPath()
: Verifies JSON fields in the response.
6. Advanced Testing with Testcontainers
For realistic integration testing, you can use Testcontainers to spin up a real database in a Docker container.
Example with PostgreSQL
@Testcontainers
@SpringBootTest
public class BookRepositoryTest {
@Container
public static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:latest")
.withDatabaseName("testdb")
.withUsername("testuser")
.withPassword("testpass");
@Autowired
private BookRepository bookRepository;
@Test
public void testSaveBook() {
Book book = new Book(null, "Spring Boot with DigiTech GenAI");
Book savedBook = bookRepository.save(book);
assertNotNull(savedBook.getId());
}
}
Explanation:
@Testcontainers
: Enables Testcontainers in your test.PostgreSQLContainer
: Spins up a PostgreSQL instance in Docker.- The test runs against a real database, providing more accurate results.
7.Tips for Effective Testing
- Use meaningful test names that describe the scenario.
- Mock external dependencies using Mockito.
- Keep tests isolated and independent.
- Write tests for edge cases, such as invalid inputs or server errors.
- Measure test coverage to ensure critical code paths are tested.
Conclusion
Testing REST APIs in Spring Boot is a crucial part of the development process. By leveraging tools like MockMvc
, RestTemplate
, and Testcontainers, you can write comprehensive tests that cover all aspects of your application. Start with unit tests to validate individual components, then move to integration and end-to-end tests for a complete evaluation. With these practices, you can ensure your APIs are robust, reliable, and ready for production use.
Question 38. How do you implement versioning in a Spring Boot REST API?
Versioning is a critical aspect of designing RESTful APIs, especially as your application evolves and new features are added. It allows you to maintain backward compatibility while introducing changes, ensuring older clients can still use the API without breaking.
Let’s explore different ways to implement versioning in a Spring Boot REST API. We’ll start with basic concepts and move to advanced techniques.
1. Why is Versioning Important?
APIs are consumed by various clients, such as web applications, mobile apps, and third-party integrations. When you update your API, older clients might not be compatible with the changes. Versioning provides a way to:
- Introduce new features or changes without disrupting existing clients.
- Maintain backward compatibility for older versions.
- Allow clients to migrate to newer versions at their own pace.
2. Approaches to API Versioning
There are several ways to version a REST API:
- URI Versioning (Path-based)
- Query Parameter Versioning
- Header Versioning
- Media Type Versioning (a.k.a. Content Negotiation)
Let’s implement each of these approaches step by step.
3. URI Versioning
In this method, the version is included as part of the URL path. This is the simplest and most commonly used approach.
Implementation
@RestController
@RequestMapping("/api/v1/books")
public class BookControllerV1 {
@GetMapping
public List<String> getAllBooks() {
return List.of("Book 1", "Book 2", "Book 3");
}
}
@RestController
@RequestMapping("/api/v2/books")
public class BookControllerV2 {
@GetMapping
public List<String> getAllBooks() {
return List.of("Advanced Book 1", "Advanced Book 2");
}
}
Explanation:
/api/v1/books
: Represents version 1 of the API./api/v2/books
: Represents version 2 of the API with updated functionality.
Pros:
- Easy to implement and understand.
- Explicit versioning in the URL.
Cons:
- May lead to cluttered URLs if many versions exist.
4. Query Parameter Versioning
In this method, the version is passed as a query parameter.
Implementation
@RestController
@RequestMapping("/api/books")
public class BookController {
@GetMapping
public List<String> getBooksByVersion(@RequestParam(value = "version", defaultValue = "1") String version) {
if ("2".equals(version)) {
return List.of("Advanced Book 1", "Advanced Book 2");
}
return List.of("Book 1", "Book 2", "Book 3");
}
}
Explanation:
/api/books?version=1
: Fetch version 1 data./api/books?version=2
: Fetch version 2 data.
Pros:
- No need to change the URL structure.
- Flexible for clients to specify the version dynamically.
Cons:
- Versioning logic can become complex in the controller.
5. Header Versioning
In this approach, the version is specified in the request header
Implementation
@RestController
@RequestMapping("/api/books")
public class BookController {
@GetMapping
public List<String> getBooksByHeader(@RequestHeader(value = "API-Version", defaultValue = "1") String version) {
if ("2".equals(version)) {
return List.of("Advanced Book 1", "Advanced Book 2");
}
return List.of("Book 1", "Book 2", "Book 3");
}
}
Explanation:
- Add a custom header
API-Version
in the request:API-Version: 1
API-Version: 2
Pros:
- Keeps URLs clean.
- Suitable for APIs used by controlled clients, like mobile apps.
Cons:
- Harder to test manually using a browser.
- Requires clients to set custom headers.
6. Media Type Versioning (Content Negotiation)
Here, the version is included in the Accept
header as part of the media type.
Implementation
@RestController
@RequestMapping("/api/books")
public class BookController {
@GetMapping(produces = "application/vnd.example.v1+json")
public List<String> getBooksV1() {
return List.of("Book 1", "Book 2", "Book 3");
}
@GetMapping(produces = "application/vnd.example.v2+json")
public List<String> getBooksV2() {
return List.of("Advanced Book 1", "Advanced Book 2");
}
}
Explanations:
- Set the
Accept
header in the request:Accept: application/vnd.example.v1+json
Accept: application/vnd.example.v2+json
Pros:
- Allows versioning based on content type.
- Suitable for APIs with complex response formats.
Cons:
- Harder to implement and understand.
- Testing requires tools like Postman or curl.
7. Advanced Techniques: Using a Versioning Strategy
You can centralize versioning logic using a custom HandlerMapping
.
Example: Centralized Versioning Strategy
@Configuration
public class ApiVersioningConfig {
@Bean
public WebMvcRegistrations webMvcRegistrationsHandlerMapping() {
return new WebMvcRegistrations() {
@Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new VersionedRequestMappingHandlerMapping();
}
};
}
}
public class VersionedRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
@Override
protected boolean isHandler(Class<?> beanType) {
return AnnotationUtils.findAnnotation(beanType, RestController.class) != null;
}
@Override
protected String getMappingForMethod(Method method, Class<?> handlerType) {
ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
if (apiVersion != null) {
return apiVersion.value();
}
return super.getMappingForMethod(method, handlerType);
}
}
Explanation:
- Define a custom annotation like
@ApiVersion
. - Implement logic to route requests based on the annotation.
8. Choosing the Right Approach
Approach | Best For | Example Use Case |
---|---|---|
URI Versioning | Simple APIs with fewer versions | Public APIs with clear versioning |
Query Parameter | Dynamic version selection | Internal APIs |
Header Versioning | Clean URLs | APIs for mobile apps or controlled clients |
Media Type Versioning | Advanced content negotiation | Complex APIs with varying response formats |
Conclusion
Versioning is a vital part of building scalable and maintainable REST APIs in Spring Boot. Depending on your use case, you can choose from URI versioning, query parameters, headers, or media type versioning. By implementing proper versioning, you ensure your APIs remain robust, flexible, and client-friendly.
Question 39. How do you handle file uploads in Spring Boot?
Uploading files is a common requirement in web applications. Spring Boot provides robust support for handling file uploads using Spring MVC and the MultipartFile
interface. Let’s walk through a step-by-step guide to implementing file uploads in a Spring Boot application.
1. Setting Up the Project
Create a Spring Boot project with the following dependencies:
- Spring Web (for building REST APIs)
- Spring Boot Starter Thymeleaf (optional, for handling file uploads via a frontend)
- Spring Boot Starter Test (for testing file uploads)
For Maven, add these dependencies to your pom.xml
:
<dependencies>
<!-- Spring Boot Web dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Starter Thymeleaf (for frontend, optional) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Spring Boot Test (for testing) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2. Configuring File Upload Settings
Spring Boot has built-in support for file uploads, but you can customize settings in application.properties
:
# Maximum file size allowed (10MB)
spring.servlet.multipart.max-file-size=10MB
# Maximum request size allowed (20MB)
spring.servlet.multipart.max-request-size=20MB
Note: This ensures that large files do not exceed the allowed limit
3. Create a REST API for File Upload
Create a Spring Boot controller to handle file uploads using the MultipartFile
interface
Controller Implementation
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpStatus;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
@RestController
@RequestMapping("/api/files")
public class FileUploadController {
private static final String UPLOAD_DIR = "uploads/";
@PostMapping("/upload")
public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Please select a file to upload.");
}
try {
// Ensure the directory exists
Files.createDirectories(Paths.get(UPLOAD_DIR));
// Define the file path
Path filePath = Paths.get(UPLOAD_DIR + file.getOriginalFilename());
// Save the file to the defined path
Files.write(filePath, file.getBytes());
return ResponseEntity.ok("File uploaded successfully: " + file.getOriginalFilename());
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("File upload failed: " + e.getMessage());
}
}
}
Explanation
- The method accepts a file via
@RequestParam("file") MultipartFile file
. - It validates if a file is selected.
- It creates the uploads directory if it does not exist.
- It writes the file to the
uploads/
folder usingFiles.write()
. - It returns an appropriate response based on success or failure.
4. Creating a Frontend for File Upload (Optional)
If you want to test this with a UI, create an HTML page using Thymeleaf:
file_upload.html
<!DOCTYPE html>
<html>
<head>
<title>File Upload</title>
</head>
<body>
<h2>Upload a File</h2>
<form method="POST" enctype="multipart/form-data" action="/api/files/upload">
<input type="file" name="file" required>
<button type="submit">Upload</button>
</form>
</body>
</html>
Explanation
- The form uses
multipart/form-data
encoding. - The input field allows users to select a file.
- The form submits the file to
/api/files/upload
.
5. Testing the File Upload API
You can test the file upload feature using Postman or cURL.
Using Postman
- Open Postman.
- Select POST request.
- Enter the API URL:
http://localhost:8080/api/files/upload
. - In the Body section, choose
form-data
- .Add a key as
file
and select a file from your system. - Click Send.
Using cURL
Run the following command:
curl -X POST -F "file=@/path/to/your/file.jpg" http://localhost:8080/api/files/upload
If successful, the response should be:
File uploaded successfully: file.jpg
6. Handling Multiple File Uploads
If you want to upload multiple files at once, modify your controller method like this:
@PostMapping("/upload-multiple")
public ResponseEntity<String> uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) {
if (files.length == 0) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Please select at least one file.");
}
for (MultipartFile file : files) {
try {
Path filePath = Paths.get(UPLOAD_DIR + file.getOriginalFilename());
Files.write(filePath, file.getBytes());
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Failed to upload: " + file.getOriginalFilename());
}
}
return ResponseEntity.ok("All files uploaded successfully.");
}
Frontend Form for Multiple Uploads
<form method="POST" enctype="multipart/form-data" action="/api/files/upload-multiple">
<input type="file" name="files" multiple required>
<button type="submit">Upload Files</button>
</form>
8. Store Files in a Database (Advanced)
Instead of storing files in the file system, you can save them in a database as BLOBs (Binary Large Objects).
Entity Class
import jakarta.persistence.*;
@Entity
public class FileEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String fileName;
@Lob
private byte[] fileData;
// Getters and Setters
}
Repository
import org.springframework.data.jpa.repository.JpaRepository;
public interface FileRepository extends JpaRepository<FileEntity, Long> {
}
Service for Saving File Data
@Service
public class FileService {
@Autowired
private FileRepository fileRepository;
public FileEntity storeFile(MultipartFile file) throws IOException {
FileEntity fileEntity = new FileEntity();
fileEntity.setFileName(file.getOriginalFilename());
fileEntity.setFileData(file.getBytes());
return fileRepository.save(fileEntity);
}
}
8. Conclusion
- Spring Boot makes file uploads simple with
MultipartFile
. - You can store files in the file system or database.
- You can use tools like Postman and cURL to test APIs.
- You can expand this by adding file validation, size restrictions, and security measures.
Question 40. What is the use of Swagger in Spring Boot?
When developing RESTful APIs in Spring Boot, documentation is essential to help developers understand how to interact with your API. Swagger is a powerful tool that simplifies API documentation and testing.
In this tutorial, we’ll go from beginner to advanced, covering:
- What Swagger is and why it’s useful
- How to integrate Swagger into a Spring Boot project
- How to customize and secure Swagger
1. What is Swagger?
- Automatic API documentation with detailed endpoints.
- An interactive UI where developers can test APIs directly in the browser.
- Code generation support for various programming languages
Why Use Swagger?
- Reduces manual API documentation effort.
- Makes API testing easy with an interactive UI.
- Improves collaboration between frontend and backend teams.
- Helps generate API clients in different languages
2. Setting Up Swagger in a Spring Boot Project
To use Swagger in a Spring Boot project, follow these steps.
Step 1: Add Dependencies
For Maven, add the following dependency to pom.xml
:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.0.2</version>
</dependency>
For Gradle, add this to build.gradle
:
dependencies {
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'
}
Step 2: Create a Simple REST API
controller
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Arrays;
@RestController
@RequestMapping("/api/products")
public class ProductController {
@GetMapping
public List<String> getAllProducts() {
return Arrays.asList("Laptop", "Smartphone", "Tablet");
}
@GetMapping("/{id}")
public String getProductById(@PathVariable int id) {
List<String> products = Arrays.asList("Laptop", "Smartphone", "Tablet");
return products.get(id);
}
}
Step 3: Access Swagger UI
Once the application is running, open Swagger UI in your browser:
http://localhost:8080/swagger-ui.html
3. Customizing Swagger
By default, Swagger scans all REST APIs, but you can customize its behavior.
Step 1: Create an API Configuration Class
You can customize API documentation using @OpenAPIDefinition
.
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;
import org.springframework.context.annotation.Configuration;
@Configuration
@OpenAPIDefinition(
info = @Info(
title = "Product API",
version = "1.0",
description = "API for managing products"
)
)
public class SwaggerConfig {
}
Note: This will change the title, version, and description of your API.
Step 2: Adding API Descriptions
By use Swagger annotations to provide more details.
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Arrays;
@RestController
@RequestMapping("/api/products")
@Tag(name = "Product API", description = "Operations related to products")
public class ProductController {
@Operation(summary = "Get all products", description = "Fetches a list of all available products")
@GetMapping
public List<String> getAllProducts() {
return Arrays.asList("Laptop", "Smartphone", "Tablet");
}
@Operation(summary = "Get product by ID", description = "Fetches details of a single product by its ID")
@GetMapping("/{id}")
public String getProductById(@PathVariable int id) {
List<String> products = Arrays.asList("Laptop", "Smartphone", "Tablet");
return products.get(id);
}
}
Note : Now, when you open Swagger UI, you’ll see detailed documentation for each endpoint.
4. Securing Swagger
In production, you might want to restrict Swagger access to only admins or disable it completely.
Option 1: Restrict Access to Swagger
You can configure Spring Security to restrict access to the Swagger UI.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin()
.and()
.logout();
return http.build();
}
}
Now, only users with the ADMIN role can access Swagger.
Option 2: Disable Swagger in Production
To disable Swagger in production, modify application.properties
:
springdoc.api-docs.enabled=false
springdoc.swagger-ui.enabled=false
Or disable it programmatically:
@Configuration
public class SwaggerDisableConfig {
@Bean
public GroupedOpenApi disableSwagger() {
return GroupedOpenApi.builder()
.group("hidden")
.pathsToExclude("/v3/api-docs/**", "/swagger-ui/**")
.build();
}
}
5. Generating API Documentation as JSON or YAML
Swagger allows you to generate API specs in JSON or YAML format.
JSON format:
http://localhost:8080/v3/api-docs
YAML format:
http://localhost:8080/v3/api-docs.yaml
Note : This is useful for generating client SDKs or sharing API specs.
Conclusion
Swagger simplifies API documentation and testing in Spring Boot.
- Automatically generates REST API documentation
- Provides an interactive UI for testing APIs
- Allows customization using annotations
- Can be secured in production
- Supports JSON/YAML exports for integration with other tools