The other day I was supporting a client in my day job by helping him use Postman to generate HTTP Post requests. The problem was, that his language of choice was Dart, a language I had never heard of, much less used. I’ve worked with Postman for quite some time, but I never clicked this discrete link to the right of the request.
Imagine my surprise when I clicked that link and a code snippet showing the Dart code.
It generates cURL requests,
Python code snippets,
Node.js code snippets,
and many more. For example, Postman can generate snippets for over 30 different languages or frameworks.
How to Generate Python Code Snippets
Create the request you desire, for example, the following is a simple GET request listing available APIs on the API Guru website.
Click the small Code generate button.
Select your desired language or framework.
Click the copy icon to copy the code snippet to your clipboard.
Here’s a video by a member of the Postman team showing how to use the code snippet generation feature.
I’ve been working as the Developer Evangelist for DynamicPDF for some time now. As well as our desktop/server product, we offer a cloud version of our software: DynamicPDF Cloud API. It’s a powerful cloud API for creating and manipulating PDFs for your business. In this post, I outline some of its features and why you should use it if you create PDFs for your organization.
The cloud API is built on the powerful DynamicPDF Core Suite, a software platform that has been used for several decades by many of the world’s largest corporations.
Returns a PDF after performing one of the PDF endpoint’s tasks (page, dlex, html, word, image) or merging.
REST Endpoint documentation
DynamicPDF Designer Online
DynamiciPDF Cloud API also offers the following client libraries to make using the endpoints easier.
C#
Java
Node.js
PHP
Go
Python
It also offers – arguably the most powerful tool available anywhere online – DynamicPDF Designer Online, a graphical tool for creating rich PDF documents using your organization’s business data.
The API is free to try, and the documentation and support are top-notch. I should know – I wrote most the documentation, tutorials, and example code. There are tons of tutorials, and our support is great.
This tutorial might leave you wanting more. Rather than giving you explicitif this then do that advice, I show you three different techniques you might use for handling Spring Boot 2 REST Exceptions. Those of you with experience might ask why even bother, as Spring Boot handles exceptions and presents a nice REST response by default. However, there are instances where you might require customizing exception handling, and this tutorial demonstrates three techniques. As with the other tutorials on this site, the caveat emptor applies…if you follow this tutorial with a different version of Spring Boot, or worse, Spring without the Boot, then be prepared to do further research, as Spring Boot 2’s primary purpose is to simplify Spring development. With simplification, many of the implementation details become hidden.
There are three ways we can handle exceptions using Spring Boot 2 Rest Exceptions: the default handling, exception handling in the controller, or global exception handling. In this tutorial we explore all three ways of handling exceptions.
Project Setup
Before beginning, create your Spring Boot application. If you are new to Spring Boot then you should refer to one of the tutorials here, or on the web before attempting this tutorial. This tutorial assumes you can create, compile, and run a Spring Boot REST application. It also assumes you know how to call a REST endpoint.
The response is not very helpful when an incorrect value for type is passed to the rest endpoint. Moreover, the response will likely result in a client application throwing a NullPointerException, as both greeting and goodbye are null. Instead, we should throw an exception when an incorrect value is passed to the endpoint.
As an aside, yes, HelloGoodbye is poorly designed. Returning a null is bad programming practice. A better option would be to do something as follows. But, creating well-designed pojos is not this tutorial’s intention. Instead, go with the poorly designed HelloGoodbye implementation above.
public class HelloGoodbye {
private String message;
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getMessage() {
return message;
}
public void setMessage(String msg) {
this.message = msg;
}
}
Default Exception Handling
Spring Boot provides exception handling by default. This makes it much easier for both the service endpoint and client to communicate failures without complex coding.
Modify createGreeting to throw an Exception if type is not the value hello or goodbye.
package com.tutorial.exceptions.spring.rest.exceptionstutorial;
import org.springframework.stereotype.Service;
@Service
public class GreetingService {
public HelloGoodbye createGreeting(String type) throws Exception {
HelloGoodbye helloGoodbye = new HelloGoodbye();
if (type.equals("hello")) {
helloGoodbye.setGreeting("Hello there.");
} else if (type.equals("goodbye")) {
helloGoodbye.setGoodbye("Goodbye for now.");
} else {
throw new Exception("Valid types are hello or goodbye.");
}
helloGoodbye.setType(type);
return helloGoodbye;
}
}
Modify GreetingControllergetGreeting to throw an Exception.
Compile, run the application, and visit the rest endpoint. Note the response returns the error as json.
{
"timestamp": "2019-04-06T18:07:34.344+0000",
"status": 500,
"error": "Internal Server Error",
"message": "Valid types are hello or goodbye.",
"path": "/greeting/greet"
}
When changing the createGreeting method we were required to either catch the exception or throw it. This is because Exception is a checked exception (more on checked exceptions). But there were no special requirements for returning that exception to a client application as JSON. This is because Spring Boot provides a default JSON error message for errors. The relevant class is DefaultErrorAttributes which implements the ErrorAttributes interface. This class provides the following attributes when an exception occurs: timestamp, status, error, exception, message, errors, trace, and path. You can easily override the default with your own error attributes class; however, this technique is not illustrated here. Refer to this tutorial for more information on writing a custom implementation of the ErrorAttributes interface (Customize error JSON response with ErrorAttributes).
Usually, business logic exceptions warrant a business logic exception rather than a generic exception. Let’s modify the code to throw a custom exception.
Create a class named GreetingTypeException that extends Exception.
Assign it an bad request status through the @ResponseStatus annotation.
package com.tutorial.exceptions.spring.rest.exceptionstutorial;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.http.HttpStatus;
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public class GreetingTypeException extends Exception {
private static final long serialVersionUID = -189365452227508599L;
public GreetingTypeException(String message) {
super(message);
}
public GreetingTypeException(Throwable cause) {
super(cause);
}
public GreetingTypeException(String message, Throwable cause)
{
super(message, cause);
}
}
Modify createGreeting to throw a GreetingTypeException rather than an Exception.
public HelloGoodbye createGreeting(String type) throws GreetingTypeException {
HelloGoodbye helloGoodbye = new HelloGoodbye();
if (type.equals("hello")) {
helloGoodbye.setGreeting("Hello there.");
} else if (type.equals("goodbye")) {
helloGoodbye.setGoodbye("Goodbye for now.");
} else {
throw new GreetingTypeException("Valid types are hello or goodbye.");
}
helloGoodbye.setType(type);
return helloGoodbye;
}
Compile, run the application, and visit the rest endpoint. Assign an incorrect value to the type parameter.
http://localhost:8080/greeting/greet?type=cc
{
"timestamp": "2019-03-29T01:54:40.114+0000",
"status": 400,
"error": "Bad Request",
"message": "Valid types are hello or goodbye.",
"path": "/greeting/greet"
}
Create an exception named NameNotFoundException. Have the exception extend RuntimeException rather than Exception.
Assign it a response status of not found.
package com.tutorial.exceptions.spring.rest.exceptionstutorial;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.http.HttpStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class NameNotFoundException extends RuntimeException {
public NameNotFoundException(String message) {
super("The id: " + message + " could not be found.");
}
}
Modify GreetingServicecreateGreeting method to take id as an integer.
Create a new method called getPersonName. Suspend disbelief and implement it as below. Obviously in a real-world project you would get user information from a database, LDAP server, or some other datastore.
Modify createGreeting to use the getPersonName method to personalize the greeting.
package com.tutorial.exceptions.spring.rest.exceptionstutorial;
import org.springframework.stereotype.Service;
@Service
public class GreetingService {
public HelloGoodbye createGreeting(String type, int id) throws GreetingTypeException {
HelloGoodbye helloGoodbye = new HelloGoodbye();
if (type.equals("hello")) {
helloGoodbye.setGreeting("Hello there " +
this.getPersonName(id));
} else if (type.equals("goodbye")) {
helloGoodbye.setGoodbye("Goodbye for now " +
this.getPersonName(id));
} else {
throw new GreetingTypeException("Valid types are hello or goodbye.");
}
helloGoodbye.setType(type);
return helloGoodbye;
}
public String getPersonName(int id) {
if(id==1) {
return "Tom";
} else if(id==2) {
return "Sue";
} else {
throw new NameNotFoundException(Integer.toString(id));
}
}
}
Modify GreetingController to take id as a request parameter and modify its call to the GreetingServicecreateGreeting method to also pass id to the service.
{
"timestamp": "2019-03-31T20:30:18.727+0000",
"status": 404,
"error": "Not Found",
"message": "The id: 6 could not be found.",
"path": "/greeting/greet"
}
As an aside, notice that we had NameNotFoundException extend RuntimeException and not Exception. By doing this we made NameNotFoundException an unchecked exception (more on unchecked exceptions) and were not required to handle the exception.
Controller Error Handlers
Although Spring Boot’s default exception handling is robust, there are times an application might require more customized error handling. One technique is to declare an exception handling method in a rest controller. This is accomplished using Spring’s @Exceptionhandler annotation (javadoc).
Create a new simple class named GreetingError. Note that it is a pojo and does not extend Exception.
package com.tutorial.exceptions.spring.rest.exceptionstutorial;
import java.util.Date;
public class GreetingError {
private Date timestamp;
private String message;
public Date getTimestamp() {
return timestamp;
}
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Modify GreetingController to have a method named nameNotFoundException that is annotated with an @ExceptionHandler annotation.
Implement nameNotFoundException to return a ResponseEntity<>.
package com.tutorial.exceptions.spring.rest.exceptionstutorial;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;
@RestController
@RequestMapping(value = "/greeting")
public class GreetingController {
@Autowired
protected GreetingService service;
@GetMapping("/greet")
public HelloGoodbye getGreeting(@RequestParam("type") String type, @RequestParam("id") int id) throws Exception {
HelloGoodbye goodBye = service.createGreeting(type, id);
return goodBye;
}
@ExceptionHandler(NameNotFoundException.class)
public ResponseEntity<?> nameNotFoundException(NameNotFoundException ex, WebRequest request) {
GreetingError errorDetails = new GreetingError();
errorDetails.setTimestamp(new Date());
errorDetails.setMessage("This is an overriding of the standard exception: " + ex.getMessage());
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
}
Compile, run the application, and visit the endpoint.
{
"timestamp": "2019-04-01T02:14:51.744+0000",
"message": "This is an overriding of the standard exception: The id: 33 could not be found."
}
The default error handling for NameNotFoundException is overridden in the controller. But you are not limited to implementing one error handler in a controller, you can define multiple error handlers, as in the code below.
Modify GreetingController to throw an arithmetic exception in getGreeting.
Create a new exception handler for ArithmeticException.
package com.tutorial.exceptions.spring.rest.exceptionstutorial;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;
@RestController
@RequestMapping(value = "/greeting")
public class GreetingController {
@Autowired
protected GreetingService service;
@GetMapping("/greet")
public HelloGoodbye getGreeting(@RequestParam("type") String type, @RequestParam("id") int id) throws Exception {
int i = 0;
int k = 22/i;
HelloGoodbye goodBye = service.createGreeting(type, id);
return goodBye;
}
@ExceptionHandler(NameNotFoundException.class)
public ResponseEntity<?> nameNotFoundException(NameNotFoundException ex, WebRequest request) {
GreetingError errorDetails = new GreetingError();
errorDetails.setTimestamp(new Date());
errorDetails.setMessage("This is an overriding of the standard exception: " + ex.getMessage());
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(ArithmeticException.class)
public ResponseEntity<?> arithmeticException(ArithmeticException ex, WebRequest request) {
GreetingError errorDetails = new GreetingError();
errorDetails.setTimestamp(new Date());
errorDetails.setMessage("This is an overriding of the standard exception: " + ex.getMessage());
return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Compile, run the application, and visit the rest endpoint.
{
"timestamp": "2019-04-01T02:40:53.527+0000",
"message": "This is an overriding of the standard exception: / by zero"
}
Before continuing, do not forget to remove the code that divides by zero.
The Exception handler is a useful annotation that allows handling exceptions within a class. We used it in our controller to handle exceptions. The method used to handle the exception returned a ResponseEntity<T> class (javadoc). This class is a subclass of HttpEntity (javadoc). The HttpEntity wraps the actual request or response – here the response – while the ResponseEntity adds the HttpStatus code. This allows you to return a custom response from your rest endpoint.
Global Error Handler
The @ControllerAdvice is a way to handle exceptions within Spring Controllers. It allows using a method annotated with the @ExceptionHandler to handle all exceptions in an application.
Create a new class named GreetingExceptionHandler.
Annotate it with the @ControllerAdvice annotation.
Copy and paste the nameNotFoundException method from the GreetingController class. Change the message text to be certain it is, in fact, being called.
package com.tutorial.exceptions.spring.rest.exceptionstutorial;
import java.util.Date;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
@ControllerAdvice
public class GreetingExceptionHandler {
@ExceptionHandler(NameNotFoundException.class)
public ResponseEntity<?> nameNotFoundException(NameNotFoundException ex, WebRequest request) {
GreetingError errorDetails = new GreetingError();
errorDetails.setTimestamp(new Date());
errorDetails.setMessage("This a global exception handler: " + ex.getMessage());
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
}
Remove the NameNotFoundException exception handler from the GreetingController class.
package com.tutorial.exceptions.spring.rest.exceptionstutorial;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;
@RestController
@RequestMapping(value = "/greeting")
public class GreetingController {
@Autowired
protected GreetingService service;
@GetMapping("/greet")
public HelloGoodbye getGreeting(@RequestParam("type") String type, @RequestParam("id") int id) throws Exception {
HelloGoodbye goodBye = service.createGreeting(type, id);
return goodBye;
}
@ExceptionHandler(ArithmeticException.class)
public ResponseEntity<?> arithmeticException(ArithmeticException ex, WebRequest request) {
GreetingError errorDetails = new GreetingError();
errorDetails.setTimestamp(new Date());
errorDetails.setMessage("This is an overriding of the standard exception: " + ex.getMessage());
return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Compile, run the application, and visit the rest endpoint. You receive the error created in the global handler.
{
“timestamp”: “2019-04-06T21:21:17.258+0000”,
“message”: “This a global exception handler: The id: 33 could not be found.”
}
The @ControllerAdvice annotation (Javadoc) allows an exception handler to be shared across controllers. It is useful if you wish creating uniform exception handling across multiple controllers. You can limit the @ControllerAdvice exception handling to apply only to certain controllers, for more information, refer to the Javadoc.
Conclusion
Spring Boot 2 Rest Exceptions and handling them is both easy and difficult. It is easy because there are concrete ways to implement exception handling. Moreover, even if you provide no exception handling, it is provided for you by default. It is difficult because there are many different ways to implement exception handling. Spring provides so much customization, so many different techniques, it is sometimes easy to become lost in the details.
In this tutorial we explored three different techniques when dealing with Spring Boot 2 REST Exceptions. You should refer to other tutorials before deciding any one technique is what you should use. In the interest of full disclosure, I personally feel the @ControllerAdvice technique is the most robust, as it allows creating a unified exception handling framework.