Permalink
Switch branches/tags
Find file
Fetching contributors…
Cannot retrieve contributors at this time
267 lines (208 sloc) 10.1 KB
Connecting Directly to Web Services with Rhodes
===
Rhodes provides another utility for connecting to backend services besides the [`SyncEngine`](/rhodes/synchronization) called `AsyncHttp`. Your application can use the `AsyncHttp` library to interact with web services, pull remote images, etc.
## AsyncHttp Method Examples
Refer to the [AsyncHttp API](/rhodesapi/asynchttp-api) methods to asynchronously make calls to http(s) services.
* [cancel](/rhodesapi/asynchttp-api#cancel) - Cancel the current AsyncHttp call.
* [download_file](/rhodesapi/asynchttp-api#downloadfile) - Download a file to the specified filename.
* [get](/rhodesapi/asynchttp-api#get) - Perform an HTTP GET request to the specified URL.
* [post](/rhodesapi/asynchttp-api#post) - Perform HTTP POST request to the specified URL.
* [upload_file](/rhodesapi/asynchttp-api#uploadfile) - Upload the specified file using HTTP POST.
### Get Example
You can perform an HTTP GET request to the specified <code>:url</code>. You can also provide an optional hash of <code>:headers</code> and <code>:callback_param</code>.<a id="asynchttp-get-ex" />
:::ruby
Rho::AsyncHttp.get(
:url => "http://www.example.com",
:headers => {"Cookie" => cookie},
:callback => (url_for :action => :httpget_callback)
)
Example using Basic Auth:
:::ruby
Rho::AsyncHttp.get(
:url => "http://www.example.com",
:headers => {"Cookie" => cookie},
:callback => (url_for :action => :httpget_callback),
:authentication => {
:type => :basic,
:username => "john",
:password => "secret"
}
)
Example of synchronous call:
:::ruby
result = Rho::AsyncHttp.get(
:url => "http://www.apache.org/licenses/LICENSE-2.0"
)
@get_result = res["body"]
**NOTE: WARNING! Do NOT use synchronous calls unless you know what you are doing. This is a blocking call and will cause your UI to freeze.**
### Post Example
You can perform an HTTP POST request to the specified <code>:url</code>. As with get, you can specify optional arguments.<a id="asynchttp-post-ex" />
:::ruby
# :post HTTP POST body to send with request.
# :http_command (optional) Use different HTTP method
# (i.e. "put").
Rho::AsyncHttp.post(
:url => "https://www.example.com",
:headers => {"Cookie" => cookie},
:body => "username=john&password=secret",
:callback => url_for(:action => :httppost_callback),
:callback_param => "post=complete"
)
### Download Example
You can download a file to the specified filename.<a id="asynchttp-download_file-ex" />
:::ruby
file_name = File.join(Rho::RhoApplication::get_base_app_path, "test.jpg")
# :filename Full path to download file target.
Rho::AsyncHttp.download_file(
:url => "http://www.google.com/images/logos/ps_logo2.png",
:filename => file_name,
:headers => {},
:callback => url_for(:action => :httpdownload_callback),
)
### Upload Example
Upload the specified file using HTTP POST:<a id="asynchttp-upload_file-ex" />
:::ruby
file_name = File.join(Rho::RhoApplication::get_base_app_path, "myfile.txt")
# :filename Full path to download file target.
# :post HTTP POST body to send with request.
Rho::AsyncHttp.upload_file(
:url => "http://example.com/receive_file",
:filename => file_name,
:body => "" #=> leave blank, AsyncHttp will fill in multipart body
:headers => {"Content-Type"=>"text/plain"}, #=> used as body text content type
:callback => url_for(:action => :httpupload_callback),
:callback_param => "" )
You can also send multiple files in a single `upload_file` request:
:::ruby
# :multipart Array of hashes containing
# file information.
#
# :multipart[:filename] Name of file to be uploaded.
#
# :multipart[:filename_base] (optional) Base directory containing
# the :filename.
# :multipart[:name] (optional) File type, defaults
# to "blob".
#
# :multipart[:content_type] (optional) Content-Type header,
# defaults to "application/octet-stream".
Rho::AsyncHttp.upload_file(
:url => "some_url",
:multipart => [
{
:filename => file_name,
# if missed base name from file path used
:filename_base => "files_to_upload",
:name => "image",
:content_type => "application/octet-stream"
},
# You can specify file content inline.
{
:body => "upload test",
:name => "upload_body_test",
:content_type => "plain/text"
}
]
)
## AsyncHttp Callback
As you noticed with each of the code samples above, we specified a `:callback` action. This callback will execute when the `AsyncHttp` request is completed.
Refer to [AsyncHttp API](/rhodesapi/asynchttp-api#callback-parameters) for a discussion of the callback parameters.
## AsyncHttp and Animated Transitions
Adding an animated transition to an `AsyncHttp` request requires some small setup and is useful for displaying a smoother user experience.
To enable an animated transition, the controller action must set a `"Wait-Page"` response header after making the `AsyncHttp` call. The response header tells the user interface that an `AsyncHttp` request has been spawned and that the rendered view should be treated as a transient page, it will not be added to the navigation history.
:::ruby
def async_show
Rho::AsyncHttp.get(
:url => "http://rhostore.herokuapp.com/products/#{@params["product_id"]}.json",
:callback => url_for(:action => :show_callback),
)
@response["headers"]["Wait-Page"] = "true"
render :action => :waiting
end
This example renders a waiting screen while awaiting a response from the `AsyncHttp` request. The `:waiting` page is transient and will not be added to the navigation history, which means clicking back won't open the page.
The `AsyncHttp` callback can render the response by calling `render_transition`. This function is defined in `ApplicationHelper` so make sure you `include` it in your controller. The `render_transition` function works much like `render` except that it will animate a transition from the previous page.
Below, a product model is created using the response from the web service and then calling `render_transition`, which leverages the show view template:
:::ruby
include ApplicationHelper
def show_callback
if @params["status"] == "ok"
@product = Product.new(@params["body"]["product"])
@product.object = @product.id
render_transition :action => :show
else
# In this example, an error just navigates back to the index w/o transition.
WebView.navigate url_for :action => :index
end
end
**NOTE: To disable jQuery Mobile page caching (by default jQuery Mobile cachepages in the DOM) globally, look for cache control options in jQuery Mobile documentation**
You can disable page caching globally by using jQuery Mobile initialization option in layout.erb file:
$.mobile.page.prototype.options.domCache = true;
Also, you can disable caching on exact page transition with `data-dom-cache` attribute, like that: *<a href="foo/bar/baz" data-dom-cache="false">link text</a>*
### Note About Animated Transitions
If you deploy to platforms that don"t handle animated transitions (like Windows Mobile and BlackBerry), the controller will need to handle both cases. In your `AsyncHttp` request, you"ll need to set the `callback_param` with the `@request` variable.
There's a helper function called `caller_request_hash_to_query` defined in `ApplicationHelper` that you can invoke. The returned value is a string that looks like "_request=<json_request>", where `<json_request>` is the URL-encoded JSON representation of the `@request` value. This parameter is used to give the callback function some context of whether the user interface made the request with or without transition enabled.
:::ruby
include ApplicationHelper
def async_show
Rho::AsyncHttp.get(
:url => "http://rhostore.herokuapp.com/products/#{@params["product_id"]}.json",
:callback => url_for(:action => :show_callback),
:callback_param => caller_request_hash_to_query
)
@response["headers"]["Wait-Page"] = "true"
render :action => :waiting
end
In your callback function, the first thing you need to do is invoke `caller_request_query_to_hash` (also defined in `ApplicationHelper`) that deserializes the `@request` query parameter value passed in via `callback_param` shown in the example above. The function sets a `@caller_request` in the current context. You can then use it to determine if the user interface had transition enabled by inspecting the `"Transition-Enabled"` request header. For transitions, call `render_transition`, otherwise call `WebView.navigate`.
:::ruby
def show_callback
caller_request_query_to_hash
if @params["status"] == "ok"
@product = Product.new(@params["body"]["product"])
@product.object = @product.id
if @caller_request["headers"]["Transition-Enabled"] == "true"
render_transition :action => :show
else
WebView.navigate(
url_for(:action => :show, :id => @product.object)
)
end
else
WebView.navigate( url_for(:action => :index) )
end
end
## AsyncHttp Example
Here is a controller in the [Rexml sample from the System API Samples](http://github.com/rhomobile/system-api-samples). It makes a `AsyncHttp.get` call to a test web service. Then it parses the web service response with rexml and displays the result.
:::ruby
def webservicetest
Rho::AsyncHttp.get(
:url => "http://rhostore.herokuapp.com/products.xml",
:callback => url_for(:action => :httpget_callback),
)
render :action => :wait
end
def get_res
@@get_result
end
def get_error
@@error_params
end
def httpget_callback
if @params["status"] != "ok"
@@error_params = @params
WebView.navigate( url_for(:action => :show_error) )
else
@@get_result = @params["body"]
begin
require "rexml/document"
doc = REXML::Document.new(@@get_result)
puts "doc : #{doc}"
rescue Exception => e
puts "Error: #{e}"
@@get_result = "Error: #{e}"
end
WebView.navigate( url_for(:action => :show_result) )
end
end
def show_result
render :action => :webservicetest, :back => "/app/RexmlTest"
end