# Rust Web Server Tutorial

**Introduction**

In this tutorial, we’ll build a simple Rust web server from scratch using the `actix-web` framework. `actix-web` is one of the most popular and performant web frameworks for Rust, and it makes it easy to get a web server running quickly.

**Prerequisites:**

* Basic understanding of Rust programming
* Rust installed on your machine (`rustc` and `cargo`)
* Basic understanding of HTTP and web servers

***

#### Step 1: Set Up Your Rust Project

First, set up a new Rust project using `cargo`. Open your terminal and run the following command:

```bash
bashCopy codecargo new rust-web-server
```

Navigate into your new project:

```bash
bashCopy codecd rust-web-server
```

This creates a basic Rust project with a `src/main.rs` file.

#### Step 2: Add Dependencies

We’ll need to add the `actix-web` crate to the project. Open `Cargo.toml` and add the following lines under `[dependencies]`:

```toml
tomlCopy code[dependencies]
actix-web = "4.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
```

* `actix-web` will be the main framework for handling HTTP requests.
* `serde` is for serializing/deserializing data.
* `serde_json` will handle JSON formatting.

Then, update your dependencies by running:

```bash
bashCopy codecargo build
```

#### Step 3: Create a Simple Web Server

Now, let’s build a simple "Hello, World!" web server. Open `src/main.rs` and replace its contents with the following:

```rust
rustCopy codeuse actix_web::{get, App, HttpResponse, HttpServer, Responder};

#[get("/")]
async fn hello() -> impl Responder {
    HttpResponse::Ok().body("Hello, World!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(hello) // Attach the hello route to the web server
    })
    .bind("127.0.0.1:8080")? // Bind the server to a local address
    .run()
    .await
}
```

Here’s what’s happening:

* We define a route `/` using the `#[get("/")]` attribute.
* The `hello` function returns a simple "Hello, World!" response using `HttpResponse::Ok()`.
* In the `main` function, we create a new `HttpServer`, bind it to port `8080`, and attach our `hello` service to the server.

To run the server, use:

```bash
bashCopy codecargo run
```

Now, go to `http://127.0.0.1:8080/` in your browser, and you should see "Hello, World!"

***

#### Step 4: Adding JSON Support

Next, let’s handle JSON responses. This is useful when building APIs. We’ll define a route that returns JSON data.

Replace the existing code in `main.rs` with the following:

```rust
rustCopy codeuse actix_web::{get, App, HttpResponse, HttpServer, Responder};
use serde::Serialize;

#[derive(Serialize)]
struct MyResponse {
    message: String,
    status: u16,
}

#[get("/json")]
async fn json_response() -> impl Responder {
    let response = MyResponse {
        message: "This is a JSON response".to_string(),
        status: 200,
    };

    HttpResponse::Ok().json(response)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(json_response) // Add the new JSON route
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}
```

Here’s what changed:

* We defined a `MyResponse` struct, deriving `Serialize`, which allows it to be converted to JSON.
* In `json_response`, we create a `MyResponse` instance and return it as JSON using `HttpResponse::Ok().json()`.

Run the server again, and navigate to `http://127.0.0.1:8080/json`. You’ll receive a JSON response like:

```json
jsonCopy code{
  "message": "This is a JSON response",
  "status": 200
}
```

***

#### Step 5: Handling POST Requests

Now that we’ve covered GET requests, let’s implement a POST route. We'll create an endpoint that accepts JSON data and responds with a message.

Modify `main.rs`:

```rust
rustCopy codeuse actix_web::{post, web, App, HttpResponse, HttpServer, Responder};
use serde::Deserialize;

#[derive(Deserialize)]
struct InputData {
    name: String,
}

#[post("/echo")]
async fn echo(input: web::Json<InputData>) -> impl Responder {
    let response = format!("Hello, {}!", input.name);
    HttpResponse::Ok().body(response)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(echo) // POST route
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}
```

Here:

* We define a `POST` route with `#[post("/echo")]`.
* The `InputData` struct is used to deserialize the incoming JSON body.
* We extract the JSON from the request using `web::Json<InputData>` and return a personalized message.

To test this, send a POST request using a tool like `curl` or Postman:

```bash
bashCopy codecurl -X POST http://127.0.0.1:8080/echo -H "Content-Type: application/json" -d '{"name": "Alice"}'
```

You should get back:

```
textCopy codeHello, Alice!
```

***

#### Step 6: Handling Errors

Let’s add basic error handling to ensure the server behaves correctly when receiving invalid requests. We’ll modify the POST handler to return an appropriate error response when the JSON format is incorrect.

Here’s how you can modify the `echo` route to handle errors:

```rust
rustCopy codeuse actix_web::{error, post, web, App, HttpResponse, HttpServer, Responder};
use serde::Deserialize;

#[derive(Deserialize)]
struct InputData {
    name: String,
}

#[post("/echo")]
async fn echo(input: web::Json<InputData>) -> Result<impl Responder, actix_web::Error> {
    if input.name.is_empty() {
        return Err(error::ErrorBadRequest("Name cannot be empty"));
    }
    
    let response = format!("Hello, {}!", input.name);
    Ok(HttpResponse::Ok().body(response))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(echo)
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}
```

Here:

* We use `Result<impl Responder, actix_web::Error>` to return either a valid response or an error.
* If the `name` field is empty, the server returns a `400 Bad Request` error with a custom message.

***

#### Step 7: Serving Static Files

Next, let’s add support for serving static files such as HTML, CSS, and JavaScript. This is useful when building full-fledged web applications.

First, create a directory named `static` in the root of your project and add a file called `index.html` inside:

```html
htmlCopy code<!-- static/index.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Rust Web Server</title>
</head>
<body>
    <h1>Welcome to the Rust Web Server</h1>
</body>
</html>
```

Now, modify `main.rs` to serve this static file:

```rust
rustCopy codeuse actix_files as fs;
use actix_web::{App, HttpServer};

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(fs::Files::new("/", "./static").index_file("index.html"))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}
```

Here:

* We use `actix_files::Files` to serve files from the `static` directory.
* The `index_file` method specifies `index.html` as the default file to serve when accessing the root.

Now when you navigate to `http://127.0.0.1:8080/`, you’ll see the `index.html` content.

***

#### Step 8: Adding Middleware

Middleware allows you to modify requests and responses globally. For example, you might want to log every incoming request or add security headers.

Let’s add a simple logger middleware:

Modify `main.rs`:

```rust
rustCopy codeuse actix_web::{middleware, App, HttpServer};

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .wrap(middleware::Logger::default()) // Add logger middleware
            .service(fs::Files::new("/", "./static").index_file("index.html"))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}
```

* The `middleware::Logger::default()` middleware logs each request.
* You can customize it by passing a format string to `Logger::new()`.

Make sure to enable `actix-web`’s logger by setting the environment variable:

```bash
bashCopy codeRUST_LOG=actix_web=info cargo run
```

***

#### Conclusion

We’ve built a simple web server using Rust and `actix-web`, covering essential web server features like routing, JSON handling, error handling, serving static files, and adding middleware. From here, you can explore more advanced features like databases, websockets, or authentication with `actix-web`.

This should give you a strong foundation for building web applications with Rust!
