简体   繁体   中英

R Shiny REST API communication

I have a shiny app that takes a JSON input file, runs it through a classifier and returns a classified JSON object. I want the app to be able to communicate with an API. I want the API to post a file to the Shiny App which will do its work and return a classified object. Basically I want the Shiny app to sit in the background until a file is posted and then do its work. I know that I can use GET from the httr package to get a file from a url. I can put this in the shiny.server file which is fine if I know the file name for the get command

However the filenames coming from the API will be different. So is there any way that I can make this dynamic according to the Post request that comes from the API.

If you do not have to use Shiny, you can use openCPU . OpenCPU provides each of your R packages as REST service automatically. I work with OpenCPU and it works fine! It is the easiest way to use R from another program.

By now library( plumber ) needs to be mentioned as an alternative in this context, however the following example is showing how to handle POST requests directly in shiny .

It is based on Joe Cheng's gist here , which suggests to add an attribute "http_methods_supported" to the UI and use httpResponse to answer the requests.

The below code starts a shiny app in a background R process (This is done only to have a single single file MRE - of course, you can put the app in a separate file and remove the r_bg -line). After the app is launched the parent process sends the iris data.frame to the UI.

In the UI function the req$PATH_INFO is checked (see uiPattern = ".*" ), then the numerical columns are multiplied by 10 ( query_params$factor ) and send back as a json string.

library(shiny)
library(jsonlite)
library(callr)
library(datasets)

ui <- function(req) {
  # The `req` object is a Rook environment
  # See https://github.com/jeffreyhorner/Rook#the-environment
  if (identical(req$REQUEST_METHOD, "GET")) {
    fluidPage(
      h1("Accepting POST requests from Shiny")
    )
  } else if (identical(req$REQUEST_METHOD, "POST")) {
    # Handle the POST
    query_params <- parseQueryString(req$QUERY_STRING)
    body_bytes <- req$rook.input$read(-1)
    if(req$PATH_INFO == "/iris"){
      postedIris <- jsonlite::fromJSON(rawToChar(body_bytes))
      modifiedIris <- postedIris[sapply(iris, class) == "numeric"]*as.numeric(query_params$factor)
      httpResponse(
        status = 200L,
        content_type = "application/json",
        content = jsonlite::toJSON(modifiedIris, dataframe = "columns")
      )
    } else {
      httpResponse(
        status = 200L,
        content_type = "application/json",
        content = '{"status": "ok"}'
      )
    }
  }
}
attr(ui, "http_methods_supported") <- c("GET", "POST")

server <- function(input, output, session) {}

app <- shinyApp(ui, server, uiPattern = ".*")
# shiny::runApp(app, port = 80, launch.browser = FALSE, host = "0.0.0.0")
shiny_process <- r_bg(function(x){ shiny::runApp(x, port = 80, launch.browser = FALSE, host = "0.0.0.0") }, args = list(x = app))

library(httr)
r <- POST(url = "http://127.0.0.1/iris?factor=10", body = iris, encode = "json", verbose())
recievedIris <- as.data.frame(fromJSON(rawToChar(r$content)))
print(recievedIris)
shiny_process$kill()

Please also check this related PR which is providing further examples (also showing how to use session$registerDataObj ) and is aiming at a better description of the httpResponse function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM