在R闪亮应用程序中接受HTTP请求

Jam*_*cox 18 r shiny shiny-server

我有一个闪亮的应用程序,我需要从另一台服务器获取其数据,即当闪亮的应用程序打开时,另一台服务器向闪亮的应用程序发送请求以打开应用程序并为其提供所需的数据.

为了模拟这个,当我在firefox中打开应用程序时,我可以将以下内容发送到R闪亮的应用程序:

 http://localhost:3838/benchmark-module/?transformerData=data/TransformerDataSampleForShiny.json
Run Code Online (Sandbox Code Playgroud)

这是一个简单的get请求,它将带有内容"data/TransformerDataSampleForShing.json"的名为"Transformer Data"的sting发送到闪亮的应用程序.

当我使用代码时它工作正常:

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

但是当我想做同样的事情,除了硬编码字符串"data/TransformerDataSampleForShiny.json"之外,我想从上面的http请求中接收该字符串.我该怎么做呢??我试过这段代码:

shinyServer(function(input, output) {
jsonFile <- input$transformerData
JSONdata <- fromJSON(jsonFile)
Run Code Online (Sandbox Code Playgroud)

我也尝试过:

....
jsonFile <- input$TransformerData
Run Code Online (Sandbox Code Playgroud)

但这些都没有奏效.

所以主要的问题是,我如何编码来接收HTTP请求?我想从POST请求接收来自HTTP GET请求和/或JSON文件的字符串.

只是为了澄清我不想发送帖子或从R获得请求.我想收到它们.我无法使用httr包或httpRequest包进行接收

非常感谢!

Xin*_*Yin 18

@ jdharrison的答案是你如何处理GETShiny请求的一种方式.不幸的是,他或她的说法

不幸的是,闪亮的不会处理POST请求.

严格来说,并非100%准确.

可以POST在函数的帮助下处理Shiny中的请求session$registerDataObj.在此示例中可以找到使用此函数的示例.基本上,通过调用registerDataObj函数,它返回一个唯一的请求URL,您可以向其发起GETPOST请求.但是,我不认为上述示例在您的问题的上下文中非常有用,因为:

  1. 在这个例子中没有明确使用AJAX.相反,该示例利用registerDataObj创建PNG文件处理程序,并直接将URL绑定到标记的src属性<img>.
  2. 它仍在使用GET请求POST.

但是,你可以复用这个方便的功能来处理这两个GET并且POST非常好.请考虑以下示例:

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

})
Run Code Online (Sandbox Code Playgroud)

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>
')
    )
  )
))
Run Code Online (Sandbox Code Playgroud)

现在,说我输入这些测试输入:

一些测试输入

然后点击"Upload",你向Shiny服务器提交一个POST请求,根据我们的R代码,它将把你的浏览器的POST请求流作为响应转发给你.

例如,我得到:

------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--
Run Code Online (Sandbox Code Playgroud)

显然,只要您正确编写POST请求处理器,您不仅可以处理文本数据,还可以处理文件上载.虽然这可能不是微不足道的,但至少它是合理的,完全可行的!

当然,你有一个明显的缺点,就是你需要以某种方式将这个唯一的请求URL传递给客户端,或者发送请求的服务器.但从技术上讲,有很多方法可以做到这一点!


jdh*_*son 13

您可以使用收到GET请求session$clientData.示例运行以下操作

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)
Run Code Online (Sandbox Code Playgroud)

然后导航到

http://127.0.0.1:5678/?transformerData=data/TransformerDataSampleForShiny.json
Run Code Online (Sandbox Code Playgroud)

有关公开POST请求的方法,请参阅@Xin Yin答案.

  • 关于`shiny不处理POST请求'的陈述是*错误*.您可以使用一些技巧在Shiny中创建POST请求处理程序. (3认同)