(This post was motivated by a talk by @jnolis at CascadiaRConf 2021)
Recent versions of Shiny have an undocumented feature for handling POST requests that are not associated with any specific Shiny session. (Note that this functionality is missing our normal level of polish; it's a fairly low-level hook that I needed to make some things possible, but doesn't make anything easy.)
In a nutshell, it works by replacing your traditional ui
object with a function(req)
, and then marking that function with an attribute indicating that it knows how to handle both GET
and POST
:
library(shiny)
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(
# as normal...
)
} else if (identical(req$REQUEST_METHOD, "POST")) {
# Handle the POST
query_params <- parseQueryString(req$QUERY_STRING)
body_bytes <- req$rook.input$read(-1)
# Be sure to return a response
httpResponse(
status = 200L,
content_type = "application/json",
content = '{"status": "ok"}'
)
}
}
attr(ui, "http_methods_supported") <- c("GET", "POST")
server <- function(input, output, session) {
# same as usual
}
shinyApp(ui, server)
Whether the request is a GET or POST, the response from the ui
function can either be:
- A normal Shiny UI object, which will be handled in the usual way
- A
shiny::httpResponse()
object, which will be returned to the client verbatim(-ish) NULL
if the request is not applicable; Shiny will fall back to other logic, and ultimately return 404 if appropriate- Async is supported, you can return a promise that resolves to any of the above
The example above works with GET or POST, but only on the root (/
) URL path. If you want to handle GET and/or POST requests on other URLs, like /callback
or whatever, the shinyApp
function has a uiPattern
argument that you can use to control exactly which URL paths should be routed to the ui
function (for example, uiPattern = ".*"
). The ui
function can then look at req$PATH_INFO
to see what the actual URL path was.
@ismirsehregal
I think I found the root cause in https://github.com/rstudio/shiny/blob/main/R/server.R#L395
httpuv
is handling the requests before they hit the R handlers. In the above line/session
is excluded. Andhits the R handlers.
So to make /postpath work you would have to somehow get
"postpath"=excludeStaticPath()
into.globals$resourcePaths
.Something like (this does not work)
Clean solution would be if
addResourcePath
((https://github.com/rstudio/shiny/blob/main/R/server-resource-paths.R#L44) could acceptexcludeStaticPath()
.