mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-04-12 11:48:30 +00:00
* Add initial documentations * Update documentation for Basic Client and add WebSocket section * feat: add a static site generator with multi-language support - Introduced a new Rust-based static site generator in the `docs-gen` directory. - Implemented core functionality for building sites from markdown files, including: - Configuration loading from `config.toml`. - Markdown rendering with frontmatter support. - Navigation generation based on page structure. - Static file copying and output directory management. - Added templates for base layout, pages, and portal. - Created a CSS file for styling and a JavaScript file for interactive features like language selection and theme toggling. - Updated documentation source with new configuration and example pages in English and Japanese. - Added a `justfile` target for building the documentation site. * Add language/theme toggle functionality - Created a new Japanese tour index page at docs/ja/tour/index.html - Implemented navigation links for various sections of the cpp-httplib tutorial - Added a language selector to switch between English and Japanese - Introduced theme toggle functionality to switch between light and dark modes - Added mobile sidebar toggle for better navigation on smaller screens
281 lines
7.9 KiB
Markdown
281 lines
7.9 KiB
Markdown
---
|
|
title: "Basic Server"
|
|
order: 3
|
|
---
|
|
|
|
In the previous chapter, you sent requests from a client to a test server. Now let's walk through how that server actually works.
|
|
|
|
## Starting the Server
|
|
|
|
Once you've registered your routes, call `svr.listen()` to start the server.
|
|
|
|
```cpp
|
|
svr.listen("0.0.0.0", 8080);
|
|
```
|
|
|
|
The first argument is the host, and the second is the port. `"0.0.0.0"` listens on all network interfaces. Use `"127.0.0.1"` if you want to accept connections from your own machine only.
|
|
|
|
`listen()` is a blocking call. It won't return until the server stops. The server keeps running until you press `Ctrl+C` in your terminal or call `svr.stop()` from another thread.
|
|
|
|
## Routing
|
|
|
|
Routing is the heart of any server. It's how you tell cpp-httplib: when a request comes in for this URL with this HTTP method, run this code.
|
|
|
|
```cpp
|
|
httplib::Server svr;
|
|
|
|
svr.Get("/hi", [](const httplib::Request &req, httplib::Response &res) {
|
|
res.set_content("Hello!", "text/plain");
|
|
});
|
|
```
|
|
|
|
`svr.Get()` registers a handler for GET requests. The first argument is the path, the second is the handler function. When a GET request arrives at `/hi`, your lambda runs.
|
|
|
|
There's a method for each HTTP verb.
|
|
|
|
```cpp
|
|
svr.Get("/path", handler); // GET
|
|
svr.Post("/path", handler); // POST
|
|
svr.Put("/path", handler); // PUT
|
|
svr.Delete("/path", handler); // DELETE
|
|
```
|
|
|
|
The handler signature is `(const httplib::Request &req, httplib::Response &res)`. You can use `auto` to keep it short.
|
|
|
|
```cpp
|
|
svr.Get("/hi", [](const auto &req, auto &res) {
|
|
res.set_content("Hello!", "text/plain");
|
|
});
|
|
```
|
|
|
|
The handler only runs when the path matches. Requests to unregistered paths automatically return 404.
|
|
|
|
## The Request Object
|
|
|
|
The first parameter `req` gives you everything the client sent.
|
|
|
|
### Body
|
|
|
|
`req.body` holds the request body as a `std::string`.
|
|
|
|
```cpp
|
|
svr.Post("/post", [](const auto &req, auto &res) {
|
|
// Echo the body back to the client
|
|
res.set_content(req.body, "text/plain");
|
|
});
|
|
```
|
|
|
|
### Headers
|
|
|
|
Use `req.get_header_value()` to read a request header.
|
|
|
|
```cpp
|
|
svr.Get("/check", [](const auto &req, auto &res) {
|
|
auto auth = req.get_header_value("Authorization");
|
|
res.set_content("Auth: " + auth, "text/plain");
|
|
});
|
|
```
|
|
|
|
### Query Parameters and Form Data
|
|
|
|
`req.get_param_value()` retrieves a parameter by name. It works for both GET query parameters and POST form data.
|
|
|
|
```cpp
|
|
svr.Get("/search", [](const auto &req, auto &res) {
|
|
auto q = req.get_param_value("q");
|
|
res.set_content("Query: " + q, "text/plain");
|
|
});
|
|
```
|
|
|
|
A request to `/search?q=cpp-httplib` gives you `"cpp-httplib"` for `q`.
|
|
|
|
To loop over all parameters, use `req.params`.
|
|
|
|
```cpp
|
|
svr.Post("/submit", [](const auto &req, auto &res) {
|
|
std::string result;
|
|
for (auto &[key, val] : req.params) {
|
|
result += key + " = " + val + "\n";
|
|
}
|
|
res.set_content(result, "text/plain");
|
|
});
|
|
```
|
|
|
|
### File Uploads
|
|
|
|
Files uploaded via multipart form data are available through `req.form.get_file()`.
|
|
|
|
```cpp
|
|
svr.Post("/upload", [](const auto &req, auto &res) {
|
|
auto f = req.form.get_file("file");
|
|
auto content = f.filename + " (" + std::to_string(f.content.size()) + " bytes)";
|
|
res.set_content(content, "text/plain");
|
|
});
|
|
```
|
|
|
|
`f.filename` gives you the filename, and `f.content` gives you the file data.
|
|
|
|
## Path Parameters
|
|
|
|
Sometimes you want to capture part of the URL as a variable -- for example, the `42` in `/users/42`. Use the `:param` syntax to do that.
|
|
|
|
```cpp
|
|
svr.Get("/users/:id", [](const auto &req, auto &res) {
|
|
auto id = req.path_params.at("id");
|
|
res.set_content("User ID: " + id, "text/plain");
|
|
});
|
|
```
|
|
|
|
A request to `/users/42` gives you `"42"` from `req.path_params.at("id")`. `/users/100` gives you `"100"`.
|
|
|
|
You can capture multiple segments at once.
|
|
|
|
```cpp
|
|
svr.Get("/users/:user_id/posts/:post_id", [](const auto &req, auto &res) {
|
|
auto user_id = req.path_params.at("user_id");
|
|
auto post_id = req.path_params.at("post_id");
|
|
res.set_content("User: " + user_id + ", Post: " + post_id, "text/plain");
|
|
});
|
|
```
|
|
|
|
### Regex Patterns
|
|
|
|
You can also write a regular expression directly in the path instead of `:param`. Capture group values are available via `req.matches`, which is a `std::smatch`.
|
|
|
|
```cpp
|
|
// Only accept numeric IDs
|
|
svr.Get(R"(/files/(\d+))", [](const auto &req, auto &res) {
|
|
auto id = req.matches[1]; // First capture group
|
|
res.set_content("File ID: " + std::string(id), "text/plain");
|
|
});
|
|
```
|
|
|
|
`/files/42` matches, but `/files/abc` doesn't. This is handy when you want to constrain what values are accepted.
|
|
|
|
## Building a Response
|
|
|
|
The second parameter `res` is how you send data back to the client.
|
|
|
|
### Body and Content-Type
|
|
|
|
`res.set_content()` sets the body and Content-Type. That's all you need for a 200 response.
|
|
|
|
```cpp
|
|
svr.Get("/hi", [](const auto &req, auto &res) {
|
|
res.set_content("Hello!", "text/plain");
|
|
});
|
|
```
|
|
|
|
### Status Code
|
|
|
|
To return a different status code, assign to `res.status`.
|
|
|
|
```cpp
|
|
svr.Get("/not-found", [](const auto &req, auto &res) {
|
|
res.status = 404;
|
|
res.set_content("Not found", "text/plain");
|
|
});
|
|
```
|
|
|
|
### Response Headers
|
|
|
|
Add response headers with `res.set_header()`.
|
|
|
|
```cpp
|
|
svr.Get("/with-header", [](const auto &req, auto &res) {
|
|
res.set_header("X-Custom", "my-value");
|
|
res.set_content("Hello!", "text/plain");
|
|
});
|
|
```
|
|
|
|
## Walking Through the Test Server
|
|
|
|
Now let's use what we've learned to read through the test server from the previous chapter.
|
|
|
|
### GET /hi
|
|
|
|
```cpp
|
|
svr.Get("/hi", [](const auto &, auto &res) {
|
|
res.set_content("Hello!", "text/plain");
|
|
});
|
|
```
|
|
|
|
The simplest possible handler. We don't need any information from the request, so the `req` parameter is left unnamed. It just returns `"Hello!"`.
|
|
|
|
### GET /search
|
|
|
|
```cpp
|
|
svr.Get("/search", [](const auto &req, auto &res) {
|
|
auto q = req.get_param_value("q");
|
|
res.set_content("Query: " + q, "text/plain");
|
|
});
|
|
```
|
|
|
|
`req.get_param_value("q")` pulls out the query parameter `q`. A request to `/search?q=cpp-httplib` returns `"Query: cpp-httplib"`.
|
|
|
|
### POST /post
|
|
|
|
```cpp
|
|
svr.Post("/post", [](const auto &req, auto &res) {
|
|
res.set_content(req.body, "text/plain");
|
|
});
|
|
```
|
|
|
|
An echo server. Whatever body the client sends, `req.body` holds it, and we send it straight back.
|
|
|
|
### POST /submit
|
|
|
|
```cpp
|
|
svr.Post("/submit", [](const auto &req, auto &res) {
|
|
std::string result;
|
|
for (auto &[key, val] : req.params) {
|
|
result += key + " = " + val + "\n";
|
|
}
|
|
res.set_content(result, "text/plain");
|
|
});
|
|
```
|
|
|
|
Loops over the form data in `req.params` using structured bindings (`auto &[key, val]`) to unpack each key-value pair.
|
|
|
|
### POST /upload
|
|
|
|
```cpp
|
|
svr.Post("/upload", [](const auto &req, auto &res) {
|
|
auto f = req.form.get_file("file");
|
|
auto content = f.filename + " (" + std::to_string(f.content.size()) + " bytes)";
|
|
res.set_content(content, "text/plain");
|
|
});
|
|
```
|
|
|
|
Receives a file uploaded via multipart form data. `req.form.get_file("file")` fetches the field named `"file"`, and we respond with the filename and size.
|
|
|
|
### GET /users/:id
|
|
|
|
```cpp
|
|
svr.Get("/users/:id", [](const auto &req, auto &res) {
|
|
auto id = req.path_params.at("id");
|
|
res.set_content("User ID: " + id, "text/plain");
|
|
});
|
|
```
|
|
|
|
`:id` is the path parameter. `req.path_params.at("id")` retrieves its value. `/users/42` gives you `"42"`, `/users/alice` gives you `"alice"`.
|
|
|
|
### GET /files/(\d+)
|
|
|
|
```cpp
|
|
svr.Get(R"(/files/(\d+))", [](const auto &req, auto &res) {
|
|
auto id = req.matches[1];
|
|
res.set_content("File ID: " + std::string(id), "text/plain");
|
|
});
|
|
```
|
|
|
|
The regex `(\d+)` matches numeric IDs only. `/files/42` hits this handler, but `/files/abc` returns 404. `req.matches[1]` retrieves the first capture group.
|
|
|
|
## Next Steps
|
|
|
|
You now have the full picture of how a server works. Routing, reading requests, building responses -- that's enough to build a real API server.
|
|
|
|
Next, let's look at serving static files. We'll build a server that delivers HTML and CSS.
|
|
|
|
**Next:** [Static File Server](../04-static-file-server)
|