Accept HTTP Request in R shiny application

James Willcox picture James Willcox · Aug 14, 2014 · Viewed 12.8k times · Source

I have a shiny app that I have made that needs to get its data from another server, i.e. the other server when the shiny app is opened sends a request to the shiny app to open the app and feed it the data that it needs.

To simulate this I can send the following to the R shiny app when I open the app in firefox:

 http://localhost:3838/benchmark-module/?transformerData=data/TransformerDataSampleForShiny.json

This is a simple get request that sends the sting called : "Transformer Data" with contents "data/TransformerDataSampleForShing.json" to the shiny app.

When I use the code it works fine:

#(Abridged code, I am only showing the start of the code)
 shinyServer(function(input, output) {
 jsonFile <- "data/TransformerDataSampleForShiny.json"
 JSONdata <- fromJSON(jsonFile)

but when I want to do the exact same thing except rather than hard coding the string "data/TransformerDataSampleForShiny.json" i want to receive that string from the http request above. How do I do this?? I have tried the code:

shinyServer(function(input, output) {
jsonFile <- input$transformerData
JSONdata <- fromJSON(jsonFile)

and I have also tried:

....
jsonFile <- input$TransformerData

but none of these have worked.

SO the main question is, is how do I code to recieve HTTP requests? I would like to receive strings from HTTP GET requests and or JSON files from POST requests.

Just to clarify I DONT want to send post or get requests from R. I want to receive them. I can't use the httr package or the httpRequest package for receiving

Thanks so much!

Answer

Xin Yin picture Xin Yin · Aug 14, 2014

@jdharrison's answer is one way how you can handle GET requests in Shiny. Unfortunately, his or her statement that

shiny doesnt handle POST requests unfortunately.

is not, strictly speaking, 100% accurate.

You can handle POST requests in Shiny with the help of the function session$registerDataObj. An example of using this function can be found in this example. Basically, by calling registerDataObj function, it returns a unique request URL to which you can initiate either GET or POST requests. However, I wouldn't consider the example above very helpful in the context of your question because:

  1. There is no explicit use of AJAX in this example. Rather, the example exploits registerDataObj to create a PNG file handler, and it directly binds the URL to the src property of <img> tag.
  2. It is still using GET request not POST.

But, you can multiplex this handy function to handle both GET and POST perfectly fine. Consider the following example:

server.R

library(shiny)

shinyServer(function(input, output, session) {
  api_url <- session$registerDataObj( 
    name   = 'api', # an arbitrary but unique name for the data object
    data   = list(), # you can bind some data here, which is the data argument for the
                     # filter function below.
    filter = function(data, req) {
      print(ls(req))  # you can inspect what variables are encapsulated in this req
                      # environment
      if (req$REQUEST_METHOD == "GET") {
        # handle GET requests
        query <- parseQueryString(req$QUERY_STRING)
        # say:
        # name <- query$name
        # etc...
      } 

      if (req$REQUEST_METHOD == "POST") {
        # handle POST requests here

        reqInput <- req$rook.input

        # read a chuck of size 2^16 bytes, should suffice for our test
        buf <- reqInput$read(2^16)

        # simply dump the HTTP request (input) stream back to client
        shiny:::httpResponse(
          200, 'text/plain', buf
        )
      }          
    }
  )

  # because the API entry is UNIQUE, we need to send it to the client
  # we can create a custom pipeline to convey this message
  session$sendCustomMessage("api_url", list(url=api_url))

})

ui.R

library(shiny)

shinyUI(fluidPage(
  singleton(tags$head(HTML(
    '
  <script type="text/javascript">
    $(document).ready(function() {
      // creates a handler for our special message type
      Shiny.addCustomMessageHandler("api_url", function(message) {
        // set up the the submit URL of the form
        $("#form1").attr("action", "/" + message.url);
        $("#submitbtn").click(function() { $("#form1").submit(); });
      });
    })
  </script>
'
  ))),
  tabsetPanel(
    tabPanel('POST request example',
             # create a raw HTML form
             HTML('
<form enctype="multipart/form-data" method="post" action="" id="form1">
    <span>Name:</span>
    <input type="text" name="name" /> <br />
    <span>Passcode: </span> <br />
    <input type="password" name="passcode" /><br />
    <span>Avatar:</span>
    <input name="file" type="file" /> <br />
    <input type="button" value="Upload" id="submitbtn" />
</form>
')
    )
  )
))

Now, say I enter these test input:

Some test input

Then hit "Upload", you submit a POST request to the Shiny server, which, based on our R code, will dump your browser's POST request stream to you as response.

For example, I get:

------WebKitFormBoundary5Z0hAYXQXBHPTLHs
Content-Disposition: form-data; name="name"

foo
------WebKitFormBoundary5Z0hAYXQXBHPTLHs
Content-Disposition: form-data; name="passcode"

bar
------WebKitFormBoundary5Z0hAYXQXBHPTLHs
Content-Disposition: form-data; name="file"; filename="conductor.png"
Content-Type: image/png

‰PNG


IHDR  X   ¦   5Š_       pHYs  a  a¨?§i  ÕiTXtXML:com.adobe.xmp     <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.1.2">
   <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
      <rdf:Description rdf:about=""
            xmlns:tiff="http://ns.adobe.com/tiff/1.0/">
         <tiff:Compression>5</tiff:Compression>
         <tiff:PhotometricInterpretation>2</tiff:PhotometricInterpretation>
         <tiff:Orientation>1</tiff:Orientation>
      </rdf:Description>
   </rdf:RDF>
</x:xmpmeta>
# here I removed the binary file content
------WebKitFormBoundary5Z0hAYXQXBHPTLHs--

Clearly you can handle not only text data, but also file uploads as long as you write a POST request processor appropriately. Although this may not be trivial, but at least it is plausible and totally doable!

Of course, you have the obvious drawback that somehow you need communicate this unique request URL to the client, or to the server which will initiate the request. But technically there are many ways you can do that!