Warning Older Docs! - You are viewing documentation for a previous released version of RhoMobile Suite.

Connecting Directly to Web Services with Rhodes

Rhodes provides another utility for connecting to backend services besides the SyncEngine 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 methods to asynchronously make calls to http(s) services.

  • cancel - Cancel the current AsyncHttp call.
  • download_file - Download a file to the specified filename.
  • get - Perform an HTTP GET request to the specified URL.
  • post - Perform HTTP POST request to the specified URL.
  • upload_file - Upload the specified file using HTTP POST.

Get Example

You can perform an HTTP GET request to the specified :url. You can also provide an optional hash of :headers and :callback_param.

Rho::AsyncHttp.get(
  :url => "http://www.example.com",
  :headers => {"Cookie" => cookie},
  :callback => (url_for :action => :httpget_callback)
)

Example using Basic Auth:

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:

result = Rho::AsyncHttp.get(
  :url => "http://www.apache.org/licenses/LICENSE-2.0"
)
@get_result = res["body"]

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 :url. As with get, you can specify optional arguments.

# :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.

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:

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:

# :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 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.

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:

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

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: link text

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=”, 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.

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.

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. It makes a AsyncHttp.get call to a test web service. Then it parses the web service response with rexml and displays the result.

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
Back to Top