No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

隆Hola mundo, Actix!

11/21
Recursos

Es momento de realizar el primer 隆Hola Mundo! Desde una aplicaci贸n web con Rust y lograr renderizar texto a un navegador.

Servidor HTTP con Rust

El primer paso para servir contenido a un navegador web, es establecer la conexi贸n con un servidor web. Para esto, utilizaremos Actix y todo comienza agregando la dependencia en el archivo Cargo.toml:

[dependencies]
actix-web = "4"

A continuaci贸n, exponemos el servidor HTTP de la siguiente manera:

// Importamos lo necesario para levantar un servidor web y exponer endpoints
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};

// Endpoint GET que devuelve texto, utiliamos el Macro GET para indicar el verbo HTTP
#[get("/")]
async fn hello_wold() -> impl Responder {
    HttpResponse::Ok().body("Hola Platzi!!!")
}

// Macro para establecer la aplicaci贸n del tipo Web, la misma debe ser as铆ncrona
#[actix_web::main]
async fn main() -> std::io::Result<()> {

    // Levantamos el servidor HTTP
    HttpServer::new(|| {
        // Exponemos los endpoints que indiquemos
        App::new().service(hello_wold)
    // Indicamos el host y el puerto donde escuchar谩 el servidor
    }).bind(("127.0.0.1", 8080)).unwrap().run().await

}

Ahora, lanza tu servidor HTTP con cargo run y si todo ha ido bien, ingresa desde un navegador a la ruta 127.0.0.1:8080/ para visualizar el mensaje Hola Platzi!!! que devuelve el endpoint que hemos creado.

Cabe mencionar un error muy com煤n. Te habr谩s dado cuenta de que Rust es un lenguaje sensible al punto y coma ;, es obligatorio su utilizaci贸n. Sin embargo, en las operaciones as铆ncronas no es del todo necesario. Si quieres utilizarlo, solo agrega un return a la l铆nea de c贸digo correspondiente:

// Sin ;
HttpResponse::Ok().body("Hola Platzi!!!")

// Con ;
return HttpResponse::Ok().body("Hola Platzi!!!");

Ambas expresiones son equivalentes.

Servidor HTTP y conexi贸n a BBDD

Teniendo el servidor web corriendo, configuremos el resto de la aplicaci贸n para realizar consultas a la BBDD y devolver los datos a trav茅s de los endpoints.

Comienza agregando al Cargo.toml la dependencia de diesel llamada r2d2 (Si, como Star Wars).

[dependencies]
diesel = { version = "1.4.4", features = ["postgres", "r2d2"] }

r2d2 nos permitir谩 crear un Pool de Conexi贸n a la BBDD. En otras palabras, establecer la conexi贸n a la base una sola vez, y luego compartir esta conexi贸n con el resto de la aplicaci贸n.

Ahora si, configuremos el resto de la aplicaci贸n para conectarnos a la base de datos y preparar nuestros endpoints para consultar los mismos.

#[macro_use]

extern crate diesel;

pub mod schema;
pub mod models;

use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};

use dotenv::dotenv;
use std::env;

use diesel::prelude::*;
use diesel::pg::PgConnection;

// Librer铆as para crear una conexi贸n a la BBDD y compartirla en toda la aplicaci贸n
use diesel::r2d2::{self, ConnectionManager};
use diesel::r2d2::Pool;


