vscode集成rust_合力:集成的Rust Web服务器

vscode集成rust

We’ve now explored a couple different libraries for some production tasks in Rust. A couple weeks ago, we used Diesel to create an ORM for some database types. And then last week, we used Rocketto make a basic web server to respond to basic requests. This week, we’ll put these two ideas together! We’ll use some more advanced functionality from Rocket to make some CRUD endpoints for our database type. Take a look at the code on Github here!

现在,我们已经探索了几个不同的库来处理Rust中的一些生产任务。 几周前,我们使用Diesel为某些数据库类型创建ORM。 然后上周,我们使用Rocket制作了一个基本的Web服务器来响应基本请求。 本周,我们将把这两个想法放在一起! 我们将使用Rocket的一些更高级的功能来为我们的数据库类型创建一些CRUD端点。 在这里看看Github上的代码!

If you’ve never written any Rust, you should start with the basics though! Take a look at our Rust Beginners Series!

如果您从未写过Rust,那么您应该从基础开始! 看看我们的Rust初学者系列

数据库状态和实例 (Database State and Instances)

Our first order of business is connecting to the database from our handler functions. There are some direct integrations you can check out between Rocket, Diesel, and other libraries. These can provide clever ways to add a connection argument to any handler.

我们的首要任务是从处理程序功能连接到数据库。 您可以在Rocket,Diesel和其他库之间签出一些直接集成 。 这些可以提供将连接参数添加到任何处理程序的巧妙方法。

But for now we’re going to keep things simple. We’ll re-generate the PgConnection within each endpoint. We'll maintain a "stateful" connection string to ensure they all use the same database.

但是现在,我们将使事情变得简单。 我们将在每个端点内重新生成PgConnection 。 我们将维护一个“有状态”连接字符串,以确保它们都使用相同的数据库。

Our Rocket server can “manage” different state elements. Suppose we have a function that gives us our database string. We can pass that to our server at initialization time.

我们的Rocket服务器可以“管理”不同的状态元素。 假设我们有一个给我们数据库字符串的函数。 我们可以在初始化时将其传递给我们的服务器。

fn local_conn_string() -> String {...}fn main() {
rocket::ignite()
.mount("/", routes![...])
.manage(local_conn_string())
.launch();}

Now we can access this String from any of our endpoints by giving an input the State<String> type. This allows us to create our connection:

现在,我们可以通过输入State<String>类型来从任何端点访问此String 。 这使我们可以创建我们的连接:

#[get(...)]
fn fetch_all_users(database_url: State<String>) -> ... {
let connection = pgConnection.establish(&database_url)
.expect("Error connecting to database!");
...
}

Note: We can’t use the PgConnection itself because stateful types need to be thread safe.

注意:我们不能使用PgConnection本身,因为有状态类型需要是线程安全的。

So any other of our endpoints can now access the same database. Before we start writing these, we need a couple things first though. Let’s recall that for our Diesel ORM we made a User type and a UserEntity type. The first is for inserting/creating, and the second is for querying. We need to add some instances to those types so they are compatible with our endpoints. We want to have JSON instances (Serialize, Deserialize), as well as FromForm for our User type:

因此,我们的任何其他端点现在都可以访问同一数据库。 在开始编写这些内容之前,我们首先需要做一些事情。 让我们回想一下,对于我们的Diesel ORM,我们创建了一个User类型和一个UserEntity类型。 第一个用于插入/创建,第二个用于查询。 我们需要向这些类型添加一些实例,以便它们与我们的端点兼容。 我们希望有JSON实例(序列化,反序列化),以及FromForm我们的User类型:

#[derive(Insertable, Deserialize, Serialize, FromForm)]
#[table_name="users"]
pub struct User {
...
}#[derive(Queryable, Serialize)]
pub struct UserEntity {
...
}

Now let’s see how we get these types from our endpoints!

现在,让我们看看如何从端点获取这些类型!

检索用户 (Retrieving Users)

