How to fix reqwest json decode errors? - asynchronous

When I run the following code:
use exitfailure::ExitFailure;
use reqwest::Url;
use serde_derive::{Deserialize, Serialize};
use std::env;
#[derive(Serialize, Deserialize, Debug)]
struct CompanyInfo {
country: String,
currency: String,
exchange: String,
ipo: String,
marketCapitalization: u128,
name: String,
phone: String,
shareOutstanding: f64,
ticker: String,
weburl: String,
logo: String,
finnhubIndustry: String,
}
impl CompanyInfo {
async fn get(symbol: &String, api_key: &String) -> Result<Self, ExitFailure> {
let url = format!(
"https://finnhub.io/api/v1/stock/profile2?symbol={}&token={}",
symbol, api_key
);
let url = Url::parse(&*url)?;
let res = reqwest::get(url).await?.json::<CompanyInfo>().await?;
Ok(res)
}
}
#[tokio::main]
async fn main() -> Result<(), ExitFailure> {
let api_key = "MY API KEY".to_string();
let args: Vec<String> = env::args().collect();
let mut symbol: String = "AAPL".to_string();
if args.len() < 2 {
println!("Since you didn't specify a company symbol, it has defaulted to AAPL.");
} else {
symbol = args[1].clone();
}
let res = CompanyInfo::get(&symbol, &api_key).await;
println!("{:?}", res);
Ok(())
}
I get an error: Err(error decoding response body: expected ',' or '}' at line 1 column 235). For another API, this code with a similar structure worked. How do you solve this issue with reqwest?

Typically, the error decoding response body means that you tried to deserialize an HTTP response that is in a given format, but the response body wasn't valid for that format. In your case, you are trying to deserialize JSON, so the error means that the thing you are trying to deserialize probably isn't valid JSON, or perhaps it is valid JSON but your expected JSON structure doesn't match the structure returned from the server. The server might have goofed in creating it's JSON, or perhaps it is returning a response body that isn't actually JSON for a specific reason (for example, some APIs will not return JSON if they are returning a 500 response).
In order to debug and fix this, you need to know exactly what the response body looks like that you are attempting to parse. One way to do this is to split the parsing of the code into two parts: one that gets the text, and another that tries to parse. For example, for debugging purposes you can print out the response that you have received by doing something like the following:
// Split up the JSON decoding into two steps.
// 1.) Get the text of the body.
let response_body = reqwest::get(url).await?.text().await?;
println!("Response Body: {}", response_body);
// 2.) Parse the results as JSON.
let res: CompanyInfo = serde_json::from_str(&response_body)?;
This code should will probably fail just as before, but now you'll have the response body that failed printed out. At that point, you'll have to analyze the response body, at which point it will hopefully become obvious why it doesn't work.

Related

Redis-OM library error during next js redis implementations

I need some help to figure it our why I am getting error when using macthes API, this is my code:
export async function searchCars(query) {
await connect();
const repository = client.fetchRepository(schema);
const cars = await repository
.search()
.where("make")
.eq(query)
.or("model")
.eq(query)
.or("description")
.matches(query)
.return.all();
return cars;
}
And I am getting this error when making API request
May I ask for help?
Many thanks ~~
I did remove the macthes() API, the whole query works, and I can get results back.
If i add it back, the APi will return redis error ...
Expectation: I would like to do text search which matches the paragraph text, then return the record(s).
Sorry, I think I have figured it our, the issue is we need to ensure the scheme field type must be text rather than string, something like below:
description: { type: "text", textSearch: true },

How do I make this example of an HTTP request work?

I am trying to make the example in the documentation work, but I can't.
use http::{Request, Response};
let mut request = Request::builder();
request.uri("https://www.rust-lang.org/")
.header("User-Agent", "my-awesome-agent/1.0");
if needs_awesome_header() {
request.header("Awesome", "yes");
}
let response = send(request.body(()).unwrap());
fn send(req: Request<()>) -> Response<()> {
// ...
}
The question is, how can I print the response in a string to save it? It seems it is not in the response.

How to get the cookie from a GET response?

