Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Agile Web Development With Rails, 1st Edition (2005).pdf
Скачиваний:
28
Добавлен:
17.08.2013
Размер:
7.99 Mб
Скачать

Leon Breedt, the author of this chapter and the Action Web Service code, is an analyst/developer originally from the city of Cape Town, South Africa.

Chapter 20

Web Services on Rails

With the Depot application up and running, we may want to let other developers write their own applications that can talk to it using standard web service protocols. To do that, we’ll need to get acquainted with Action Web Service (which we’ll call AWS from now on).

In this chapter, we’ll discuss how AWS is structured. We’ll see how to declare an API, write the code to implement it, and then make sure it works by writing tests for it.

20.1 What AWS Is (and What It Isn’t)

AWS handles server-side support for the SOAP and XML-RPC protocols in our Rails application. It converts incoming method invocation requests into method calls on our web services and takes care of sending back the responses. This lets us focus on the work of writing the application-specific methods to service the requests.

AWS does not try to implement every facet of the W3C specifications for SOAP and WSDL or provide every possible feature of XML-RPC. Instead, it focuses on the functionality we can reasonably expect to use regularly in our web services.

Arbitrarily nested structured types

Typed arrays

Sending of exceptions and traces back over the wire when web service methods raise exceptions

Prepared exclusively for Rida Al Barazi

THE API DEFINITION 412

Action Web Service lets us be liberal in the input we accept from remote callers, and strict in the output we emit,1 by coercing input and output values into the correct types.

Using Action Web Service, we could

add support for the Blogger or metaWeblog APIs to a Rails blogging application,

implement our own custom API and have .NET developers be able to generate a class to use it from the Action Web Service–generated WSDL, and

support both SOAP and XML-RPC backends with the same code.

20.2 The API Definition

The first step in creating a web services application is deciding the functionality we want to provide to remote callers and how much information we’re going to expose to them.

Ideally, it would then be enough to simply write a class implementing this functionality and make it available for invocation. However, this causes problems when we want to interoperate with languages that aren’t as dynamic as Ruby. A Ruby method can return an object of any type. This can cause things to blow up spectacularly when our remote callers get back something they didn’t expect.

AWS deals with this problem by performing type coercion. If a method parameter or return value is not of the correct type, AWS tries to convert it. This makes remote callers happy but also stops us from having to jump through hoops to get input parameters into the correct type if we have remote callers sending us bogus values, such as strings instead of proper integers.

Since Ruby can’t use method definitions to determine the expected method parameter types and return value types, we have to help it by creating

an API definition class. Think of the API definition class as similar to a API definition Java or C# interface: It contains no implementation code and cannot be instantiated. It just describes the API.

Enough talk, let’s see an example. We’ll use the generator to get started. We’ll create a web service that has two methods: one to return a list of all products and the other to return details of a particular product.

1To paraphrase Larry Wall.

Prepared exclusively for Rida Al Barazi

Report erratum

 

 

THE API DEFINITION

413

 

depot> ruby script/generate web_service Backend find_all_products find_product_by_id

 

exists

app/apis/

 

 

exists

test/functional/

 

 

create

app/apis/backend_api.rb

 

 

create

app/controllers/backend_controller.rb

 

 

create test/functional/backend_api_test.rb

 

 

This generates a stub API definition.

 

File 126

class BackendApi < ActionWebService::API::Base

 

 

api_method :find_all_products

 

 

api_method :find_product_by_id

 

 

end

 

 

 

And it generates a skeleton controller.

 

File 133

class BackendController < ApplicationController

 

 

wsdl_service_name 'Backend'

 

 

def find_all_products

 

 

end

 

 

 

def find_product_by_id

 

 

end

 

 

 

end

 

 

 

And it generates a sample functional test that we’ll cover in Section 20.6,

 

 

Testing Web Services, on page 423.

 

 

We’ll need to finish off the API definition. We’ll change its name to Product-

 

 

Api and its filename to app/apis/product_api.rb.

 

File 128

class ProductApi < ActionWebService::API::Base

 

 

api_method :find_all_products,

 

 

 

