21

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

为了模拟这一点,当我在 Firefox 中打开应用程序时,我可以将以下内容发送到 R Shiny 应用程序:

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

这是一个简单的获取请求,它将名为“Transformer Data”的字符串发送到闪亮的应用程序,内容为“data/TransformerDataSampleForShing.json”。

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

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

但是当我想做完全相同的事情时,除了对字符串“data/TransformerDataSampleForShiny.json”进行硬编码之外,我想从上面的 http 请求中接收该字符串。我该怎么做呢??我已经尝试过代码:

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

我也试过:

....
jsonFile <- input$TransformerData

但这些都没有奏效。

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

只是为了澄清我不想发送帖子或从 R 获取请求。我想接收它们。我不能使用 httr 包或 httpRequest 包来接收

非常感谢!

4

4 回答 4

24

@jdharrison 的回答是您GET在 Shiny 中处理请求的一种方式。不幸的是,他或她的声明

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

严格来说,不是 100% 准确的。

可以POST借助函数在 Shiny中处理请求session$registerDataObj在这个例子中可以找到一个使用这个函数的例子。基本上,通过调用registerDataObj函数,它返回一个唯一的请求 URL,您可以向该 URL 发起一个GET或多个POST请求。但是,对于您的问题,我认为上面的示例不会很有帮助,因为:

  1. 此示例中没有明确使用 AJAX。相反,该示例利用registerDataObj创建一个 PNG 文件处理程序,并将 URL 直接绑定到tag的src属性。<img>
  2. 它仍在使用GETrequest not POST

但是,您可以多路复用这个方便的功能来处理这两者GET并且POST非常好。考虑以下示例:

服务器.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))

})

用户界面

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

现在,假设我输入这些测试输入:

一些测试输入

然后点击"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--

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

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

于 2014-08-14T02:55:21.010 回答
15

您可以使用 接收 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)

并导航到

http://127.0.0.1:5678/?transformerData=data/TransformerDataSampleForShiny.json

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

于 2014-08-13T23:06:29.940 回答
2

激动人心的更新:截至 2017 年 1 月,在 RStudio Conf 上宣布这将在未来版本中内置到闪亮中(从 15:00 开始观看)。

截至 2017 年 5 月,此 API 功能仍未发布,但我希望它会尽快发布。

于 2017-05-27T02:15:22.367 回答
0

不确定这是否有帮助,但是当我想将 React-Django 堆栈换成 Shiny-Django 堆栈时,这对我来说是一个问题——这样做的好处是 Shiny 不适合创建真正复杂的 UI , 只使用 R 代码 - 但问题是确保应用程序中有完整的 CRUD 功能以及整个帖子到闪亮的问题

我发现它非常好用,只是将我的所有数据保存在 Django 应用程序中,Django Rest Framework 管理所有安全性并让 Shiny App 调用它,例如:

DJANGO_BASE_URL = "your-url-to-django-app"
DJANGO_API_KEY= "authToken for django, something like 'Token TJDU473C738383...'" 

APIResults <- httr::GET(paste0(DJANGO_BASE_URL, "accounts-api/v1/current-user/"),
                            add_headers(Authorization = DJANGO_API_KEY))
gCurrentUser <- fromJSON(content(APIResults, "text"))

在点击 actionButton 后可能会触发的发布请求或类似的东西

   url <- "your-post-url"
    body <- list(formField = "blah")
     r <- POST(url, body = body, encode = "json")

如果您想让 Shiny 不需要推出自己的身份验证,这似乎也是一个不错的解决方案。您可以非常轻松地管理登录/注销应用程序的所有方面,因为整个事情只是 django rest 框架中数据的前端。我也采用了这种方法,因为我想避免那些看起来很酷但在部署到 RConnect(至少对我而言)时往往会导致困难的 ShinyAuth 类型的包,并且 django rest 框架在安全性方面非常好。

于 2021-10-16T06:08:22.073 回答