I am writing a function that makes a GET request to a website and returns the response cookie:
extern crate futures;
extern crate hyper;
extern crate tokio_core;
use tokio_core::reactor::Core;
use hyper::Client;
use std::error::Error;
use hyper::header::Cookie;
use futures::future::Future;
fn get_new_cookie() -> Result<String, Box<Error>> {
println!("Getting cookie...");
let core = Core::new()?;
let client = Client::new(&core.handle());
println!("Created client");
let uri = "http://www.cnn.com".parse().expect("Cannot parse url");
println!("Parsed url");
let response = client.get(uri).wait().expect("Cannot get url.");
println!("Got response");
let cookie = response
.headers()
.get::<Cookie>()
.expect("Cannot get cookie");
println!("Cookie: {}", cookie);
Ok(cookie)
}
fn main() {
println!("{:?}", get_new_cookie());
}
This doesn't work; it is stuck on the client.get(...) string. The output I'm getting is:
Getting cookie...
Created client
Parsed url
and after that nothing happens.
What am I doing wrong and how I can change it so it'd work?
As Stefan points out, by calling wait, you are putting the thread to sleep until the future has completed. However, that thread needs to run the event loop, so you've just caused a deadlock. Using Core::run is more correct.
As Francis Gagné points out, the "Cookie" header is used to send a cookie to the server. SetCookie is used to send a cookie to the client. It also returns a vector of all the cookies together:
fn get_new_cookie() -> Result<String, Box<Error>> {
println!("Getting cookie...");
let mut core = Core::new()?;
let client = Client::new(&core.handle());
println!("Created client");
let uri = "http://www.cnn.com".parse().expect("Cannot parse url");
println!("Parsed url");
let response = core.run(client.get(uri)).expect("Cannot get url.");
println!("Got response");
let cookie = response
.headers()
.get::<SetCookie>()
.expect("Cannot get cookie");
println!("Cookie: {:?}", cookie);
Ok(cookie.join(","))
}
However, if you only want a synchronous API, use Reqwest instead. It is built on top of Hyper:
extern crate reqwest;
use std::error::Error;
use reqwest::header::SetCookie;
fn get_new_cookie() -> Result<String, Box<Error>> {
let response = reqwest::get("http://www.cnn.com")?;
let cookies = match response.headers().get::<SetCookie>() {
Some(cookies) => cookies.join(","),
None => String::new(),
};
Ok(cookies)
}
fn main() {
println!("{:?}", get_new_cookie());
}
See the documentation for the wait method:
Note: This method is not appropriate to call on event loops or similar
I/O situations because it will prevent the event loop from making
progress (this blocks the thread). This method should only be called
when it's guaranteed that the blocking work associated with this
future will be completed by another thread.
Future::wait is already deprecated in the tokio-reform branch.
I'd recommend to design the full application to deal with async concepts (i.e. get_new_cookie should take a Handle and return a Future, not allocating its own event loop).
You could run the request with Core::run like this:
let response = core.run(client.get(uri)).expect("Cannot get url.");
reqwest 0.11 (and perhaps earlier) update
In the get_new_cookie function, I believe the code snippet to retrieve the cookies from a reqwest::Response goes something like:
// returns Option<&HeaderValue>
response.headers().get(http::header::SET_COOKIE)

IRC server doesn't respond to Rust IRC Client identify requests

I'm working on an IRC bot using TcpStream from the standard library.
I'm able to read all the lines that come in, but the IRC server doesn't seem to respond to my identify requests. I thought I was sending the request too soon so I tried sleeping before sending the IDENT but that doesn't work. I write using both BufReader, BufWriter and calling read and write directly on the stream to no avail.
use std::net::TcpStream;
use std::io::{BufReader, BufWriter, BufRead, Write, Read};
use std::{thread, time};
struct Rusty {
name: String,
stream: TcpStream,
reader: BufReader<TcpStream>,
writer: BufWriter<TcpStream>,
}
impl Rusty {
fn new(name: &str, address: &str) -> Rusty {
let stream = TcpStream::connect(address).expect("Couldn't connect to server");
let reader = BufReader::new(stream.try_clone().unwrap());
let writer = BufWriter::new(stream.try_clone().unwrap());
Rusty {
name: String::from(name),
reader: reader,
writer: writer,
stream: stream,
}
}
fn write_line(&mut self, string: String) {
let line = format!("{}\r\n", string);
&self.writer.write(line.as_bytes());
}
fn identify(&mut self) {
let nick = &self.name.clone();
self.write_line(format!("USER {} {} {} : {}", nick, nick, nick, nick));
self.write_line(format!("NICK {}", nick));
}
fn read_lines(&mut self) {
let mut line = String::new();
loop {
self.reader.read_line(&mut line);
println!("{}", line);
}
}
}
fn main() {
let mut bot = Rusty::new("rustyrusty", "irc.rizon.net:6667");
thread::sleep_ms(5000);
bot.identify();
bot.read_lines();
}
It's very important to read the documentation for the components we use when programming. For example, the docs for BufWriter states (emphasis mine):
Wraps a writer and buffers its output.
It can be excessively inefficient to work directly with something that
implements Write. For example, every call to write on TcpStream
results in a system call. A BufWriter keeps an in-memory buffer of
data and writes it to an underlying writer in large, infrequent
batches.
The buffer will be written out when the writer is dropped.
Said another way, the entire purpose of a buffered reader or writer is that read or write requests don't have a one-to-one mapping to the underlying stream.
That means when you call write, you are only writing to the buffer. You also need to call flush if you need to ensure that the bytes are written to the underlying stream.
Additionally, you should:
Handle the errors that can arise from read, write, and flush.
Re-familiarize yourself with what each function does. For example, read and write don't guarantee that they read or write as much data as you ask them to. They may perform a partial read or write, and it's up to you to handle that. That's why there are helper methods like read_to_end or write_all.
Clear your String that you are reading into. Otherwise the output just repeats every time the loop cycles.
Use write! instead of building up a string that is immediately thrown away.
fn write_line(&mut self, string: &str) {
write!(self.writer, "{}\r\n", string).unwrap();
self.writer.flush().unwrap();
}
With these changes, I was able to get a PING message from the server.

