rcurlhttrhttr2

Status code in httr2 when uploading a file that already exists


I'm using the R package httr2 to upload files to a secure environment. If a file already exists, I'd like to receive a warning. Instead, I get a response status of 200 which, means "OK". However, the file is not updated on the server.

This is the code I use for the upload:

url <- "<server URL><Filepath on server>?action=upload"

req <- request(url) %>%
  req_method("POST") %>%
  req_headers("X-Auth-Token" = token) %>%
  req_body_multipart(uploadFile = curl::form_file(file_path))
resp <- req_perform(req, verbosity=3)

I get the following status information:

> resp_status(resp)
[1] 200
> resp_status_desc(resp)
[1] "OK"

However, the curl status message (obtained using verbosity=3 with req_perform) shows "code":20012,"type":"WARNING"}. But the curl status is not included in the resp object so I can't include a check that the upload went as intended.

If I do the same request in httr instead of httr2, then the status will also be 200 but I can use the content function to obtain the curl status.

response <- PUT(
  url,
  add_headers(`X-Auth-Token` = token),
  body = list(uploadFile = upload_file(file_path))
)
> response$status
[1] 200
> content(response)$status$code
[1] 20012

I am aware that I can append &overwrite=true to the URL but I don't want to automatically overwrite the file. It's my understanding that the PUT method is intended for existing files and that POST should be used for new files. However, the server (LSAF 5.4.2, a statistical computing environment by SAS) only accepts PUT.

Is there a way to programmatically extract the curl status code using httr2?


Solution

  • You can parse JSON response with resp_body_json().
    To override default HTTP error handling and to control if something in response body should turn response into error, check req_error() , it also lets you extract information, e.g. warnings from API.

    We can test this with https://httpbin.org/ , all our request arguments are included in JSON response.

    library(httr2)
    
    # convert into R error if type in JSON response is not "OK"
    f_is_error <- \(resp) resp_body_json(resp)$args$type != "OK"
    # in your case it might look something like 
    # f_is_error <- \(resp) resp_body_json(resp)$status$type == "WARNING"
    
    # extract (part of) JSON response as string for some context
    f_body <- \(resp) paste0("Something in response was not quite right: ",  
                             jsonlite::toJSON(resp_body_json(resp)$args, auto_unbox = TRUE))
    
    # generate JSON response that includes a warning
    request("https://httpbin.org/get?code=200012&type=WARNING") |> 
      req_error(is_error = f_is_error, body = f_body) |> 
      req_perform(verbosity = 2)
    #> -> GET /get?code=200012&type=WARNING HTTP/1.1
    #> ...
    #> <- HTTP/1.1 200 OK
    #> ...
    #> << {
    #> <<   "args": {
    #> <<     "code": "200012",
    #> <<     "type": "WARNING"
    #> <<   },
    #> <<   "headers": { ...
    #> ...
    #> << }
    #> Error in `req_perform()`:
    #> ! HTTP 200 OK.
    #> • Something in response was not quite right: {"code":"200012","type":"WARNING"}
    
    # generate JSON response with type "OK"
    request("https://httpbin.org/get?code=100012&type=OK") |> 
      req_error(is_error = f_is_error, body = f_body) |> 
      req_perform(verbosity = 2)
    #> -> GET /get?code=100012&type=OK HTTP/1.1
    #> ...
    #> <- HTTP/1.1 200 OK
    #> ...
    #> << {
    #> <<   "args": {
    #> <<     "code": "100012",
    #> <<     "type": "OK"
    #> <<   },
    #> <<   "headers": { ...
    #> ...
    #> << }
    #> <httr2_response>
    #> GET https://httpbin.org/get?code=100012&type=OK
    #> Status: 200 OK
    #> Content-Type: application/json
    #> Body: In memory (388 bytes)