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 API
Below is the list of available `AsyncHttp` methods you can use to asynchronously make calls to http(s) services:

### Common Parameters
* `:url` - URL of the request.
* `:headers` - Hash of headers to send with the request.
* `:callback` - Callback action to execute when the request is done. This must be a controller action.
* `:callback_param` - (optional) Parameters to send to the callback. Parameters values should be url encoded:

        :callback_param => "action=text&title=#{Rho::RhoSupport.url_encode(@params['page_title'])}"
        #values will be decoded automatically in callback

* `:authentication` - (optional) Send Basic Auth header with request.  This takes the form:

		:authentication => {
		  :type => :basic,
		  :username => "john",
		  :password => "secret"
		}

* `:ssl_verify_peer` - (optional) Verify SSL certificates, `true` by default.

### `get(:url, :headers, :callback, :callback_param)`
Perform 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(:url, :headers, :body, :callback, :callback_param)`
Perform 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_file(:url, :headers, :filename, :callback, :callback_param)`
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_file(:url, :headers, :filename, :body, :callback, :callback_param)`
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"
        }
      ]
    )

### `cancel(cancel_callback = "*")`
Cancel the current `AsyncHttp` call.  Defaults to "*", which cancels all requests.

	:::ruby
	AsyncHttp.cancel

## AsyncHttp Callback
As you noticed with each of the code samples above, we specified a `:callback` action.  This will execute with the `AsyncHttp` request is completed.

### Callback Parameters
The following parameters are available in an `AsyncHttp` callback:

* `@params["body"]` - The body of the HTTP response.

**NOTE: In the case of a JSON response (Content-Type="application/json"), the `@params["body"] will be parsed automatically and contain a ruby data structure.  Otherwise, `@params["body"]` contains the raw response body.**

**NOTE: In the case of an XML response (Content-Type="application/xml"), Rhodes can automatically parse the `@params["body"]` as well if you enable the ["rexml extension"](/rhodes/extensions#rhodes-extensions) in your application.**


* `@params["headers"]` - A hash containing the response headers.
* `@params["cookies"]` - A the server cookies parsed and usable for subsequent requests.
* `@params["http_error"]` - HTTP error code if response code was not 200.

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