TODO List Project - Backend Part III

previous_step

Connecting Angular Frontend to Spring Boot Restful Services 1

The next few steps would associated with invoking RESTful API from angular applications. We would use something called HTTPClientModule, where you’ll see that Angular provides us with an awesome framework based on something called the observable.

In our backend controller, the code is as follows.

//hello-world-bean
@GetMapping(path = "/hello-world-bean")
public HelloWorldBean helloWorldBean() {
	return new HelloWorldBean("Hello World");
}

1. Creating Data Service

How can we invoke some data from angular front end? Usually we create a new service. This service would call up backend service a backend API.

2. HttpClientModule

HttpClient - we can use this client to get, post, delete, put: all kinds of HTTP requests.

  • Firstly, import the HttpClient Module.
    import { HttpClient } from '@angular/common/http';
    constructor (
    	private http: HttpClient;
    ) {}
  • Second, we can use the client to do HTTP requests, such as
    executeHelloWorldBeanService() {
    	console.log(this.http.get('http://localhost:8080/hello-world-bean'));
    }

But only doing this will cause error, i.e. NullInjectorError: No provider for HttpClient. Why this error happens?

To make use of a component or a utility from a module, what do we need to do is importing that module in. The thing is we have not really imported the modul in. We need to import it in app.module.ts. In app.module.ts we add HttpClientModule in imports array.

Now we can see an Observable is printed int the console, like

Observable { \_isScalar: false, source: Observable, operator: MapOperator }

But by checking the NetWork in Chrome, we find actually there is no request sent to backend. That is this call is not going over the network. What happens? Let’s see the next step.

3. Understanding Observable

Typically if I call a http service, it might really take a long time to execute. If we do it synchronously, that what would happen because of this is we wait for the response, and meanwhile the entire browser would hang. That’s the reason why we should call all http services asynchrnously.

Observable is one of the best approaches to implementing an asynchronnous communication. The most important thing about observable is this is declarative, in the sense that you can define what you are allowed to call. We can define what should happen when I got a response back, or what should happen when I get an error back.

Let’s look at the above question again. If we want that request to be executed, we need to do something called subscribe.

So basically observable will help us to define what you are allowed to call, but it does not really invoke the service until somebody subscribes to it like the following

getWelcomeMessage() {
	this.service.executeHelloWorldBeanService().subscribe();
}

Note that we also need to do some modification in WelcomeDataService Module to let method executeHelloWorldBeanService() return something. That is,

executeHelloWorldBeanService() {
	return this.http.get('http://localhost:8080/hello-world-bean');
}

Now, we may still have one more error, i.e. Access to XMLHttpRequest at 'http://localhost:8080/hello-world-bean' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

This is because we are calling from one web server to another web server. So we are calling a service which is hosted on different server from our front-end application. And by default, spring boot prevent these kind of request. We need to tell Spring Boot to allow such request, by adding @CrossOrigin(origins = "http://localhost:4200"). Now we can have the correct response back.

4. Understanding Subscribe

The subscribers send asynchronous call. We should define in .subscribe() what should do when we get the results back, when we get the response back what should we do? So we should create a function like that, and tie it up to the subsribe. For example,

getWelcomeMessage() {
	this.service.executeHelloWorldBeanService();
	this.service.executeHelloWorldBeanService().subscribe(
		response => this.handleSuccessfulResponse(response)
	);
	console.log('Last line of Welcome Message');
}

handleSuccessfulResponse(response) {
	console.log(response);
}

Next, we want to realize console.log(response.message), we need to define what type of the response in WelcomeDataService. That is,

export class HelloWorldBean {
	constructor(public message: string) { }
}
executeHelloWorldBeanService() {
	return this.http.get<HelloWorldBean>('http://localhost:8080/hello-world-bean');
}

This is very similar to Generics in Java. After doing this, the following code will be correct.

getWelcomeMessage() {
	this.service.executeHelloWorldBeanService().subscribe(
		response => this.handleSuccessfulResponse(response)
	);
}
handleSuccessfulResponse(response) {
	console.log(response.message);
}

5. Handling Error Response

Now we change the code in backend controller

//hello-world-bean
@GetMapping(path = "/hello-world-bean")
public HelloWorldBean helloWorldBean() {
	throw new RuntimeException("Some error has happened");
}

Next, we will use subscribe() method to handle Error Response. In subscribe() the first parameter is the function to handle successful response. You can add another parameter to tell what should happen when there is an error. The code is like this.

getWelcomeMessage() {
	this.service.executeHelloWorldBeanService();
	this.service.executeHelloWorldBeanService().subscribe(
		response => this.handleSuccessfulResponse(response),
		error => this.handleErrorResponse(error)
	);
	console.log('Last line of Welcome Message');
}

handleSuccessfulResponse(response) {
//console.log(response.message);
	this.welcomeMessageFromService = response.message;
}

handleErrorResponse(error) {
	console.log(error)
	console.log(error.error)
	console.log(error.error.message)
	this.welcomeMessageFromService = error.error.message;
}

6. Calling Welcome Http Service with Path Variable

Now we want to call the following service:

//hello-world-bean/path-variable/teemo
@GetMapping(path = "/hello-world-bean/path-variable/{name}")
public HelloWorldBean helloWorldPathVariable(@PathVariable String name) {
	return new HelloWorldBean(String.format("Hello World, %s", name));
}
  • First step: define the service in welcome-data.service.ts. How can we pass varaible in the uri? We can use back-tick, i.e. ``. After using backtick we can use ${name} to pass the path varaible name into the url. That is,
executeHelloWorldServiceWithPathVariable(name) {
    return this.http.get<HelloWorldBean>(`http://localhost:8080/hello-world-bean/path-variable/${name}`);
  }
next_step

   Reprint policy


《TODO List Project - Backend Part III》 by Tong Shi is licensed under a Creative Commons Attribution 4.0 International License
  TOC