#[get("/")]
async fn hello_wold() -> impl Responder {
    HttpResponse::Ok().body("Hola Platzi!!!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {

    dotenv().ok();
    let db_url = env::var("DATABASE_URL").expect("La variable de entorno DATABASE_URL no existe.");

    let connection = ConnectionManager::<PgConnection>::new(db_url);

    // El POOL sirve para compartir la conexi贸n con otros servicios
    let pool = Pool::builder().build(connection).expect("No se pudo construir el Pool");

    HttpServer::new(move || {
        // Compartimos el pool de conexi贸n a cada endpoint
        App::new().service(hello_wold).app_data(web::Data::new(pool.clone()))       
    }).bind(("127.0.0.1", 8080)).unwrap().run().await

}

Observa que en el HttpServer estamos utilizando una palabra reservada llamada move. move viene de un concepto a煤n m谩s grande en Rust denominado Ownership, en pocas palabras, todo en Rust tiene un 鈥渄ue帽o鈥 y si queremos compartir el Pool de Conexi贸n con otra funci贸n o m贸dulo, debemos indicarlo expl铆citamente con esta palabra reservada.

No deja de ser un concepto relacionado con el Scope de una pieza de c贸digo, aunque es un concepto que no existe en otros lenguajes.

Rust es mono-hilo y move mueve, justamente, el hijo de ejecuci贸n principal de la aplicaci贸n, a esa otra funci贸n que necesitamos emplear y compartir un dato. Por eso las tareas son as铆ncronas, para permitirle a Rust procesar m煤ltiples solicitudes a la vez. Muy similar a como funciona Javascript. Si es que est谩s familiarizado con el lenguaje, lo comprender谩s m谩s f谩cilmente.

Vuelve a ejecutar tu aplicaci贸n con cargo run para corroborar que el servidor HTTP a煤n funciona.

Y todo listo, la app est谩 lista para mostrar datos tomados de la BBDD en el navegador web.


Contribuci贸n creada por: Kevin Fiorentino.

Aportes 11

Preguntas 1

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

como comentario el shortcut de unwrap es 鈥?鈥

.bind(("0.0.0.0", 9900))?.run()

En lugar de 0.0.0.0, conviene siempre utilizar localhost o 127.0.0.1.

Actualmente .data() aparece como deprecado, una alternativa sugerido por Rust es cambiarlo por app_data()

En principio, no deber铆a de cambiar nada relacionado con el curso

Hubo una actualizaci贸n de Diesel, para la version 2 (he tenido muchos problemas con respecto a las versiones) y ahora fue con Diesel, estuve como 3 horas revisando y revisando (soy totalmente nuevo) logre armar una soluci贸n completa para esta clase, pero actualizado.

extern crate diesel;

use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
use dotenvy::dotenv;
use std::env;

use diesel::pg::PgConnection;
use diesel::prelude::*;

use diesel::r2d2::Pool;
use diesel::r2d2::{self, ConnectionManager};

pub mod models;
pub mod schema;

#[get("/")]
async fn hello_world() -> impl Responder {
    return format!("Hello world!");
}

#[actix_web::main] // or #[tokio::main]
async fn main() -> std::io::Result<()> {
    dotenv().ok();
    let database_url = env::var("DATABASE_URL").expect("DB url variable doesn't found");

    let manager = ConnectionManager::<PgConnection>::new(database_url);

    let pool = Pool::builder()
        .build(manager)
        .expect("No se pudo construir la pool");

    HttpServer::new(move || App::new().service(hello_world).app_data(pool.clone()))
        .bind(("localhost", 8080))?
        .run()
        .await
}

Para el archivo cargo se debe de agregar r2d2 como feature en Diesel ya que no viene activo por defecto.

// Cargo.toml
[package]
name = "blog-platzi"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
diesel = { version = "2.0.0", features = ["postgres", "r2d2"] }
dotenvy = "0.15"
actix-web = "4"

Como pueden observar, se debe de agregar de manera manual r2d2 y con esto funciona correctamente la importaci贸n del paquete.

Espero pueda apoyar a alguno de ustedes que este con el mismo problema

Si no comprend铆 mal, todo en Rust tiene un 鈥渄ue帽o鈥. El concepto de Ownership indica que, en este ejemplo, el Pool de Conexi贸n pertenece a main() y si queremos utilizarlo en otra funci贸n debemos compartirlo con la palabra reservada move para mover el hilo de ejecuci贸n principal a esa otra funci贸n, ya que Rust es mono-hilo. Similar a como funciona NodeJS que tambi茅n lo es y necesita de la asincronisidad para procesar m煤ltiples solicitudes a la vez.

Hector dice que: 鈥測a sabemos que significa cada cosa鈥, frase que me acaba de generar bastante incomodidad porque habla como si lo hubiese explicado todo, cuando en realidad no ha sido as铆. Muchos huecos conceptuales, parase ser que presuntamente el curso es improvisado, como si estuviera tratando de explicar el ejemplo de inicio de alguna documentaci贸n. Creo que sale mas eficaz leer personalmente la documentaci贸n y generar apoyo con chatGPT.

Si alguien se siente perdido hasta este punto, pare, no contin煤e. Tome aire, respire profundo, auto regule la frustraci贸n que siente hasta el momento, evita sentir enojo y claro que te entiendo, el curso b谩sico de Rust no tiene nada que ver con este curso. Por esa raz贸n te sugiero que ve a leer los siguiente libros y en el proceso que comprendas un par de cosas mas, regresa y continua si ese es tu deseo. Quiz谩 y aprendas un par de cosas mas viendo este tutorial, y no, no todos los cursos de Platzi son as铆, asi que no te rindas. Si, es verdad, te va a tomar un poquito mas de tiempo leer los libros pero cr茅eme, valdr谩 el esfuerzo, recuerda que es importante construir bases solidas.

Libros sugeridos:

no tengo problema con que haya errores al codear pero desde el primer curso de rust que vi contigo me incomoda que no planees las clases o que digas que explicaras algo despu茅s y luego no explicarlo.

Para entender mejor la 鈥渕ove鈥 keyword

Closures Rust book
O el link completo: https://doc.rust-lang.org/book/ch13-01-closures.html?highlight=closure#moving-captured-values-out-of-closures-and-the-fn-traits

let mut x = String::from("Hello");

let func = move || {  // <--- Con la move keyword, la closure toma ownership de la variable 'x'
   // Move es como decir: "hey pasa la variable x a esta function como valor y no como referencia"
    x.push_str(" World!");
};

println!("{}", x); // Y al tomar ownership de 'x' no se puede usar aqu铆

Diesel ha cambiado mucho, al dia de hoy 25/07/2023 pude hacer funcionar el programa.
Resulta que uno debe descargar el archivo gettext0.21-iconv1.16-shared-64 (buscar en Internet), renombrarlo y ponerlo en la carpeta src para que se pueda ejecutar el programa.

Como recomendacion, si algo no funciona vayan a la documentacion porque lo mas seguro es que han actualizado algo, tambien es bueno revisar las respuestas de

en la version actix-web = 鈥4.3.1鈥 ya el pool de datos se implementa de una manera diferente

HttpServer::new( move || {
        App::new()
        //cambios por version
        .app_data(web::Data::new(pool.clone()))
        //
        .service(hello_world)
        
    })

use diesel::prelude:馃槜;

Esto al inicio del archivo de models me ayud贸 con los Queryable que me aparecia error