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!
@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:
registerDataObj
to create a PNG file handler, and it directly binds the URL to the src
property of <img>
tag.GET
request not POST
. But, you can multiplex this handy function to handle both GET
and POST
perfectly fine.
Consider the following example:
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))
})
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:
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!
You can receive GET requests using session$clientData
. An example run the following
library(shiny)
runApp(list(
ui = bootstrapPage(
textOutput('text')
),
server = function(input, output, session) {
output$text <- renderText({
query <- parseQueryString(session$clientData$url_search)
paste(names(query), query, sep = "=", collapse=", ")
})
}
), port = 5678, launch.browser = FALSE)
and navigate to
http://127.0.0.1:5678/?transformerData=data/TransformerDataSampleForShiny.json
See @Xin Yin answer for a method to expose POST requests.
Exciting update: As of Jan 2017, it was announced on RStudio Conf that this will be built into shiny in a future version (start watching at minute 15:00).
As of May 2017, this API feature is still not released, but I'm hoping it'll come soon.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With