Hi, I am trying to curl this json message to a slack channel, so I tried the following code:
let body = format!("payload={}", message);
let client = reqwest::blocking::Client::new();
let response = client.post(slackchan)
.json(&body)
.send()
.unwrap();
Where slackchan is the link to the slack channel, and body is the string to be curled. However, when I run the code, it doesn't get sent to the slack channel. I know the link works, because in the ruby version:
body = {"text" => message}.to_json
`curl -XPOST #{slackchan} -d 'payload=#{body}'`
puts body
The body gets curled. Additionally, I know that body is not an empty string since I tried printing it before the reqwest post. I figure I am not using reqwest properly, or perhaps there's a different crate that would suit my needs better(I would prefer using reqwest since I need the string body to be formatted as a json file)?
Any help would be greatly appreciated. Thanks for your time.
Sends the specified data in a POST request to the HTTP server, in the same way that a browser does when a user has filled in an HTML form and presses the submit button. This will cause curl to pass the data to the server using the content-type application/x-www-form-urlencoded.
I haven't used reqwest, but my guess is you should send a multipart request (side note: request, not response). That appears to be the equivalent of -d, and it lets you submit a form field called payload.
let form = reqwest::multipart::Form::new()
.text("payload", serde_json::to_string(message)?);
let request = client.post(slackchan)
.multipart(form)
.send()
.unwrap();
Like the Ruby code, the JSON encoding happens first before constructing the request.
Also, you'll need the JSON to look like {"text": <...>}. Make sure it's wrapped in that envelope.
I am having trouble using multipart, because when I tried the code you suggested, the error: "could not find multipart in reqwest". Under dependencies I have:
reqwest = "0.11.4"
And I tried both:
use reqwest::blocking::multipart;
use reqwest:::multipart;
But, it says "no multipart in root". Additionally,
.multipart(form)
Gives the error "method not found in reqwest::blocking::RequestBuilder".
The docs state that this function needs to be enabled by the feature flag multipart. You can enable this dependency feature by adding something like the following to your Cargo.toml
[depencencies]
reqwest = { version = "0.11", features = ["multipart"] }
Thanks, that worked for me. However, now I'm getting the following error: "
.text("payload", serde_json::to_string(&message));
| ^^^^ the trait `From<Result<std::string::String, serde_json::Error>>` is not implemented for `Cow<'static, str>`
I think it has to do with the fact that I am formatting message as a JSON. How would I go about dealing with this?
Unpack the Result with ? or .expect or the like to handle failure.
To help understand that error, .text is written in a really flexible, albeit complicated, way. It accepts anything that can be converted into a Cow<'static, str>. A Cow<str> is a reference type that can be constructed both from borrowed &str references and from owned Strings. By accepting Cows -- or anything convertible into a Cow -- it is effectively letting you pass a &str or a String, whichever you happen to have.
It makes for a complicated signature and error messages but don't hold it against them. They're trying to be helpful, I promise.
What I'm getting at is that all the Cow business should be ignored. The important part is that serde_json is wrapping it in a Result<_> because the conversion could fail. Unpack it and you'll be good to go. The String inside it is convertible to Cow<&str> so that should be all you need to do.
Sorry I don't think I was very clear. The error is from the following line:
let form = reqwest::blocking::multipart::Form::new()
.text("payload", serde_json::to_string(&message));
And the problem is:
error[E0277]: the trait bound `Cow<'static, str>: From<Result<std::string::String, serde_json::Error>>` is not satisfied
--> src/main.rs:246:10
|
246 | .text("payload", serde_json::to_string(&body));
| ^^^^ the trait `From<Result<std::string::String, serde_json::Error>>` is not implemented for `Cow<'static, str>`
|
= help: the following implementations were found:
<Cow<'a, CStr> as From<&'a CStr>>
<Cow<'a, CStr> as From<&'a CString>>
<Cow<'a, CStr> as From<CString>>
<Cow<'a, OsStr> as From<&'a OsStr>>
and 15 others
= note: required because of the requirements on the impl of `Into<Cow<'static, str>>` for `Result<std::string::String, serde_json::Error>`
Which I believe means that it expects .text() to contain a static and str instead of the current values which is a String, and a serde_json value. I tried .expect(), but the method isn't found for reqwest:blocking:multipart:Form.
You need to put the .expect("...") on the expression serde_json::to_string(&message), not on Form::new. This is because serde_json::to_string returns a Result value, which will be an Err if serialization failed. After that you should be good to go since Cow<'static, str> does implement From<String>.
Thanks for the clarification, the following code compiles and runs now:
let body = format!("payload={}", message);
let client = reqwest::blocking::Client::new();
let form = reqwest::blocking::multipart::Form::new()
.text("payload", serde_json::to_string(&body).expect("could not be formatted"));
let request = client.post(slackchan)
.multipart(form)
.send()
.unwrap();
However, I still don't see the message curled to the slack channel. I'm unsure as to why I don't see the output. Just to be clear, this is using slack's webhook app.
I think you want serde_json::to_string(&message) not serde_json::to_string(&body)? Otherwise the value of the payload field will start with payload= which seems redundant, plus the Display formatting of message might not be the JSON you want (and anyway it will be wrapped in an extra level of quotes/escaping by serde_json::to_string).
Is this the API you're using? That doesn't seem to use multipart requests, you would just do
let response = client.post(slackchan)
.json(&message)
.send()
.unwrap();
That's assuming the Serialize impl for whatever message is produces an object like { "text": "blah blah blah", "other_field": "foo" }—you could use a custom wrapper type or construct message as a serde_json::Value to achieve that. I don't see anything about a payload field.
Payload isn't supposed to be a field. It's just part of the string message that I want to output to the slack channel. The code you just posted is what I initially tried(if you look at my first post), but that did not send anything to the slack channel.
I think the problem with your initial code is that you were just sending a JSON string as the body, not an object with a "text" field. If message is a String or &str, this should do the trick (untested):
let body = format!("payload={}", message);
let object = serde_json::json!({ "text": body });
let client = reqwest::blocking::Client::new();
let response = client.post(slackchan)
.json(&object)
.send()
.unwrap();
Sorry for the previous bad suggestion(s), I was a little slow getting up to speed.