We’ll start with a simple endpoint to fetch all the different users in our database. This will take no inputs, except our stateful database URL. It will return a vector of UserEntity objects, wrapped in Json.

我们将从一个简单的端点开始,以获取数据库中所有不同的用户。 除了我们的有状态数据库URL,这将不需要任何输入。 它将返回包装在JsonUserEntity对象的向量。

#[get("/users/all")]
fn fetch_all_users(database_url: State<String>)
-> Json<Vec<UserEntity>> {
...
}

Now all we need to do is connect to our database and run the query function. We can make our users vector into a Json object by wrapping with Json(). The Serialize instance lets us satisfy the Responder trait for the return value.

现在,我们需要做的就是连接到数据库并运行查询功能。 通过使用Json()包装,可以使用户向量成为Json对象。 Serialize实例使我们满足返回值的Responder特性。

#[get("/users/all")]
fn fetch_all_users(database_url: State<String>)
-> Json<Vec<UserEntity>> {
let connection = PgConnection::establish(&database_url)
.expect("Error connecting to database!");
Json(users.load::<UserEntity>(&connection)
.expect("Error loading users"))
}

Now for getting individual users. Once again, we’ll wrap a response in JSON. But this time we’ll return an optional, single, user. We’ll use a dynamic capture parameter in the URL for the User ID.

现在用于获取个人用户。 再一次,我们将响应包装在JSON中。 但是这一次,我们将返回一个可选的单一用户。 我们将在URL中为用户ID使用动态捕获参数。

#[get("/users/<uid>")]
fn fetch_user(database_url: State<String>, uid: i32)
-> Option<Json<UserEntity>> {
let connection = ...;
...
}

We’ll want to filter on the users table by the ID. This will give us a list of different results. We want to specify this vector as mutable. Why? In the end, we want to return the first user. But Rust’s memory rules mean we must either copy or move this item. And we don’t want to move a single item from the vector without moving the whole vector. So we’ll remove the head from the vector entirely, which requires mutability.

我们要按ID过滤用户表。 这将为我们提供不同结果的列表。 我们想将此向量指定为可变的。 为什么? 最后,我们要返回第一个用户。 但是Rust的记忆规则意味着我们必须复制或移动该项目。 而且,我们不希望在不移动整个矢量的情况下从矢量移动单个项目。 因此,我们将从向量中完全删除头部,这需要可变性。

#[get("/users/<uid>")]
fn fetch_user(database_url: State<String>, uid: i32)
-> Option<Json<UserEntity>> {
let connection = ...;
use rust_web::schema::users::dsl::*;
let mut users_by_id: Vec<UserEntity> =
users.filter(id.eq(uid))
.load::<UserEntity>(&connection)
.expect("Error loading users");
...
}

Now we can do our case analysis. If the list is empty, we return None. Otherwise, we'll remove the user from the vector and wrap it.

现在我们可以进行案例分析了。 如果列表为空,则返回None 。 否则,我们将从向量中删除用户并将其包装。

#[get("/users/<uid>")]
fn fetch_user(database_url: State<String>, uid: i32) -> Option<Json<UserEntity>> {
let connection = ...;
use rust_web::schema::users::dsl::*;
let mut users_by_id: Vec<UserEntity> =
users.filter(id.eq(uid))
.load::<UserEntity>(&connection)
.expect("Error loading users");
if users_by_id.len() == 0 {
None
} else {
let first_user = users_by_id.remove(0);
Some(Json(first_user))
}
}

创建/更新/删除 (Create/Update/Delete)

Hopefully you can see the pattern now! Our queries are all pretty simple. So our endpoints all follow a similar pattern. Connect to the database, run the query and wrap the result. We can follow this process for the remaining three endpoints in a basic CRUD setup. Let’s start with “Create”:

希望您现在可以看到图案! 我们的查询都非常简单。 因此,我们的端点都遵循类似的模式。 连接到数据库,运行查询并包装结果。 我们可以针对基本CRUD设置中的其余三个端点遵循此过程。 让我们从“创建”开始:

