3

Consider the following example of a PUT request, using Plumber (R API):

example_body <- list(
  a=1,
  b=2,
  c=3
)
#* Example PUT endpoint
#* @serializer unboxedJSON list(na = NULL)
#* @param body:object
#* @put /my_example
function(req, body = example_body) {
  print(body)
  print(req$body$body)
  result <- body$a + body$b + body$c
  return(result)
}

Testing this example from Swagger works great: Testing PUT endpoint in Swagger using the given example

The two print statements show that values of body and req$body$body are identical. However, sending a request from anywhere else (not Swagger) means the body is accessed as just req$body!

A super dirty hack is to put something like the following at the top of the function:

if (grepl(pattern = "/__docs__/|/__swagger__/|/openapi.json",x = req$HTTP_REFERER)) {
   req$body <- req$body$body
}

But this is undesirable when you have lots of endpoints. So, to solve this madness, how can the example be used in Swagger while also allowing the endpoint to be used with a real request containing a body?

fifthace
  • 506
  • 1
  • 10
  • 33

1 Answers1

0

I think there is confusion about what your route does currently. As written above, the endpoint expects the REQUEST body to consist of a single OBJECT named "body" (hence the need for req$body$body). If you renamed your function parameter, this might be more obvious:

example_data <- list(
  a=1,
  b=2,
  c=3
)
#* Example PUT endpoint
#* @serializer unboxedJSON list(na = NULL)
#* @param body:object
#* @put /my_example
function(req, data = example_data) {
  print(body)
  print(req$body$data)
  result <- data$a + data$b + data$c
  return(result)
}

Plumber automatically parses the REQUEST body and then passes values as named arguments to the function. So if what you really want is the values 'a', 'b', 'c' to be in the (request) body, you would define it as follows:

#* Example PUT endpoint
#* @serializer unboxedJSON list(na = NULL)
#* @param a
#* @param b
#* @param c
#* @put /my_example
function(req, a = 1, b = 2, c = 3) {
  print(list(a = a, b = b, c = c))
  print(req$body)
  result <- a + b + c
  return(result)
}

Otherwise, when you compose the body in a non-Swagger way, just copy the exactly what the Swagger show to make sure it's the same.

Swagger Input

Using Postman: Postman Input

Or in R

library(httr2)
example_body <- list(
  data = list(
    a=1,
    b=2,
    c=3
  )
)

request("http://127.0.0.1:4280/my_example") |> 
  req_method("PUT") |> 
  req_body_json(
    data = example_body
  ) |>
  req_perform() |> 
  resp_body_json()
Marcus
  • 3,478
  • 1
  • 7
  • 16
  • 1
    My interpretation of this answer is that you cannot have a Swagger example where you click Try it out button and a request that sends individual parameters in the body (rather than explicitly defined parameters). I get you can do it with individual parameters, but the front end developers I work with prefer everything in the body. – fifthace Mar 20 '23 at 21:18