:returns => [[:int]]

 

 

api_method :find_product_by_id,

 

 

 

:expects => [:int],

 

 

 

:returns => [Product]

 

 

end

 

 

 

Since we changed the API definition name, the automatic loading of the API

 

 

definition BackendApi (because it shares a prefix with the controller) will no

 

 

longer work. So, we’ll add a web_service_api( ) call to to the controller attach

 

 

it to the controller explicitly. We also add some code to the method bodies

 

 

and make the signatures match up with the API.

 

File 132

class BackendController < ApplicationController

 

 

wsdl_service_name 'Backend'

 

web_service_api ProductApi web_service_scaffold :invoke def find_all_products

Product.find(:all).map{ |product| product.id } end

def find_product_by_id(id) Product.find(id)

end end

Prepared exclusively for Rida Al Barazi

Report erratum

THE API DEFINITION 414

Figure 20.1: Web Service Scaffolding Lets You Test APIs

There are a couple of important things in the above example controller that may not immediately be obvious. The wsdl_service_name( ) method associates a name with the service that will be used in generated Web Services Definition Language (WSDL). It is not necessary, but setting it is recommended. The web_service_scaffold( ) call acts like the standard Action Pack scaffolding. This provides a way to execute web service methods from a web browser while in development and is something we will want to remove in production.

Now that we’ve implemented the service, and the scaffolding is in place, we can test it by navigating to the scaffold action (we passed its name as first parameter to web_service_scaffold( ) above). Figure 20.1 , shows the result of navigating to the scaffold in a browser.

Prepared exclusively for Rida Al Barazi

Report erratum

THE API DEFINITION

415

Method Signatures

AWS API declarations use api_method( ) to declare each method in the

web service interface. These declarations use signatures to specify the signatures method’s calling convention and return type.

A signature is an array containing one or more parameter specifiers. The parameter specifier tells AWS what type of value to expect for the corresponding parameter and, optionally, the name of the parameter.

api_method( ) accepts the :expects and :returns options for specifying signatures. The :expects option indicates the type (and optionally the name) of each of our method’s parameters. The :returns option gives the type of the method’s return value.

If we omit :expects, AWS will raise an error if remote callers attempt to supply parameters. If we omit :returns, AWS will discard the method return value, returning nothing to the caller. The presence of either option will cause AWS to perform casting to ensure the following.

The method input parameters are of the correct type by the time the method executes.

The value returned by the method body is of the correct type before returning it to the remote caller.

parameter specifiers

Format of Parameter Specifiers

Parameter specifiers are one of the following.

A symbol or a string identifying one of the Action Web Service base types

The Class object of a custom structured type (such as an ActionWebService::Struct or ActiveRecord::Base; see Section 20.2, Structured Parameter Types, on page 417)

A single-element array containing an item from (1) or (2)

A single-element hash containing as a key the name of parameter and one of (1), (2), or (3) as a value

For example, the following are valid signatures.

[[:string]]

A string array parameter.

Prepared exclusively for Rida Al Barazi

Report erratum

THE API DEFINITION 416

Parameter Names

Notice that we didn’t name the method parameters in the :expects signature for the example ProductApi. Naming the parameters in :expects is not necessary, but without names, the generated WSDL will not have descriptive parameter names, making it less useful to external developers.

[:bool]

A boolean parameter.

[Person]

A Person structured-type parameter.

[{:lastname=>:string}]

A string parameter, with a name of lastname in generated WSDL.

[:int, :int]

Two integer parameters.

Base Parameter Types

For simple types like numbers, strings, booleans, dates, and times, AWS defines a set of names that can be used to refer to the type in a signature instead of using the possibly ambigious Class object.

We can use either the symbol or the corresponding string as a parameter specifier.

:int

An integer number parameter.

:string

A string value.

:base64

Use this to receive binary data. When the remote caller supplies a value using the protocol’s Base64 type, and :base64 was used in the signature, the value will be decoded to binary by the time our method sees it.

:bool

A boolean value.

Prepared exclusively for Rida Al Barazi

Report erratum