Shopping list
Hi devs, I want to show you a simple app I have create for playing with Ionic 5 and Java Spring.
Let me introduce you to Shopping List
Shopping List is a simple app for manage my stuff to buy from grocery to clothes and other stuff when I go out.
But why create your own app when there are plenty of them? Just for learn some stuff, get my hands dirty and stress a bit my Raspberry :D
Let's take a look to a few key elements for the app and the server.
APP
The app is created with Ionic 5 and Angular 10.
The idea, behind this project, was to write some clean code and play a bit with RxJS operators, interceptors and guards.
The HTTP interceptor is necessary because I have to inject the OAUTH2 Token for all the request to the server.
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const currentUser = this.authService.currentUserValue;
if (currentUser != null) {
const token: string = currentUser.access_token;
if (token) {
request = request.clone({ headers: request.headers.set('Authorization', 'Bearer ' + token) });
}
}
if (!request.headers.has('Content-Type')) {
request = request.clone({ headers: request.headers.set('Content-Type', 'application/json') });
}
request = request.clone({ headers: request.headers.set('Accept', 'application/json') });
return next.handle(request).pipe(
map((event: HttpEvent<any>) => {
return event;
}),
catchError((error: HttpErrorResponse) => {
// TODO: Add 401 Handler
return throwError(error);
})
);
}
I used the RxJS operators for manipulate all the Observables used for calling the server.
I really used and loved a lot the catchError for handling server error response and throw them were I have subscribed to this observables.
Here is an example about an Observable I have created for retrieve the full list of shops.
getShops(): Observable<IShopsResponse> {
return this.http.get<IShopsResponse>(this.url + 'getAll').pipe((
catchError((error: HttpErrorResponse) => {
let err: IShopsResponse = null;
err.status = error.status;
err.message = error.error;
return throwError(err);
})
))
}
Here is where I subscribe to the Observable and how I manage the error, and what to show to the user.
getAllShops() {
this.shopService.getShops().subscribe(
(response: IShopsResponse) => {
this.list = response.data;
},
(error) => {
let title = this.langService.getString('ALERT.error');
let msg = this.langService.getString('ERROR.retrieve_list');
this.alertService.showAlert(title, msg).then(
(alertBox) => { alertBox.present(); }
);
}
);
}
Remember to take a look, if you haven't at RxJS documentation
SERVER
The server is created using Spring Boot framework.
The basic idea was to create a simple server with a basic authentication for the REST using OAuth2 following this tutorial.
After following this tutorial I decided to take a look at Spring Boot potentials and I find out the possibility of retrieve user informations without doing a ping-pong with ids and other private informations using SecurityContextHolder.
Here is a snipping of the function for the current user:
public User getCurrentUser(){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if ((authentication instanceof AnonymousAuthenticationToken)) {
return null;
}
String email = authentication.getName();
Optional<User> optUser = userRepo.findByEmail(email);
if(optUser.isEmpty()){
return null;
}
return optUser.get();
}
Another element that I decide to add was a logger for request and response, a very usefull way to analyze future server errors.
For achieve this I had to create a specific class that extends HandlerInterceptorAdapter and with the some extra manipulation I was able to get my logs.
Here are the methods I created for the logs:
public void writeRequestPayloadAudit(ResettableStreamHttpServletRequest wrappedRequest) {
try {
String requestHeaders = getRawHeaders(wrappedRequest);
String requestBody = org.apache.commons.io.IOUtils.toString(wrappedRequest.getReader());
LOGGER.info("=================================== Request Start ===================================");
LOGGER.info("Request Method: " + wrappedRequest.getMethod());
LOGGER.info("Request URL: " + wrappedRequest.getRequestURI());
LOGGER.info("Request Headers:" + requestHeaders.replace("\n", ","));
LOGGER.info("Request Body:" + requestBody.replace("\n", ""));
LOGGER.info("==================================== Request End ====================================");
} catch (Exception e) {
LOGGER.error("Exception Request" + e.getMessage());
}
}
public void writeResponsePayloadAudit(ResettableStreamHttpServletResponse wrappedResponse) {
String rawHeaders = getRawHeaders(wrappedResponse);
LOGGER.info("=================================== Response Start ===================================");
LOGGER.info("Response Status: " + wrappedResponse.getStatus());
LOGGER.info("Response Headers:" + rawHeaders.replace("\n", ","));
byte[] data = new byte[wrappedResponse.rawData.size()];
for (int i = 0; i < data.length; i++) {
data[i] = (byte) wrappedResponse.rawData.get(i);
}
String responseBody = new String(data);
LOGGER.info("Response body:" + responseBody);
LOGGER.info("==================================== Response End ====================================");
}
And here is how my logs looks like:
2020-10-30 18:32:35,583 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] =================================== Request Start ===================================
2020-10-30 18:32:35,584 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] Request Method: GET
2020-10-30 18:32:35,585 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] Request Headers:content-type:application/json,authorization:Bearer 6de79b7b-03bd-4e05-a8f8-af7f618d2fbc,user-agent:PostmanRuntime/7.26.5,accept:*/*,postman-token:18287157-4a9d-483f-9031-62cc2b3aa5dd,host:localhost:8080,accept-encoding:gzip, deflate, br,connection:keep-alive,
2020-10-30 18:32:35,585 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] Request body:
2020-10-30 18:32:35,586 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] ==================================== Request End ====================================
2020-10-30 18:32:35,647 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] =================================== Response Start ===================================
2020-10-30 18:32:35,648 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] Response Status: 200
2020-10-30 18:32:35,648 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] Response Headers:Vary:Origin,Vary:Origin,Vary:Origin,
2020-10-30 18:32:35,649 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] Response body:{"status":200,"message":"","data":[[{"id":1,"name":"Supermercato 1","createdAt":"2020-10-29T22:44:33","updatedAt":"2020-10-29T22:44:33","deleted":0,"total":0,"bought":0,"items":[]}],[{"id":2,"name":"Supermercato 2","createdAt":"2020-10-29T22:44:41","updatedAt":"2020-10-29T22:44:41","deleted":0,"total":0,"bought":0,"items":[]}],[{"id":13,"name":"Supermercato 3","createdAt":"2020-10-29T22:49:06","updatedAt":"2020-10-29T22:49:06","deleted":0,"total":0,"bought":0,"items":[]}]]}
2020-10-30 18:32:35,649 INFO com.bebetos.shoppinglist.interceptors.LoggerInterceptor [http-nio-8080-exec-3] ==================================== Response End ====================================
Special credits to my friend Alessandro Valenti for Angular advices.
For extra code informations here is the link to the repository.
What are your opinions/advices?
Top comments (0)