@@ -4,8 +4,8 @@ Credits / Notes taken from:
4
4
5
5
- [ Spring Boot and Angular Full Stack Development | 4 Hour Youtube Tutorial from AmigosCode] ( https://www.youtube.com/watch?v=8ZPsZBcue50 )
6
6
- [ 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
9
9
10
10
<br />
11
11
@@ -24,6 +24,7 @@ Table of Contents (ToC):
24
24
- [ Server Controller/Resource - HTTP requests handling, Exposing the API] ( #server-controllerresource---http-requests-handling-exposing-the-api )
25
25
- [ Database configuration] ( #database-configuration )
26
26
- [ Testing with Postman] ( #testing-with-postman )
27
+ - [ NEW - Exception Handling] ( #new---exception-handling )
27
28
- [ Frontend Angular] ( #frontend-angular )
28
29
- [ package.json] ( #packagejson )
29
30
- [ Enums, interfaces, services] ( #enums-interfaces-services )
@@ -1376,6 +1377,285 @@ Another example:
1376
1377
1377
1378
< br/>
1378
1379
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
+
1379
1659
# Frontend Angular
1380
1660
1381
1661
[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
3927
4207
3928
4208
< br/>
3929
4209
3930
- (Sunday, August 27 , 2023 , 12 :16 )
4210
+ (Sunday, August 27 , 2023 , 12 :16 )
0 commit comments