官术网_书友最值得收藏!

Creating the web service

Now that the data collection and storage has been re-factored, it is time to build a web service. In some ways, this is similar to the blog server we created previously. However, we want to be ambitious, and so will plan for future development by starting to build a web API for our data.

The REST architecture style is very popular as it is well suited to JSON and being consumed by client web applications.

Note

REST is an architecture style for web services that has gained acceptance as a less complex alternative to WSDL and SOAP.

REST builds on HTTP and uses the verbs GET, POST, PUT, and DELETE. It can use any Internet format as a data type (XML, images, and so on), but usually uses JSON.

There is no agreed standard for RESTful web APIs, as it is a style rather than a protocol. This means that it is very flexible, but it can also be hard to get an answer to questions such as "is this the right way to do it?". The usual advice is to be pragmatic and follow REST as far as makes sense.

For more information, see http://www.restapitutorial.com/.

Using the package rpc

A RESTful API has a number of conventions and expectations. Fortunately, there is an existing Dart package, authored by the Dart development team, that covers most of the implementation detail.

Note

The rpc package is available from Pub at https://pub.dartlang.org/packages/rpc.

The source code is available from GitHub at https://github.com/dart-lang/rpc.

As a bonus, the rpc package also includes discoverability features (via Google's Discovery Document, https://developers.google.com/discovery/v1/reference/apis), which allow the easy creation of client code in any supporting language.

Initiating the API server

In the main.dart file, the server is set up and set to serve on the local machine using port 8080, as shown in the following code snippet:

library io_rpc_sample;

import 'dart:io';
import 'package:georestwebservice/georestwebservice.dart';
import 'package:rpc/rpc.dart';

final ApiServer _apiServer = new ApiServer(apiPrefix: '/api', prettyPrint: true);

main() async {
  _apiServer.addApi(new Quake());
  _apiServer.enableDiscoveryApi();

  HttpServer server = await HttpServer.bind(InternetAddress.ANY_IP_V4, 8080);
  server.listen(_apiServer.httpRequestHandler);
}

The _apiServer instance is set up with two parameters. The first sets an expected prefix for the API so that valid URLs will start with the form http://127.0.0.1:8080/api. The second parameter, prettyPrint, formats the JSON for easy viewing in the browser or another consuming application that may include a debugger. This is particularly useful when the JSON is heavily nested.

The _apiServer instance is then configured with a Quake object. This object contains the implementation of the API methods. The Quake class definition is located in the file georestwebservice.dart, and is heavily annotated. Let's have a look at the following code snippet:

@ApiClass(
    name: 'quake',
    version: 'v1',
    description: 'Rest API for Earthquake feature data.')
class Quake {
  ...
}

The @ApiClass annotation exposes this class as an API on the rpc server. This allows separate class to handle the implementation per API name and per version. The different versions are accessed by changing the version or name in the URL that is being requested from the server.

Exposing methods

The simplest method is the classic Hello World example:

  @ApiMethod(path: 'hello')
  QuakeResponse hello() {
    return new QuakeResponse()..result = 'Hello world.';
  }

The path setting in the attribute defines the name part of the URL. A QuakeResponse object is returned with a string set as the result, as shown in the following screenshot:

Exposing methods

The QuakeResponse class is a simple class that is automatically converted to a JSON response by the rpc package, as shown in the following code snippet:

class QuakeResponse {
  String result;
  QuakeResponse();
}

Classes used as a response are required to be concrete (not abstract) and to have a constructor with no parameters. Also, the constructor must not be a named constructor.

Error handling of incorrect requests

The rpc package handles any URLs for incorrect methods automatically, and provides an error message in JSON format:

Error handling of incorrect requests

The error handling also covers an exception or other error occurring within the implementation of an API method, as shown here:

@ApiMethod(path: 'implementationerror')
QuakeResponse implementationError() {
  throw new Exception();
}

This doomed-to-fail method will produce a valid JSON response that the client application can handle:

Error handling of incorrect requests

Serving the latest information

For the grid view data display, the most important URL for the web API will be http://127.0.0.1:8080/api/quake/v1/latest:

  @ApiMethod(path: 'latest')
  List<String> latest()  {
    DaoQuakeAPI qa = new DaoQuakeAPI();
    return qa.fetchTopTenLatest();
  }

The rpc package handles the conversion to JSON of several common return types. The List<String> is probably the most common collection.

Supplying the data

The DaoQuakeAPI class is implemented in the file daoapi.dart and retrieves the features list from the database, as shown in the following code snippet:

Future<List<String>> fetchTopTenLatest() async {
  var dbConn;
  try {
    dbConn = await connect(uri);

    var query = """
    select geojson from dm_quakefeatures order by modified_date desc limit 10
    """;

    List<Row> dbResults = await dbConn.query(query).toList();
    List<String> results = new List<String>();

    dbResults.forEach((record) {
      results.add(record[0]);
    });

    return results;
  } catch (exception, stackTrace) {
    print(exception);
    print(stackTrace);
  } finally {
    dbConn.close();
  }
}

The results are returned as a list of records, and each record returned is a List object. This means that dbr[0] needs to be specified, as we only require the string in the first field.

Discovering the API

The rpc package provides built-in discoverability for the API. This is provided as a JSON definition of the service that is accessed from the API server:

http://127.0.0.1:8080/api/discovery/v1/apis/quake/v1/rest

This definition would be consumed by development tools to produce wrapper objects and supporting code in the required language. Let's have a look at the output in the following screenshot:

Discovering the API

The header contains identifying information, like the version, and key usage data, such as the base URL for calling methods.

The schemas section summarizes all the data types used by the API, the methods lists, and the available function calls that can be made. In addition, the resources section (empty for this project) would contain the objects that group API methods together.

Running the web service

As with the data monitor, you may wish to run the REST web service outside of the development environment. To run from a terminal (command line), enter the following code:

$ cd georestwebservice/
$ dart bin/main.dart
Starting georestwebservice...
主站蜘蛛池模板: 广宗县| 玉门市| 改则县| 呼图壁县| 甘南县| 灌阳县| 南郑县| 和静县| 盐山县| 大连市| 怀柔区| 嘉禾县| 武安市| 昌都县| 正宁县| 乡城县| 信宜市| 防城港市| 南陵县| 潮安县| 墨脱县| 保定市| 衡阳市| 九龙坡区| 临潭县| 乌恰县| 黎川县| 尉氏县| 迭部县| 丰城市| 桐柏县| 遂昌县| 华容县| 陆河县| 崇州市| 廊坊市| 莲花县| 九龙坡区| 剑阁县| 喀喇| 怀柔区|