I'm attempting to open and write to a database in a Rust library I will call from python, with the help of pyo3. If an error occurs, I would like to raise an exception that can be caught in the calling Python process, but I'm having difficulties terminating execution and raising an error.
use rusqlite::{Connection};
use rusqlite::NO_PARAMS;
use pyo3::{Python, wrap_pyfunction};
use pyo3::exceptions::PyIOError;
#[pyfunction]
fn do_something(_py: Python) -> PyResult<u32> {
match Connection::open("database.sql") {
Ok(t) => conn = t,
Err(e) => {
let gil = Python::acquire_gil();
let py = gil.python();
let error_message = format!("Unable to open database! {}", e.to_string());
PyIOError::new_err(error_message).restore(py)
}
};
match conn.execute(
"create table if not exists cats (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
)",
NO_PARAMS,
) {
Ok(_t) => (),
Err(e) => {
let gil = Python::acquire_gil();
let py = gil.python();
let error_message = format!("Unable to open database! {}", e.to_string());
PyIOError::new_err(error_message).restore(py)
}
}
Ok(0)
It's my understanding that by calling the restore function on the PyIOError object, an error would be raised, however, I must be misunderstanding because the compiler seems to consider it a possibility that conn is not initialised:
error[E0381]: borrow of possibly-uninitialized variable: `conn`
18 | match conn.execute(
| ^^^^ use of possibly-uninitialized `conn`
What would be an appropriate approach here?
First of all, your Ok(t) = conn = t fails, as you haven't defined conn. So prior to the match add let conn;. Alternatively, you can also just assign the result of the match to conn.
Second, you still need to return an Err.
#[pyfunction]
fn do_something(_py: Python) -> PyResult<u32> {
let conn = match Connection::open("database.sql") {
Ok(t) => t,
Err(e) => {
let gil = Python::acquire_gil();
let py = gil.python();
let error_message = format!("Unable to open database! {}", e.to_string());
PyIOError::new_err(error_message).restore(py);
return Err(PyErr::fetch(py));
}
};
match conn.execute(
"create table if not exists cats (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
)",
NO_PARAMS,
) {
Ok(_t) => (),
Err(e) => {
let gil = Python::acquire_gil();
let py = gil.python();
let error_message = format!("Unable to open database! {}", e.to_string());
PyIOError::new_err(error_message).restore(py);
return Err(PyErr::fetch(py));
}
}
Ok(0)
}
It's been some time since I used PyO3. But unless I remember incorrectly, then you can just remove restore() also just return the Err and let PyO3 handle the rest.
#[pyfunction]
fn do_something(_py: Python) -> PyResult<u32> {
let conn = match Connection::open("database.sql") {
Ok(t) => t,
Err(e) => {
let error_message = format!("Unable to open database! {}", e.to_string());
return Err(PyIOError::new_err(error_message));
}
};
match conn.execute(
"create table if not exists cats (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
)",
NO_PARAMS,
) {
Ok(_t) => (),
Err(e) => {
let error_message = format!("Unable to open database! {}", e.to_string());
return Err(PyIOError::new_err(error_message));
}
}
Ok(0)
}
Related
I'm new to Rust and am trying to wrap my head around error handling.
I'm trying to return error if parsing the date goes wrong, here is the function:
pub fn create_posts(contents: &Vec<String>) -> Result<Vec<Post>, CreatePostError> {
const TITLE_SEP: &str = "Title: ";
const DESC_SEP: &str = "Description: ";
const DATE_SEP: &str = "Date: ";
const TAGS_SEP: &str = "Tags: ";
let mut posts: Vec<Post> = Vec::new();
for entry in contents {
let lines = entry.lines().collect::<Vec<_>>();
let metadata = lines[0].contains(&TITLE_SEP)
&& lines[1].contains(&DESC_SEP)
&& lines[2].contains(&DATE_SEP)
&& lines[3].contains(&TAGS_SEP);
if metadata {
let date = &lines[2][DATE_SEP.len()..];
let parsed_date = match NaiveDate::parse_from_str(date, "%Y-%m-%d") {
Ok(parsed_date) => parsed_date,
Err(e) => eprintln!("Error: {:?}", CreatePostError::ParseError { inner_err: e }),
};
let tags: Vec<String> = lines[3][TAGS_SEP.len()..]
.split(", ")
.map(|s| s.to_string())
.collect();
let mut article_content = String::new();
for line in &lines[4..] {
article_content.push_str(line);
article_content.push_str("\n")
}
let post = Post {
title: lines[0][TITLE_SEP.len()..].to_string(),
description: lines[1][DESC_SEP.len()..].to_string(),
date: parsed_date,
tags,
content: article_content,
};
posts.push(post);
} else {
return Err(CreatePostError::MetadataError);
}
}
return Ok(posts);
}
You can see the full code here since i wrote custom errors: link
The problem I'm having is with this part:
let date = &lines[2][DATE_SEP.len()..];
let parsed_date = match NaiveDate::parse_from_str(date, "%Y-%m-%d") {
Ok(parsed_date) => parsed_date,
Err(e) => eprintln!("Error: {:?}", CreatePostError::ParseError { inner_err: e }),
};
I'm getting match arms have incompatible types. Expected struct NaiveDate, found ()
Here is my enum and impl for the error:
#[derive(Debug)]
pub enum CreatePostError {
ReadFileError { path: PathBuf },
MetadataError,
ParseError { inner_err: ParseError },
}
impl fmt::Display for CreatePostError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::ReadFileError { path } => write!(f, "Error reading file {path:?}"),
Self::MetadataError => write!(f, "Some metadata is missing"),
Self::ParseError { inner_err } => {
write!(f, "Error parsing date: {inner_err}")
}
}
}
}
impl From<chrono::format::ParseError> for CreatePostError {
fn from(e: chrono::format::ParseError) -> Self {
CreatePostError::ParseError { inner_err: e }
}
}
You probably want to have a result here, here is a way to do it:
let parsed_date = match NaiveDate::parse_from_str(date, "%Y-%m-%d") {
Ok(parsed_date) => Ok(parsed_date),
Err(e) => {eprintln!("Error: {:?}", &e);
Err(CreatePostError::ParseError { inner_err: e }}),
}?;
The ? is saying: if the thing before is an error, return it, and if it is Ok, unwrap it.
This pattern is so common that rust's result gives a lot of utilities to make this kind of things easier. Here, the map_err function would make it more straightforward: map_err
see:
let parsed_date = NaiveDate::parse_from_str(date, "%Y-%m-%d")
.map_err(|e| {
eprintln!("Error: {:?}", &e);
CreatePostError::ParseError { inner_err: e }})?;
But it is only a matter of preference and it might be a lot to digest if you are just beginning, so you can choose the way that you like the most.
I would like to load a Json file to a Vec.
When I do everything with unwrap() it works fine. Now I want to Catch some Errors like "File not Found" and Handle them, but I'm not able to figure out how to do it probably.
Maybe you could help me? Thanks in advance.
fn read_from_file(filename: &str) -> Result<Vec<Card>, io::Error> {
let f = File::open(filename);
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut s = String::new();
f.read_to_string(&mut s)?;
let data = serde_json::from_str(&s)?;
Ok(data)
}
fn main() {
let mut cards = read_from_file("Test.json");
let mut cards = match cards {
Ok(Vec) => Vec,
Err(_) => {
println!(
"File was not found!\n
Do you want to create a new one? (y/N)"
);
let mut answer = String::new();
io::stdin()
.read_line(&mut answer)
.expect("Failed to read line");
answer.pop();
if answer == "y" {
let mut cards: Vec<Card> = Vec::new();
return cards;
} else {
panic!("No file was found and you did not allow to create a new one");
}
}
};
}
You need to check what the ErrorKind value is. If it is NotFound, then you know the file doesn't exist.
In your code:
let f = File::open(filename);
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};
, where you have Err(e) match arm, the type of e is std::io::Error. That struct has a method kind() which is what you need to check. Like this:
let mut f = match f {
Ok(file) => file,
Err(e) => {
if e.kind() == ErrorKind::NotFound {
println!("Oh dear, the file doesn't exist!");
}
...
}
};
I have a set of futures to be run in parallel and if one fails I would like to get the error to return to the caller.
Here is what I have been testing so far:
use futures::prelude::*;
use futures::stream::futures_unordered::FuturesUnordered;
use futures::{future, Future};
fn main() {
let tasks: FuturesUnordered<_> = (1..10).map(|_| async_func(false)).collect();
let mut runtime = tokio::runtime::Runtime::new().expect("Unable to start runtime");
let res = runtime.block_on(tasks.into_future());
if let Err(_) = res {
println!("err");
}
}
fn async_func(success: bool) -> impl Future<Item = (), Error = String> {
if success {
future::ok(())
} else {
future::err("Error".to_string())
}
}
How can I get the error from any failed futures? Even better would be to stop running any pending futures if a single future fails.
Your code is already returning and handling the error. If you attempted to use the error, the compiler will quickly direct you to the solution:
if let Err(e) = res {
println!("err: {}", e);
}
error[E0277]: `(std::string::String, futures::stream::futures_unordered::FuturesUnordered<impl futures::future::Future>)` doesn't implement `std::fmt::Display`
--> src/main.rs:12:29
|
12 | println!("err: {}", e);
| ^ `(std::string::String, futures::stream::futures_unordered::FuturesUnordered<impl futures::future::Future>)` cannot be formatted with the default formatter
|
= help: the trait `std::fmt::Display` is not implemented for `(std::string::String, futures::stream::futures_unordered::FuturesUnordered<impl futures::future::Future>)`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: required by `std::fmt::Display::fmt`
The Err value is a tuple of your error and the original stream to continue pulling after you have dealt with the error. This is what Stream::into_future / StreamFuture does.
Access the first value in the tuple to get to the error:
if let Err((e, _)) = res {
println!("err: {}", e);
}
If you want to see all of the values, you could keep polling the stream over and over (but don't do this because it's probably inefficient):
let mut f = tasks.into_future();
loop {
match runtime.block_on(f) {
Ok((None, _)) => {
println!("Stream complete");
break;
}
Ok((Some(v), next)) => {
println!("Success: {:?}", v);
f = next.into_future();
}
Err((e, next)) => {
println!("Error: {:?}", e);
f = next.into_future();
}
}
}
I modified an existing websocket client to save the message payload from the websocket server:
fn main()
{
let mut globaltest = "";
// ...some othercode
let receive_loop = thread::spawn(move || {
// Receive loop
for message in receiver.incoming_messages() {
let message: Message = match message {
Ok(m) => m,
Err(e) => {
println!("Receive Loop: {:?}", e);
let _ = tx_1.send(Message::close());
return;
}
};
match message.opcode {
Type::Close => {
// Got a close message, so send a close message and return
let _ = tx_1.send(Message::close());
return;
}
Type::Ping => match tx_1.send(Message::pong(message.payload)) {
// Send a pong in response
Ok(()) => (),
Err(e) => {
println!("Receive Loop: {:?}", e);
return;
}
},
// Say what we received
_ => {
println!("Receive Loop: {:?}", message);
// recive from motion
let buf = &message.payload;
{
let s = match str::from_utf8(buf) {
Ok(v) => v,
Err(e) => panic!("Invalid UTF-8 sequence: {}", e),
};
println!("{}",s);
////>>> problem here <<<
globaltest = s;
}
},
}
}
});
// ...some othercode
}
When I build, I get an error message:
nathaniel#nathaniel-virtual-machine:~/rustcoderep/rsdummywsclient$ sudo cargo build
Compiling rsdummywsclient v0.1.0 (file:///home/nathaniel/rustcoderep/rsdummywsclient)
src/main.rs:94:36: 94:51 error: `message.payload` does not live long enough
src/main.rs:94 let buf = &message.payload;
^~~~~~~~~~~~~~~
note: reference must be valid for the static lifetime...
src/main.rs:75:6: 106:4 note: ...but borrowed value is only valid for the block suffix following statement 0 at 75:5
src/main.rs:75 };
src/main.rs:76 match message.opcode {
src/main.rs:77 Type::Close => {
src/main.rs:78 // Got a close message, so send a close message and return
src/main.rs:79 let _ = tx_1.send(Message::close());
src/main.rs:80 return;
...
error: aborting due to previous error
I have no idea why. I tried a lot of solutions like Arc and Mutex, but none of them work :(
When I remove globaltest = s, the code builds and runs without problems. So I tried to write a simpler example:
use std::str;
use std::thread;
fn main() {
let mut y = 2;
let receive_loop = thread::spawn(move || {
let x = 1;
y = x;
println!("tt{:?}",y);
});
let receive_loop2 = thread::spawn(move || {
println!("tt2{:?}",y);
});
println!("{:?}",y);
}
This works... with almost the same structure.
Here is the full code, only a little different from the rust-websocket client sample:
extern crate websocket;
fn main() {
use std::thread;
use std::sync::mpsc::channel;
use std::io::stdin;
use std::str;
use websocket::{Message, Sender, Receiver};
use websocket::message::Type;
use websocket::client::request::Url;
use websocket::Client;
let mut globaltest ="";
let url = Url::parse("ws://127.0.0.1:2794").unwrap();
println!("Connecting to {}", url);
let request = Client::connect(url).unwrap();
let response = request.send().unwrap(); // Send the request and retrieve a response
println!("Validating response...");
response.validate().unwrap(); // Validate the response
println!("Successfully connected");
let (mut sender, mut receiver) = response.begin().split();
let (tx, rx) = channel();
let tx_1 = tx.clone();
let send_loop = thread::spawn(move || {
loop {
// Send loop
let message: Message = match rx.recv() {
Ok(m) => m,
Err(e) => {
println!("Send Loop: {:?}", e);
return;
}
};
match message.opcode {
Type::Close => {
let _ = sender.send_message(&message);
// If it's a close message, just send it and then return.
return;
},
_ => (),
}
// Send the message
match sender.send_message(&message) {
Ok(()) => (),
Err(e) => {
println!("Send Loop: {:?}", e);
let _ = sender.send_message(&Message::close());
return;
}
}
}
});
let receive_loop = thread::spawn(move || {
// Receive loop
for message in receiver.incoming_messages() {
let message: Message = match message {
Ok(m) => m,
Err(e) => {
println!("Receive Loop: {:?}", e);
let _ = tx_1.send(Message::close());
return;
}
};
match message.opcode {
Type::Close => {
// Got a close message, so send a close message and return
let _ = tx_1.send(Message::close());
return;
}
Type::Ping => match tx_1.send(Message::pong(message.payload)) {
// Send a pong in response
Ok(()) => (),
Err(e) => {
println!("Receive Loop: {:?}", e);
return;
}
},
// Say what we received
_ => {
println!("Receive Loop: {:?}", message);
// recive from motion
let buf = &message.payload;
{
let s = match str::from_utf8(buf) {
Ok(v) => v,
Err(e) => panic!("Invalid UTF-8 sequence: {}", e),
};
println!("{}",s);
globaltest = s;
}
},
}
}
});
loop {
let mut input = String::new();
stdin().read_line(&mut input).unwrap();
let trimmed = input.trim();
let message = match trimmed {
"/close" => {
// Close the connection
let _ = tx.send(Message::close());
break;
}
// Send a ping
"/ping" => Message::ping(b"PING".to_vec()),
// Otherwise, just send text
_ => Message::text(trimmed.to_string()),
};
match tx.send(message) {
Ok(()) => (),
Err(e) => {
println!("Main Loop: {:?}", e);
break;
}
}
}
// We're exiting
println!("Waiting for child threads to exit");
let _ = send_loop.join();
let _ = receive_loop.join();
println!("Exited");
}
#fjh thanks your reply!
I modified my code into this, changing globaltest in receive_loop and accessing it from the main thread loop. I still get a confusing error, even after spending three hours I still cannot solve it :(
fn main() {
let mut globaltest:Arc<Mutex<String>> = Arc::new(Mutex::new(String::from("")));
//some other code...
let receive_loop = thread::spawn(move || {
// Receive loop
for message in receiver.incoming_messages() {
let message: Message = match message {
Ok(m) => m,
Err(e) => {
println!("Receive Loop: {:?}", e);
let _ = tx_1.send(Message::close());
return;
}
};
match message.opcode {
Type::Close => {
// Got a close message, so send a close message and return
let _ = tx_1.send(Message::close());
return;
}
Type::Ping => match tx_1.send(Message::pong(message.payload)) {
// Send a pong in response
Ok(()) => (),
Err(e) => {
println!("Receive Loop: {:?}", e);
return;
}
},
// Say what we received
_ => {
let mut globaltest_child = globaltest.lock().unwrap();
println!("Receive Loop: {:?}", message);
// recive from motion
let buf = &message.payload;
{
let s = match str::from_utf8(buf) {
Ok(v) => v,
Err(e) => panic!("Invalid UTF-8 sequence: {}", e),
};
{
//>>> if I do like this, globaltest value will same like globaltest_child??
*globaltest_child = String::from(s);
println!("{:?}",globaltest_child.clone());
}
}
},
}
}
});
loop {
let message = Message::text("mtconnect");
match tx.send(message) {
Ok(()) => (),
Err(e) => {
println!("Main Loop: {:?}", e);
break;
}
}
///>>> problem here////
println!("{:?}",globaltest.clone());
thread::sleep(time::Duration::from_millis(3000));
}
}
The compiler always tells me:
athaniel#nathaniel-virtual-machine:~/rustcoderep/rsadapter$ sudo cargo run
Compiling rsadapter v0.1.0 (file:///home/nathaniel/rustcoderep/rsadapter)
src/main.rs:166:25: 166:35 error: use of moved value: `globaltest` [E0382]
src/main.rs:166 println!("{:?}",globaltest.clone());
^~~~~~~~~~
<std macros>:2:25: 2:56 note: in this expansion of format_args!
<std macros>:3:1: 3:54 note: in this expansion of print! (defined in <std macros>)
src/main.rs:166:9: 166:45 note: in this expansion of println! (defined in <std macros>)
src/main.rs:166:25: 166:35 help: run `rustc --explain E0382` to see a detailed explanation
src/main.rs:102:35: 153:3 note: `globaltest` moved into closure environment here because it has type `alloc::arc::Arc<std::sync::mutex::Mutex<collections::string::String>>`, which is non-copyable
src/main.rs:102 let receive_loop = thread::spawn(move || {
src/main.rs:103
src/main.rs:104
src/main.rs:105 // Receive loop
src/main.rs:106 for message in receiver.incoming_messages() {
src/main.rs:107 let message: Message = match message {
...
src/main.rs:102:35: 153:3 help: perhaps you meant to use `clone()`?
error: aborting due to previous error
I still can't access globaltest in another thread.
When I remove globaltest = s, the code builds and runs without problems.
Yes, because that assignment is not safe to do. You're trying to modify a local variable declared in the main thread from within your other thread. That could lead to all sorts of problems, like data races, which is why the compiler won't let you do it.
It's difficult to say what the best way to fix this is without knowing more about what you want to do. That being said, you could probably fix this by making globaltest an Arc<Mutex<String>> instead of a &str, so you could safely access it from both threads.
Whenever you have a problem, you should spend time reducing the problem. This helps you understand where the problem is and is likely to remove extraneous details that may be confusing you.
In this case, you could start by removing all of the other match arms, replacing them with panic! calls. Then try replacing libraries with your own code, then eventually just standard library code. Eventually you will get to something much smaller that reproduces the problem.
This is called creating an MCVE, and is highly encouraged when you ask a question on Stack Overflow. However, it's 100% useful to yourself whenever you have a problem you don't yet understand. As a professional programmer, you are expected to do this legwork.
Here's one possible MCVE I was able to create:
use std::{str, thread};
fn main() {
let mut global_string = "one";
let child = thread::spawn(move || {
let payload = b"Some allocated raw data".to_vec();
let s = str::from_utf8(&payload).unwrap();
global_string = s;
});
println!("{}", global_string);
}
And it produces the same error ("reference must be valid for the static lifetime"). Specifically, the global_string variable is a &'static str, while s is a &str with a lifetime equivalent to the payload it is borrowed from. Simply put, the payload will be deallocated before the thread exits, which means that the string would point to invalid memory, which could cause a crash or security vulnerability. This is a class of errors that Rust prevents against.
This is what fjh is telling you.
Instead, you need to be able to ensure that the string will continue to live outside of the thread. The simplest way is to allocate memory that it will control. In Rust, this is a String:
use std::{str, thread};
fn main() {
let mut global_string = "one".to_string();
let child = thread::spawn(move || {
let payload = b"Some allocated raw data".to_vec();
let s = str::from_utf8(&payload).unwrap();
global_string = s.to_string();
});
println!("{}", global_string);
}
Now we've changed the error to "use of moved value: global_string", because we are transferring ownership of the String from main to the thread. We could try to fix that by cloning the string before we give it to the thread, but then we wouldn't be changing the outer one that we want.
Even if we could set the value in the outer thread, we'd get in trouble because we'd be creating a race condition where two threads are acting in parallel on one piece of data. You have no idea what state the variable is in when you try to access it. That's where a Mutex comes in. It makes it so that multiple threads can safely share access to one piece of data, one at a time.
However, you still have the problem that only one thread can own the Mutex at a time, but you need two threads to own it. That's where Arc comes in. An Arc can be cloned and the clone can be given to another thread. Both Arcs then point to the same value and ensure it is cleaned up when nothing is using it any more.
Note that we have to clone the Arc<Mutex<String>>> before we spawn the thread because we are transferring ownership of it into the thread:
use std::{str, thread};
use std::sync::{Arc, Mutex};
fn main() {
let global_string = Arc::new(Mutex::new("one".to_string()));
let inner = global_string.clone();
let child = thread::spawn(move || {
let payload = b"Some allocated raw data".to_vec();
let s = str::from_utf8(&payload).unwrap();
*inner.lock().unwrap() = s.to_string();
});
child.join().unwrap();
let s = global_string.lock().unwrap();
println!("{}", *s);
}
I am using nickel.rs, PostgreSQL, and Angular.js. I can insert into my table with an HTTP POST:
// insert
{
let conn = shared_connection.clone();
router.post("/api/movies", middleware! { |request, mut response|
let conn = conn.lock().unwrap();
let stmt = match conn.prepare("insert into movie (title, releaseYear, director, genre)
values ($1, $2, $3, $4)") {
Ok(stmt) => stmt,
Err(e) => {
return response.send(format!("Preparing query failed: {}", e));
}
};
let movie = request.json_as::<MovieInsert>().unwrap();
match stmt.execute(&[
&movie.title.to_string(),
&movie.releaseYear,
&movie.director.to_string(),
&movie.genre.to_string()
]) {
Ok(v) => println!("Inserting movie was Success."),
Err(e) => println!("Inserting movie failed. => {:?}", e),
};
// ERROR (1)
// return response.set(Location("/".into()));
});
}
I know this works fine because the row is inserted in the PostgreSQL table. However, the Chrome web browser shows an error:
POST http://localhost:6767/api/movies 404 (Not Found)
I also added the code in ERROR (1) line
response.set(Location("/".into()));
however, console show the error.
expected `core::result::Result<nickel::middleware::Action<nickel::response::Response<'mw, _>, nickel::response::Response<'mw, _, hyper::net::Streaming>>, nickel::nickel_error::NickelError<'mw, _>>`,
found `&mut nickel::response::Response<'_, _>`
(expected enum `core::result::Result`,
found &-ptr)
Now it is my code applied what Shepmaster said.
// insert
{
let conn = shared_connection.clone();
router.post("/api/movies", middleware! { |request, mut response|
let conn = conn.lock().unwrap();
let stmt = match conn.prepare("insert into movie (title, releaseYear, director, genre)
values ($1, $2, $3, $4)") {
Ok(stmt) => stmt,
Err(e) => {
return response.send(format!("Preparing query failed: {}", e));
}
};
let movie = request.json_as::<MovieInsert>().unwrap();
match stmt.execute(&[
&movie.title.to_string(),
&movie.releaseYear,
&movie.director.to_string(),
&movie.genre.to_string()
]) {
Ok(v) => println!("Inserting movie was Success."),
Err(e) => println!("Inserting movie failed. => {:?}", e),
};
response.set(StatusCode::PermanentRedirect)
.set(Location("/".into()));
""
});
}
but the error occurred.
src/main.rs:155:18: 155:43 error: the trait modifier::Modifier<nickel::response::Response<'_, _>> is not implemented for the type hyper::header::common::location::Location [E0277]
src/main.rs:155 .set(Location("/".into()));
finally I fix like this!
Ok(v) => {
println!("Inserting movie was Success.");
response.set(StatusCode::Ok);
},
Err(e) => println!("Inserting movie failed. => {:?}", e),
};
//response.set(StatusCode::PermanentRedirect)
// .set(Location("/".into()));
//""
return response.send("");
The code, as currently listed, doesn't make any sense. There's nothing returned from your route handler:
match ... {
Ok(v) => println!("Inserting movie was Success."),
Err(e) => println!("Inserting movie failed. => {:?}", e),
};
Because nothing about the database or the frontend framework is important, your code is equivalent to this:
#[macro_use]
extern crate nickel;
use nickel::{Nickel, HttpRouter};
fn main() {
let mut server = Nickel::new();
server.post("/api/movies", middleware! { |request, mut response|
println!("Hello");
});
server.listen("127.0.0.1:6767");
}
If you return something from the handler, then the HTTP status code changes from a 404 to a 200.
If you'd like to redirect somewhere, you need to explicitly change the status code. The documentation for Response::set happens to have an example:
server.get("/a", middleware! { |_, mut res|
res.set(StatusCode::PermanentRedirect)
.set(Location("http://nickel.rs".into()));
""
});
Note that an empty string is returned, similar to before. You were trying to return the Response type directly.