I am currently writing a webhook on a heroku server and for some reason I am having difficulty obtaining the values stored in the body of the request as it always seems to turn up as NULL. My app.R script looks like all those in the guides:
#app.R
library(plumber)
library(tidyverse)
port <- Sys.getenv('PORT')
pr <- plumb("plumber.R")
pr$run(
host = '0.0.0.0',
port = as.numeric(port)
)
My plumber.R file starts like this.
library(plumber)
library(tidyverse)
#* Log some information about the incoming request
#* #filter logger
function(req){
print('LOGGER')
print( req)
cat(as.character(Sys.time()), "-",
req$REQUEST_METHOD, req$PATH_INFO, "-",
req$HTTP_USER_AGENT, "#", req$REMOTE_ADDR, "\n")
plumber::forward()
}
#* #filter bodyExists?
function(req, res){
print( 'BODY FILTER')
print( req$body)
if (is.null(req$body)){
res$status <- 404
return(list(error="Body not found"))
} else {
plumber::forward()
}
}
Every time it receives a request the console shows out this:
And stops because there is no body. I have tried sending it requests from multiple sources where I know a body is being sent with the request but for some reason plumber does not find it when it reaches my api. I have been following using https://github.com/virtualstaticvoid/heroku-plumber-app as a template and I don't see what Im doing different from them.
Was supposed to use req$postBody rather than req$body
Plumber should be able to parse the body of the post request automatically unless the content type is incorrect or unspecified.
In my case, I had forgotten to specify the content type header of the request (I tested the API using curl from the command line). Once I set it to -H "Content-Type: application/json", plumber was able to parse the body correctly and I could see the payload in req$body as well as req$args.
If you decide to use req$postBody, like the OP did, note that as of plumber v1.0.0 it is recommended to use req$bodyRaw rather than req$postBody. For more information, see postBody in the plumber request object as well as plumber PR#665.
Related
I’m trying to make a http.request to have a graph created, and then save the resulting .png graph image that is created. The problem is I want to do this with Lua, yet I’m struggling on two parts. (If you take url, you’ll see that this should work fine in a standard browser)
Handling a 301 error, have looked through SO, I could see a few references to this and the need to use luasec, which I believe I have.
301 moved permanently with socket.http
Here is the script, with the URL I’m trying to call via HTTP, and then (eventually want to ) save the resulting graph image (.png file) that’s created
local http = require "socket.http"
--local https = require("ssl.https")
local ltn12 = require "ltn12"
r = {} -- init empty table
local result, code, headers, status = http.request{
url="http://www.chartgo.com/create.do?charttype=line&width=650&height=650&chrtbkgndcolor=white&gridlines=1&labelorientation=horizontal&title=Fdsfsdfdsfsdfsdfsdf&subtitle=Qrqwrwqrqwrqwr&xtitle=Cbnmcbnm&ytitle=Ghjghj&source=Hgjghj&fonttypetitle=bold&fonttypelabel=normal&gradient=1&max_yaxis=&min_yaxis=&threshold=&labels=1&xaxis1=Jan%0D%0AFeb%0D%0AMar%0D%0AApr%0D%0AMay%0D%0AJun%0D%0AJul%0D%0AAug%0D%0ASep%0D%0AOct%0D%0ANov%0D%0ADec&yaxis1=20%0D%0A30%0D%0A80%0D%0A90%0D%0A50%0D%0A30%0D%0A60%0D%0A50%0D%0A40%0D%0A50%0D%0A10%0D%0A20&group1=Group+1&viewsource=mainView&language=en§ionSetting=§ionSpecific=§ionData=",
sink = ltn12.sink.table( r )
}
print("code=".. tostring(code))
print("status=".. tostring(status))
print("headers=".. tostring(headers))
print("result=".. tostring(result))
print("sink= ".. table.concat( r, "" ) )
print(result, code, headers, status )
for i,v in pairs(headers) do
print("\t",i, v)
end
Which returns the 301 Moved Permanently error, plus via a viewer it also provides me with a link to another URL (this time a https on)
So to try and get to the https site first off, I tried adding in the ssl.http element with the following, but that that does not return anything at all, all nil .
local https = require("ssl.https")
local ltn12 = require "ltn12"
r = {} -- init empty table
local result, code, headers, status = https.request{
url="https://www.chartgo.com/create.do?charttype=line&width=650&height=650&chrtbkgndcolor=white&gridlines=1&labelorientation=horizontal&title=Fdsfsdfdsfsdfsdfsdf&subtitle=Qrqwrwqrqwrqwr&xtitle=Cbnmcbnm&ytitle=Ghjghj&source=Hgjghj&fonttypetitle=bold&fonttypelabel=normal&gradient=1&max_yaxis=&min_yaxis=&threshold=&labels=1&xaxis1=Jan%0D%0AFeb%0D%0AMar%0D%0AApr%0D%0AMay%0D%0AJun%0D%0AJul%0D%0AAug%0D%0ASep%0D%0AOct%0D%0ANov%0D%0ADec&yaxis1=20%0D%0A30%0D%0A80%0D%0A90%0D%0A50%0D%0A30%0D%0A60%0D%0A50%0D%0A40%0D%0A50%0D%0A10%0D%0A20&group1=Group+1&viewsource=mainView&language=en§ionSetting=§ionSpecific=§ionData=",
sink = ltn12.sink.table( r )
}
print("code=".. tostring(code))
print("status=".. tostring(status))
print("headers=".. tostring(headers))
print("result=".. tostring(result))
print("sink= ".. table.concat( r, "" ) )
print(result, code, headers, status )
And then …
assuming I can eventually make the http.request work, the web page returns a png. image of the resulting graph - I’d love to be able to extract/copy that for further use within this piece of code..
As always any help/advice would be appreciated..
Im attempting to setup some R code to create a new work item task in Azure Devops. Im okay with a mostly empty work item to start with if thats okay to do (my example code is only trying to create a work item with a title).
I receive a 203 response but the work item doesn't appear in Devops.
Ive been following this documentation from Microsoft, I suspect that I might be formatting the body incorrectly.
https://learn.microsoft.com/en-us/rest/api/azure/devops/wit/work%20items/create?view=azure-devops-rest-5.1
Ive tried updating different fields and formatting the body differently with no success. I have attempted to create either a bug or feature work item but both return the same 203 response.
To validate that my token is working I can GET work item data by ID but the POST continues to return a 203.
require(httr)
require(jsonlite)
url <- 'https://dev.azure.com/{organization}/{project}/_apis/wit/workitems/$bug?api-version=5.1'
headers = c(
'Authorization' = sprintf('basic %s',token),
'Content-Type' = 'application/json-patch+json',
'Host' = 'dev.azure.com'
)
data <- toJSON(list('body'= list("op"= "add",
"path"= "/fields/System.AreaPath",
"value"= "Sample task")), auto_unbox = TRUE, pretty = TRUE)
res <- httr::POST(url,
httr::add_headers(.headers=headers),
httr::verbose(),
body = data)
Im expecting a 200 response (similar to the example in the link above) and a work item task in Azure DevOps Services when I navigate to the website.
Im not the best with R so please be detailed. Thank you in advanced!
The POST continues to return a 203.
The HTTP response code 203 means Non-Authoritative Information, it should caused by your token format is converted incorrectly.
If you wish to provide the personal access token through an HTTP
header, you must first convert it to a Base64 string.
Refer to this doc described, if you want to use VSTS rest api, you must convert your token to a Base64 string. But in your script, you did not have this script to achieve this convert.
So, please try with the follow script to convert the token to make the key conformant with the requirements(load the base64enc package first):
require(base64enc)
key <- token
keys <- charToRaw(paste0(key,":token"))
auth <- paste0("Basic ",base64encode(keys))
Hope this help you get 200 response code
I know this question is fairly old, but I cannot seem to find a good solution posted yet. So, I will add my solution in case others find themselves in this situation. Note, this did take some reading through other SO posts and trial-and-error.
Mengdi is correct that you do need to convert your token to a Base64 string.
Additionally, Daniel from this SO question pointed out that:
In my experience with doing this via other similar mechanisms, you have to include a leading colon on the PAT, before base64 encoding.
Mengdi came up big in another SO solution
Please try with adding [{ }] outside your request body.
From there, I just made slight modifications to your headers and data objects. Removed 'body' from your json, and made use of paste to add square brackets as well. I found that the Rcurl package made base64 encoding a breeze. Then I was able to successfully create a blank ticket (just titled) using the API! Hope this helps someone!
library(httr)
library(jsonlite)
library(RCurl)
#user and PAT for api
userid <- ''
token= 'whateveryourtokenis'
url <- 'https://dev.azure.com/{organization}/{project}/_apis/wit/workitems/$bug?api-version=5.1'
#create a combined user/pat
#user id can actually be a blank string
#RCurl's base64 seemed to work well
c_id <- RCurl::base64(txt = paste0(userid,
":",
devops_pat
),
mode = "character"
)
#headers for api call
headers <- c(
"Authorization" = paste("Basic",
c_id,
sep = " "
),
'Content-Type' = 'application/json-patch+json',
'Host' = 'dev.azure.com'
)
#body
data <- paste0("[",
toJSON(list( "op"= "add",
"path"= "/fields/System.Title",
"value"= "API test - please ignore"),
auto_unbox = TRUE,
pretty = TRUE
),
"]"
)
#make the call
res <- httr::POST(url,
httr::add_headers(.headers=headers),
httr::verbose(),
body = data
)
#check status
status <- res$status_code
#check content of response
check <- content(res)
I have a list of PDF URLs, and I want to download these PDFs. However, not all of the URLs are still existing, this is why I check them before by means of the RCurl function url.exists(). With some URLs, however, this function is running forever without delivering a result. I can't even stop it with a withTimeout() function.
I wrapped url.exists() into withTimeout(), but the timeout does not work:
library(RCurl)
library(R.utils)
url <- "http://www.shangri-la.com/uploadedFiles/corporate/about_us/csr_2011/Shangri-La%20Asia%202010%20Sustainability%20Report.pdf"
withTimeout(url.exists(url), timeout = 15, onTimeout = "warning")
The function runs forever, timeout is ignored.
Thus my questions:
Is there any possible check which sorts out this URL before it gets to url.exists()?
Or is there a possibility to prevent url.exists() from running forever?
Other checks I tried (but which do not sort out this URL) are:
try(length(getBinaryURL(url))>0) == T
http_status(GET(url))
!class(try(GET(url]))) == "try-error"
library(httr)
urls <- c(
'https://www.deakin.edu.au/current-students/unitguides/UnitGuide.php?year=2015&semester=TRI-1&unit=SLE010',
'https://www.deakin.edu.au/current-students/unitguides/UnitGuide.php?year=2015&semester=TRI-2&unit=HMM202',
'https://www.deakin.edu.au/current-students/unitguides/UnitGuide.php?year=2015&semester=TRI-2&unit=SLE339'
)
sapply(urls, url_success, config(followlocation = 0L), USE.NAMES = FALSE)
This functions is analogous to file.exists and determines whether a request for a specific URL responds without error. We make the request but ask the server not to return the body. We just process the header.
I tried to get data from a netatmo station I have access to via API. The following code I used in R.
myapp <- oauth_app("my_netatmo",key="my_netatmo_client_id",secret="my_netatmo_client_secret")
ep <- oauth_endpoint(authorize = "http://api.netatmo.net/oauth2/authorize",access = "http://api.netatmo.net/oauth2/token")
sig_tok <- oauth2.0_token(ep,myapp, scope="read_station")
#after that I get redirected to my browser to log in and after that sig_tok contains an access token
sig <- config(token = sig_tok)
html_get<-GET("http://api.netatmo.net/api/devicelist",sig)
html_get contains this:
html_get
Response [http://api.netatmo.net/api/devicelist]
Status: 400
Content-type: application/json; charset=utf-8
{"error":{"code":1,"message":"Access token is missing"}}
What am I doing wrong since "sig" seems to contain a token:
sig$token$credentials$access_token
[1] "5**********************f|3**********************a"
There are two tokens or am I wrong (because of "|" in between)?
The '|' inside the access_token is part of it, it is only one access token.
From the documentation: http://dev.netatmo.com/doc/methods/devicelist,
the parameter name is: "access_token". I don't know the R language, but it seems you are sending "token" as a parameter, and not "access_token". It may explain the issue.
I am trying to get data from the mobile analytics service Localytics via their API (https://api.localytics.com/docs#query). In particular I would like to translate the following cURL command in R:
curl --get 'https://api.localytics.com/v1/query' \
--user 'API_KEY:API_SECRET' \
--data 'app_id=APP_ID' \
--data 'metrics=users' \
--data 'dimensions=day' \
--data-urlencode 'conditions={"day":["between","2013-04-01","2013-04-07"]}'
My R code looks like this at the moment. APIKey and API secret are of course replaced by the actual keys. However, I receive an error stating that at least a dimension or a metric has to be specified.
object <- getURL('https://api.localytics.com/v1/query', userpwd = "API_Key:API_Secret", httpheader=list(app_id = "app_id=03343434353534",
metrics = "metrics=users",
dimensions = "dimensions=day",
conditions = toJSON('conditions={"day":["between","2014-07-01","2014-07-10"]}')), ssl.verifypeer = FALSE)
What changes would be necessary to get it to work.
Thanks in advance for helping me out,
Peter
This is particular easy with the dev version of httr:
library(httr)
r <- POST('https://api.localytics.com/v1/query',
body = list(
app_id = "APP_ID",
metrics = "users",
dimensions = "day",
conditions = list(
day = c("between", "2014-07-01", "2004-07-10")
)
),
encode = "json",
authenticate("API_key", "API_secret")
)
stop_for_status(r)
content(r)
(I converted the request to a POST and used json encoding for everything, as describe in the API docs).
If you want to see exactly what's being sent to the server, use the verbose() config.
It looks like getURL passes the parameters you quested as HTTP headers and not as querystring data as your curl call does. You should use getForm instead. Also, I wasn't sure from which library your toJSON function came form, but that's at least not the right syntax for the one from rsjon.
Anyway, here's a call from R which should produce the same HTTP call are your curl command
library(rjson)
library(RCurl)
object <- getForm('https://api.localytics.com/v1/query',
app_id = "APP_ID",
metrics = "users",
dimensions = "day",
conditions = toJSON(list(day=c("between","2014-07-01","2004-07-10"))),
.opts=curlOptions(
userpwd = "API_Key:API_Secret", httpauth = 1L)
)
I found that using the site http://requestb.in/ is very helpful in debugging these problems (and that's exactly what I used to create this solution). You can send requests to their site and they record the exact HTTP message that was sent so you can compare different methods.
The httpauth part was from this SO question which seemed to be required to trigger authentication for the test site; you may not need it for the "real" site.