Get HTTP Status using swift

I am sorry, I haven't found an answer for my question(( Please, don't be very harsh, I am not a professional programmer, but I keep learning and hope once I will be able to answer someone's question))
I am trying to get HTTP Status of the link (I am sort of generating links depending on one database entries code, like ABCDEF, I keep them in an array and then generate a link to second database, like www.blablabla.ABCDEF.net), so I can see whether the page exists in database or not.
I have written this code, but something is wrong. So maybe it's a question like: "What's wrong with my code?" But on the stack they also say, you have to show your attempts of problem solving...
I wish I could keep it all swift, without any additional modules or something, I think NSHTTPURLResponse must be enough, but I am using it somehow wrong.
Looking forward to help and replies))
var err: NSError!
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse?, data: NSData!, error: err) -> Void in
if (err != nil) {
let httpStatus: NSHTTPURLResponse = response as NSHHTPURLResponse
for myLink in allLinks {
println("HERE IS THE CURRENT STATUS CODE" + httpStatus.statusCode + "OF A LINK:" + myLink)
if httpStatus.statusCode == 200 {println("SUCCESS!!!!!")}
}
}
}
The fundamental issue here is that you appear to be looking at the statusCode only if the err is not nil. But if you have error, you probably don't have status code. The error parameter to the closure indicates fundamental network issue that probably prevented you from getting to the page in question. The statusCode is generally only meaningful if the error was nil (i.e. you succeeded in connecting to the server), in which case the statusCode is how the server informs you of its ability to service the HTTP request.
A couple of minor things:
You don't need the var err: NSError! line (because the error object is passed as parameter to the closure). This variable you've declared is not used here.
I don't see how error: err as the third parameter to the closure could have worked. The syntax is "variableName: variableType", but there is no type of err.
Likewise, your code is referring to a non-existent class, NSHHTPURLResponse. In Swift 3 and later, it's HTTPURLResponse.
It's probably prudent to do if let for the retrieval of the HTTPURLResponse in order to get the statusCode.
I'm unclear as to your intent in iterating through allLinks, because this connection is just for a given request, not a bunch of links. Just look at the statusCode in light of the particular request. If you need to test multiple URLs, then you do a separate request for each.
We should consider any codes between 200 and 299 as success, not just 200. I'd suggest using the range 200 ..< 300.
Thus:
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, let httpResponse = response as? HTTPURLResponse, error == nil else {
print("No valid response")
return
}
guard 200 ..< 300 ~= httpResponse.statusCode else {
print("Status code was \(httpResponse.statusCode), but expected 2xx")
return
}
// everything OK, process `data` here
}
task.resume()
I also made a few other changes (updated for Swift 3 and later; use URLSession rather than URLConnection; I think error is fine choice for the variable name; I prefer the trailing closure syntax; I tend to use inferred types for closure parameters to make the declaration a little more concise, etc.), but all of that is immaterial to the question at hand: Hopefully this illustrates how one checks the status code.
For Swift 2 rendition, see previous revision of this answer.
[Swift 5.2]
Hi there, you can try this one:
let task = URLSession.shared.dataTask(with: yourRequest) {
(data, response, error) in
guard let response = response else {
print("Cannot found the response")
return
}
let myResponse = response as! HTTPURLResponse
print("Status Code:", myResponse.statusCode)
}
task.resume()
Here is the output
Status Code: 200
(This is your StatusCode)

Resources