HTTP Client 4.0
Most frontend applications communicate with backend services using the HTTP protocol. Dart web apps typically do this using the XMLHttpRequest (XHR) API, using either HttpRequest from the dart:html library or a higher level API, such as what the http package provides.
The following demos, which use the http package, illustrate server communication:
Providing HTTP services
HTTP client demo: Tour of Heroes
This demo is a shorter version of the Tour of Heroes app. It receives heroes from a server and displays them as a list. The user can add new heroes and save them to the server.
Here’s what the app’s UI looks like:
This demo has a single component, the
HeroListComponent. Here is its template:
ngFor directive displays the list of heroes.
Below the list are an input box and an Add Hero button,
which allow the user to add new heroes.
A template reference variable,
(click) event binding access to the value of the input box. When the
user clicks the button, the click handler passes the input value to
addHero() method of the component. The click handler also clears the
Below the button is an area for an error message.
The HeroListComponent class
Here’s the component class:
Angular injects a
HeroService into the constructor,
and the component calls that service to fetch and save data.
The component doesn’t interact directly with the
Instead, it delegates data access to the
Always delegate data access to a supporting service class.
Although at runtime the component requests heroes immediately after creation,
this request is not in the component’s constructor.
Instead, the request is in the
ngOnInit lifecycle hook.
Keep constructors simple. Components are easier to test and debug when their constructors are simple, with all real work (such as calling a remote server) handled by a separate method.
The asynchronous methods in the hero service,
return the Future values of the current hero list and the newly added
hero, respectively. The methods in the hero list component,
addHero(), specify the actions to be taken when the asynchronous method
calls succeed or fail.
For more information about
Future, see the
and the resources at the end of that tutorial.
In the previous samples, the app faked interaction with the server by returning mock heroes in a service:
It’s time to get real data. The following code makes
the heroes from the server:
Use a Client object
This demo uses a
that’s injected into the
Here’s the code that uses the client’s
get() method to fetch data:
get() method takes a resource URL, which it uses to contact the server
that returns heroes.
Mock the server
When no server exists yet or you want to
avoid network reliability issues during testing,
don’t use a
BrowserClient as the
Instead, you can mock the server by using the
in-memory web API,
which is what the
Alternatively, use a JSON file:
Processing the response object
getHeroes() method uses an
_extractData() helper method to
_http.get() response object to heroes:
response object doesn’t hold the data in a form that
the app can use directly.
To use the response data, you must first decode it.
The response data is in JSON string form.
You must deserialize that string into objects, which you can do by calling
JSON.decode() method from the dart:convert library.
For examples of decoding and encoding JSON, see the
dart:convert section of the Dart library tour.
The decoded JSON doesn’t list the heroes.
Instead, the server wraps JSON results in an object with a
This is conventional web API behavior, driven by
Assume nothing about the server API.
Not all servers return an object with a
Don’t return the response object
Although it’s possible for
getHeroes() to return the HTTP response,
that’s not a good practice. The point of a data service is to hide the server
interaction details from consumers. A component that calls the
only wants the heroes. It is separated from from the code that’s responsible
for getting the data, and from the response object.
Always handle errors
An important part of dealing with I/O is anticipating errors by preparing to catch them and do something with them. One way to handle errors is to pass an error message back to the component for presentation to the user, but only if the message is something that the user can understand and act upon.
This simple app handles a
getHeroes() error as follows:
HeroListComponent error handling
HeroListComponent, the call to
_heroService.getHeroes() is in a
try clause, and the
errorMessage variable is conditionally bound in the template.
When an exception occurs,
errorMessage variable is assigned a value as follows:
To create a failure scenario,
reset the API endpoint to a bad value in
Afterward, remember to restore its original value.
Sending data to the server
So far you’ve seen how to retrieve data from a remote location using an HTTP service. The next task is adding the ability to create new heroes and save them in the backend.
First, the service needs a method that the component can call to create
and save a hero.
For this demo, the method is called
create() and takes the name of a new hero:
To implement the method, you must know the server’s API for creating heroes.
This sample’s data server
follows typical REST guidelines.
It supports a
at the same endpoint as
The new hero data must be in the body of the request,
structured like a
Hero entity but without the
Here’s an example of the body of the request:
The server generates the
id and returns the JSON representation of the
new hero, including the generated ID. The hero is inside a response object
with its own
Now that you know the server’s API, here’s the implementation of
_headers object, the
Content-Type specifies that
the body represents JSON.
extracts the data from the response.
waits for the service’s asynchronous
create() method to create a hero.
create() is finished,
addHero() puts the new hero in the
Cross-origin requests: Wikipedia example
Although making XMLHttpRequests
(often with a helper API, such as
is a common approach to server communication in Dart web apps,
this approach isn’t always possible.
For security reasons, web browsers block XHR calls to a remote server whose origin is different from the origin of the web page. The origin is the combination of URI scheme, hostname, and port number. This is called the same-origin policy.
Some servers do not support CORS but do support an older, read-only alternative called JSONP.
For more information about JSONP, see this Stack Overflow answer.
The following simple search shows suggestions from Wikipedia as the user types in a text box:
Wikipedia offers a modern CORS API and a legacy JSONP search API.
This page is under construction. For now, see the demo source code for an example of using Wikipedia’s JSONP API.