#[post("/users/create", format="application/json", data = "<user>")]
fn create_user(database_url: State<String>, user: Json<User>)
-> Json<i32> {
let connection = ...;
let user_entity: UserEntity = diesel::insert_into(users::table)
.values(&*user)
.get_result(&connection).expect("Error saving user");
Json(user_entity.id)
}

As we discussed last week, we can use data together with Json to specify the form data in our post request. We de-reference the user with * to get it out of the JSON wrapper. Then we insert the user and wrap its ID to send back.

正如我们上周讨论的那样,我们可以将dataJson一起使用以在我们的帖子请求中指定表单数据。 我们用*取消引用用户,以使其脱离JSON包装器。 然后,我们插入用户并包装其ID以发回。

Deleting a user is simple as well. It has the same dynamic path as fetching a user. We just make a delete call on our database instead.

删除用户也很简单。 它具有与获取用户相同的动态路径。 我们只是对数据库进行delete调用。

#[delete("/users/<uid>")]
fn delete_user(database_url: State<String>, uid: i32) -> Json<i32> {
let connection = ...;
use rust_web::schema::users::dsl::*;
diesel::delete(users.filter(id.eq(uid)))
.execute(&connection)
.expect("Error deleting user");
Json(uid)
}

Updating is the last endpoint, which takes a put request. The endpoint mechanics are just like our other endpoints. We use a dynamic path component to get the user's ID, and then provide a Userbody with the updated field values. The only trick is that we need to expand our Diesel knowledge a bit. We'll use update and set to change individual fields on an item.

更新是最后一个端点,它接受put请求。 端点机制与我们的其他端点一样。 我们使用动态路径组件来获取用户的ID,然后为User主体提供更新的字段值。 唯一的窍门是我们需要稍微扩展柴油知识。 我们将使用updateset来更改项目上的各个字段。

#[put("/users/<uid>/update", format="json", data="<user>")]
fn update_user(
database_url: State<String>, uid: i32, user: Json<User>)
-> Json<UserEntity> { let connection = ...;
use rust_web::schema::users::dsl::*;
let updated_user: UserEntity =
diesel::update(users.filter(id.eq(uid)))
.set((name.eq(&user.name),
email.eq(&user.email),
age.eq(user.age)))
.get_result::<UserEntity>(&connection)
.expect("Error updating user");
Json(updated_user)
}

The other gotcha is that we need to use references (&) for the string fields in the input user. But now we can add these routes to our server, and it will manipulate our database as desired!

另一个难题是我们需要对输入用户中的字符串字段使用引用( & )。 但是现在我们可以将这些路由添加到我们的服务器,它将按需操作数据库!

结论 (Conclusion)

There are still lots of things we could improve here. For example, we’re still using .expect in many places. From the perspective of a web server, we should be catching these issues and wrapping them with "Err 500". Rocket also provides some good mechanics for fixing that. Next week though, we'll pivot to another server problem that Rocket solves adeptly: authentication. We should restrict certain endpoints to particular users. Rust provides an authentication scheme that is neatly encoded in the type system!

在这里,我们还有很多可以改进的地方。 例如,我们仍然在许多地方使用.expect 。 从Web服务器的角度来看,我们应该抓住这些问题,并用“ Err 500”包装它们。 火箭队还提供了一些很好的机制来解决这个问题。 不过,下周,我们将讨论Rocket巧妙解决的另一个服务器问题:身份验证。 我们应该将某些端点限制为特定用户。 Rust提供了一种在类型系统中经过整齐编码的身份验证方案!

For a more in-depth introduction to Rust, watch our Rust Video Tutorial. It will take you through a lot of key skills like understanding memory and using Cargo!

有关Rust的更深入介绍,请观看我们的Rust Video Tutorial 。 它会带您学习很多关键技能,例如了解内存和使用Cargo!

翻译自: https://medium.com/@james_32022/joining-forces-an-integrated-rust-web-server-7314b0b6d199

vscode集成rust

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值