Skip to content

Commit 57fcfcd

Browse files
Add exception handling notes on Spring Boot Ping App
1 parent bdb3667 commit 57fcfcd

File tree

2 files changed

+283
-3
lines changed

2 files changed

+283
-3
lines changed

SpringBoot/SpringBootAngularPingStatusApp.md

+283-3
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ Credits / Notes taken from:
44

55
- [Spring Boot and Angular Full Stack Development | 4 Hour Youtube Tutorial from AmigosCode](https://www.youtube.com/watch?v=8ZPsZBcue50)
66
- [Full Stack Spring Boot RESTful API with MySQL and Angular - Youtube Playlist - Direct Source from getarrays.io | Roland Toussaint "Junior"](https://www.youtube.com/playlist?list=PLopcHtZ0hJF0OIOr88qHuJ3-UKRuCUrKf)
7-
- https://github.com/getarrays/server-backend/ - Spring Boot Backend
8-
- https://github.com/getarrays/serverapp/ - Angular Frontend
7+
- https://github.com/getarrays/server-backend/ - Spring Boot Backend
8+
- https://github.com/getarrays/serverapp/ - Angular Frontend
99

1010
<br/>
1111

@@ -24,6 +24,7 @@ Table of Contents (ToC):
2424
- [Server Controller/Resource - HTTP requests handling, Exposing the API](#server-controllerresource---http-requests-handling-exposing-the-api)
2525
- [Database configuration](#database-configuration)
2626
- [Testing with Postman](#testing-with-postman)
27+
- [NEW - Exception Handling](#new---exception-handling)
2728
- [Frontend Angular](#frontend-angular)
2829
- [package.json](#packagejson)
2930
- [Enums, interfaces, services](#enums-interfaces-services)
@@ -1376,6 +1377,285 @@ Another example:
13761377
13771378
<br/>
13781379
1380+
## NEW - Exception Handling
1381+
1382+
(Saturday, February 03, 2024, 20:35)
1383+
1384+
Resources:
1385+
1386+
- [Amigoscode - Spring Boot 2 Tutorial | How To Handle Exceptions](https://youtu.be/PzK4ZXa2Tbc)
1387+
- [Teddy Smith - Spring Boot 2.7.4 For Beginners - Exception Handling](https://youtu.be/hBb7Rd1afck)
1388+
- GitHub Repository https://github.com/teddysmithdev/pokemon-review-springboot
1389+
- [Spring Boot | REST API Request Validation & Exception Handling Realtime Example | JavaTechie 38m](https://youtu.be/gPnd-hzM_6A)
1390+
1391+
---
1392+
1393+
<br/>
1394+
1395+
- Under `com.radubulai.serverpingstatustracker` create `exception` package
1396+
- Under `exception` package, create `ServerNotFoundException.java` class
1397+
1398+
```java
1399+
// ServerNotFoundException.java
1400+
package com.radubulai.serverpingstatustracker.exception;
1401+
1402+
public class ServerNotFoundException extends RuntimeException {
1403+
public ServerNotFoundException(String message) {
1404+
super(message);
1405+
}
1406+
1407+
public ServerNotFoundException(String message, Throwable cause) {
1408+
super(message, cause);
1409+
}
1410+
}
1411+
```
1412+
1413+
<br/>
1414+
1415+
- Under `exception` package, create `ApiException.java` class
1416+
1417+
```java
1418+
// ApiException.java
1419+
package com.radubulai.serverpingstatustracker.exception;
1420+
1421+
import org.springframework.http.HttpStatus;
1422+
1423+
import java.time.LocalDateTime;
1424+
1425+
public class ApiException {
1426+
private final LocalDateTime timeStamp;
1427+
private final int statusCode;
1428+
private final HttpStatus status;
1429+
private final String reason;
1430+
private final String message;
1431+
private final String developerMessage;
1432+
1433+
public ApiException(LocalDateTime timeStamp,
1434+
int statusCode,
1435+
HttpStatus status,
1436+
String reason,
1437+
String message,
1438+
String developerMessage) {
1439+
this.timeStamp = timeStamp;
1440+
this.statusCode = statusCode;
1441+
this.status = status;
1442+
this.reason = reason;
1443+
this.message = message;
1444+
this.developerMessage = developerMessage;
1445+
}
1446+
1447+
public LocalDateTime getTimeStamp() {
1448+
return timeStamp;
1449+
}
1450+
1451+
public int getStatusCode() {
1452+
return statusCode;
1453+
}
1454+
1455+
public HttpStatus getStatus() {
1456+
return status;
1457+
}
1458+
1459+
public String getReason() {
1460+
return reason;
1461+
}
1462+
1463+
public String getMessage() {
1464+
return message;
1465+
}
1466+
1467+
public String getDeveloperMessage() {
1468+
return developerMessage;
1469+
}
1470+
}
1471+
```
1472+
1473+
<br/>
1474+
1475+
- Under `exception` package, create `ApiExceptionHandler.java` class
1476+
1477+
```java
1478+
// ApiExceptionHandler.java
1479+
package com.radubulai.serverpingstatustracker.exception;
1480+
1481+
import lombok.extern.slf4j.Slf4j;
1482+
import org.springframework.http.HttpStatus;
1483+
import org.springframework.http.ResponseEntity;
1484+
import org.springframework.web.bind.annotation.ControllerAdvice;
1485+
import org.springframework.web.bind.annotation.ExceptionHandler;
1486+
1487+
import java.net.UnknownHostException;
1488+
import java.sql.SQLIntegrityConstraintViolationException;
1489+
1490+
import static java.time.LocalDateTime.now;
1491+
1492+
/**
1493+
* @author Radu-Alexandru Bulai (<a href="https://radubulai.com">https://radubulai.com</a>)
1494+
* @version 1.0
1495+
* @since 03-Feb-2024
1496+
*/
1497+
@Slf4j
1498+
@ControllerAdvice
1499+
public class ApiExceptionHandler {
1500+
1501+
@ExceptionHandler(value = {ServerNotFoundException.class})
1502+
public ResponseEntity<Object> handleServerNotFoundException(ServerNotFoundException e) {
1503+
HttpStatus notFound = HttpStatus.NOT_FOUND;
1504+
ApiException apiException = new ApiException(
1505+
now(),
1506+
notFound.value(),
1507+
notFound,
1508+
"ServerNotFoundException",
1509+
e.getMessage(),
1510+
e.toString()
1511+
);
1512+
log.error(e.toString());
1513+
return new ResponseEntity<>(apiException, notFound);
1514+
}
1515+
1516+
@ExceptionHandler(value = {UnknownHostException.class})
1517+
public ResponseEntity<Object> handleUnknownHostException(UnknownHostException e) {
1518+
HttpStatus httpStatus = HttpStatus.BAD_REQUEST;
1519+
ApiException apiException = new ApiException(
1520+
now(),
1521+
httpStatus.value(),
1522+
httpStatus,
1523+
"UnknownHostException",
1524+
e.getMessage(),
1525+
e.toString()
1526+
);
1527+
log.error(e.toString());
1528+
e.printStackTrace();
1529+
return new ResponseEntity<>(apiException, httpStatus);
1530+
}
1531+
1532+
@ExceptionHandler(value = {SQLIntegrityConstraintViolationException.class})
1533+
public ResponseEntity<Object> handleSQLIntegrityConstraintViolationException(
1534+
SQLIntegrityConstraintViolationException e) {
1535+
HttpStatus httpStatus = HttpStatus.BAD_REQUEST;
1536+
ApiException apiException = new ApiException(
1537+
now(),
1538+
httpStatus.value(),
1539+
httpStatus,
1540+
"SQLIntegrityConstraintViolationException",
1541+
e.getMessage(),
1542+
e.toString()
1543+
);
1544+
log.error(e.toString());
1545+
e.printStackTrace();
1546+
return new ResponseEntity<>(apiException, httpStatus);
1547+
}
1548+
}
1549+
```
1550+
1551+
<hr/>
1552+
1553+
<br/>
1554+
1555+
Difference in Responses with exception handling vs no exception handling in Spring Boot:
1556+
1557+
**_Example 01. Making a GET request to a non-existing server:_**
1558+
1559+
- Postman: GET http://localhost:8080/api/servers/349
1560+
1561+
```json
1562+
// Response
1563+
{
1564+
"timeStamp": "2024-02-03T20:47:17.0402758",
1565+
"statusCode": 200,
1566+
"status": "OK",
1567+
"message": "Server retrieved",
1568+
"data": {
1569+
"server": {
1570+
"id": 349,
1571+
"ipAddress": "dns.adguard.com",
1572+
"name": "DNS Adguard",
1573+
"network": "External",
1574+
"details": "One of the best servers for DNS with ad block",
1575+
"status": "SERVER_UP"
1576+
}
1577+
}
1578+
}
1579+
```
1580+
1581+
- Postman: GET http://localhost:8080/api/servers/350 (with no Spring Boot exception handling)
1582+
1583+
```json
1584+
// Response
1585+
{
1586+
"timestamp": "2024-02-03T18:49:00.842+00:00",
1587+
"status": 500,
1588+
"error": "Internal Server Error",
1589+
"path": "/api/servers/350"
1590+
}
1591+
```
1592+
1593+
- Postman: GET http://localhost:8080/api/servers/350 (with Spring Boot exception handling)
1594+
1595+
```json
1596+
// Response
1597+
{
1598+
"timeStamp": "2024-02-03T20:49:32.0222647",
1599+
"statusCode": 404,
1600+
"status": "NOT_FOUND",
1601+
"reason": "ServerNotFoundException",
1602+
"message": "Server by id 350 was not found",
1603+
"developerMessage": "com.radubulai.serverpingstatustracker.exception.ServerNotFoundException: Server by id 350 was not found"
1604+
}
1605+
```
1606+
1607+
<br/>
1608+
1609+
**_Example 02. Making a POST request to a server which IpAddress value (`8.8.8.8`) already exists in database_**
1610+
1611+
- Postman: POST http://localhost:8080/api/servers
1612+
1613+
```json
1614+
// Request Payload
1615+
{
1616+
"ipAddress": "8.8.8.8",
1617+
"name": "test",
1618+
"network": "test network",
1619+
"status": "SERVER_DOWN"
1620+
}
1621+
```
1622+
1623+
```json
1624+
// Response with no custom Spring Boot exception handling
1625+
{
1626+
"timestamp": "2024-02-03T16:46:59.537+00:00",
1627+
"status": 500,
1628+
"error": "Internal Server Error",
1629+
"path": "/api/servers/"
1630+
}
1631+
```
1632+
1633+
```json
1634+
// Response with custom Spring Boot exception handling
1635+
{
1636+
"timeStamp": "2024-02-03T18:48:16.7354346",
1637+
"statusCode": 400,
1638+
"status": "BAD_REQUEST",
1639+
"reason": "SQLIntegrityConstraintViolationException",
1640+
"message": "Duplicate entry '8.8.8.8' for key 'server.UK_96tx503up4941ibvsnhh8itdi'",
1641+
"developerMessage": "java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '8.8.8.8' for key 'server.UK_96tx503up4941ibvsnhh8itdi'"
1642+
}
1643+
```
1644+
1645+
![Error Handling in Angular from Spring Boot](./SpringBootAngularPingStatusApp/ErrorHandling_Angular01.jpg)
1646+
1647+
```log
1648+
POST http://localhost:8080/api/servers 400 (Bad Request)
1649+
server.service.ts:133 HttpErrorResponse {headers: HttpHeaders, status: 400, statusText: 'OK', url: 'http://localhost:8080/api/servers', ok: false, …}error: developerMessage: "java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '8.8.8.8' for key 'server.UK_96tx503up4941ibvsnhh8itdi'"message: "Duplicate entry '8.8.8.8' for key 'server.UK_96tx503up4941ibvsnhh8itdi'"reason: "SQLIntegrityConstraintViolationException"status: "BAD_REQUEST"statusCode: 400timeStamp: "2024-02-03T18:41:03.5251476"[[Prototype]]: Objectheaders: HttpHeaders {normalizedNames: Map(0), lazyUpdate: null, lazyInit: ƒ}message: "Http failure response for http://localhost:8080/api/servers: 400 OK"name: "HttpErrorResponse"ok: falsestatus: 400statusText: "OK"url: "http://localhost:8080/api/servers"[[Prototype]]: HttpResponseBase
1650+
```
1651+
1652+
```log
1653+
POST http://localhost:8080/api/servers 500 (Internal Server Error)
1654+
server.service.ts:133 HttpErrorResponse {headers: HttpHeaders, status: 500, statusText: 'OK', url: 'http://localhost:8080/api/servers', ok: false, …}error: error: "Internal Server Error"path: "/api/servers"status: 500timestamp: "2024-02-03T16:43:15.929+00:00"[[Prototype]]: Objectheaders: HttpHeaders {normalizedNames: Map(0), lazyUpdate: null, lazyInit: ƒ}message: "Http failure response for http://localhost:8080/api/servers: 500 OK"name: "HttpErrorResponse"ok: falsestatus: 500statusText: "OK"url: "http://localhost:8080/api/servers"[[Prototype]]: HttpResponseBase
1655+
```
1656+
1657+
<br/>
1658+
13791659
# Frontend Angular
13801660
13811661
[Full Stack Spring Boot RESTful API with MySQL and Angular | RxJs State Management - Part 13](https://www.youtube.com/watch?v=Bwl2WAsC-8Y&list=PLopcHtZ0hJF0OIOr88qHuJ3-UKRuCUrKf&index=13)
@@ -3927,4 +4207,4 @@ This approach minimizes the frequency of change detection cycles and results in
39274207
39284208
<br/>
39294209
3930-
(Sunday, August 27, 2023, 12:16)
4210+
(Sunday, August 27, 2023, 12:16)
Loading

0 commit comments

Comments
 (0)