Search code examples
rmultipartform-datarestrserve

Multipart/form-data with RestRserve


I would like to expose an endpoint that accepts multipart/form-data, parses the multipart content and returns a csv-file. (the multipart input contains a csv dataset and processing instructions)

I’ve done this with plumber using Rook::Multipart$parse() as suggested here. Because plumber doesn’t support parallel requests, I’d like to re-implement this with RestRserve. The following won’t work – plumber’s inputs are of class environment (which Rook::Multipart$parse() assumes) whereas RestRserve’s inputs are of class Request R6.

application = Application$new(content_type = "text/plain")
application$add_post("/echo", function(req, res) {
  multipart <- Rook::Multipart$parse(req$body)
  dta <- read_csv(multipart$dta$tempfile, trim_ws=FALSE)
  res$set_body(dta)
})

Any ideas on how to get multipart/form-data input to work with RestRserve?


Solution

  • RestRserve parses multipart body when process the incoming request. As result you have a raw request$body and metatdata in the request$files. Request object also provides a get_file method to extract body content. Let me show example for the app and request:

    # load packages
    library(readr)
    library(callr)
    library(httr)
    
    # run RestRserve in the background
    ps <- r_bg(function() {
      library(RestRserve)
      library(readr)
    
      app = Application$new(content_type = "text/plain")
      app$add_post(
        path = "/echo",
        FUN = function(request, response) {
          # for debug
          str(request$body)
          str(request$files)
          # extract multipart body field
          cnt <- request$get_file("csv") # 'csv' from the upload form field
          # parse CSV
          dt <- read_csv(cnt)
          # for debug
          str(dt)
          # do something with dt
          identity(dt)
          # write result to temp file
          tmp <- tempfile()
          write_csv(dt, tmp)
          # set output body
          response$set_body(c(tmpfile = tmp))
          # or simply response$set_body(format_csv(dt))
        }
      )
    
      backend = BackendRserve$new()
      backend$start(app, http_port = 65080)
    })
    
    # wait for up
    Sys.sleep(2L)
    
    # check is alive
    ps$is_alive()
    
    # prepare CSV to upload
    tmp <- tempfile()
    write_csv(head(iris, 5), tmp)
    
    # POST request with file
    rs <- POST(
      url = "http:/127.0.0.1:65080/echo",
      body = list(csv = upload_file(tmp)),
      encode = "multipart"
    )
    # get response content
    cat(content(rs))
    
    # read log from the RestRserve
    cat(ps$read_output())
    
    # kill background prcoess
    ps$kill()
    

    See ?Request for more details about fields and methods in this class.