我正在学习 Axum,我想将日志记录添加到我组装的服务中,但不幸的是我无法让它工作。
我已添加 tower-http 来使用TraceLayer并将其添加到我的应用程序中:
# Cargo.toml
[dependencies]
axum = "0.6.1"
tower-http = { version = "0.3.5", features = ["trace"] }
Run Code Online (Sandbox Code Playgroud)
use tower_http::trace::TraceLayer;
let app = Router::new()
.route("/:name/path", axum::routing::get(handler))
.layer(TraceLayer::new_for_http())
Run Code Online (Sandbox Code Playgroud)
但是当我启动应用程序并向端点发出请求时,没有记录任何内容。是否有我可能错过的配置步骤?
我将 Axum 用于相对简单的 Web API,并希望获得类似于 Go Gin、IIS 日志、Python FastAPI 等的传入请求的日志记录/跟踪输出 - 简单的路径和参数输出。
HTTP 层已添加到路由器:
let app = Router::new()
.route("/hello", get(hello_img))
.layer(TraceLayer::new_for_http());
Run Code Online (Sandbox Code Playgroud)
然而,还有很多额外的不需要的日志记录正在发生,所以我添加了一个过滤器来排除这些日志记录。添加过滤器后:
let filter = filter::Targets::new()
.with_target("tower_http::trace::on_response", Level::TRACE)
.with_target("tower_http::trace::on_request", Level::TRACE)
.with_default(Level::INFO);
Run Code Online (Sandbox Code Playgroud)
并将其添加到订阅者:
let tracing_layer = tracing_subscriber::fmt::layer();
tracing_subscriber::registry()
.with(tracing_layer)
.with(filter)
.init();
Run Code Online (Sandbox Code Playgroud)
详细信息(方法、URI、参数)都消失了。
即使没有指定格式更改,为什么会发生这种情况?如何在控制台中保留请求/响应跟踪,但过滤掉其他不需要的跟踪?
当我尝试使用axum构建应用程序时,我未能将框架与处理程序分开。对于Go,经典的方法是定义一个Interface,实现它并将处理程序注册到框架。通过这种方式,可以很容易地提供一个模拟处理程序来进行测试。然而,我无法让它与 Axum 一起工作。我trait像上面一样定义了 a ,但它不会编译:
use std::net::ToSocketAddrs;
use std::sync::{Arc, Mutex};
use serde_derive::{Serialize, Deserialize};
use serde_json::json;
use axum::{Server, Router, Json};
use axum::extract::Extension;
use axum::routing::BoxRoute;
use axum::handler::get;
#[tokio::main]
async fn main() {
let app = new_router(
Foo{}
);
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
trait Handler {
fn get(&self, get: GetRequest) -> Result<GetResponse, String>;
}
struct Foo {}
impl Handler for Foo {
fn get(&self, req: GetRequest) -> Result<GetResponse, String> {
Ok(GetResponse{ message: "It works.".to_owned()})
}
} …Run Code Online (Sandbox Code Playgroud) 我想实例化一个 struct 实例,然后在 api 路由中调用该实例的方法。这是我想要的示例,但它会导致错误:
use axum::{http::StatusCode, routing::get, Router, Server};
#[derive(Clone)]
struct Api {
name: String
}
impl Api {
async fn hello(&self) -> Result<String, StatusCode> {
Ok(format!("Hello {}!", self.name))
}
}
#[tokio::main]
async fn main() {
let api = Api { name: "Alice".to_owned() };
let app = Router::new()
.route("/hello-user", get(api.hello));
Server::bind(&([127, 0, 0, 1], 3000).into())
.serve(app.into_make_service())
.await
.unwrap();
}
Run Code Online (Sandbox Code Playgroud)
use axum::{http::StatusCode, routing::get, Router, Server};
#[derive(Clone)]
struct Api {
name: String
}
impl Api {
async fn hello(&self) -> Result<String, …Run Code Online (Sandbox Code Playgroud) 例如,当用户访问时http://127.0.0.1:8080/hello,如果查询参数id为1,则返回纯文本响应。如果id是2,给出一个json结构。
概括:
| id(输入) | 状态码 | 内容类型 | 身体 |
|---|---|---|---|
| 1 | 200 | 应用程序/json | {“名称”:“世界”} |
| 2 | 400 | 文本/纯文本 | 没有这样的人 |
struct HelloParam {
id: u16,
}
struct HelloResponse {
name: String,
}
async fn hello_get(Query(params): Query<HelloParam>) -> Response {
// how to implement it?
}
let router= Router::new().route("/hello", get(hello_get));
Run Code Online (Sandbox Code Playgroud) 我想将http://0.0.0.0/foo和路由http://0.0.0.0/foo/到同一个get_foo处理程序。然而,实际上,只有/foo路由和/foo/404。我怀疑我设置/附加的中间件错误:
use axum::http::StatusCode;
use axum::{routing::{get}, Router};
use std::{net::SocketAddr};
use tower_http::normalize_path::NormalizePathLayer;
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/foo", get(get_foo))
.layer(NormalizePathLayer::trim_trailing_slash());
let port_str = std::env::var("PORT").unwrap_or("8000".to_owned());
let port = port_str.parse::<u16>().unwrap();
let addr = SocketAddr::from(([0, 0, 0, 0], port));
println!("listening on http://{}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
async fn get_foo() -> Result<String, StatusCode> {
Ok("Hello from foo.".to_owned())
}
Run Code Online (Sandbox Code Playgroud)
...以及随附的Cargo.toml:
[package]
name = "axum_trailing_slash"
version = "0.1.0"
edition = …Run Code Online (Sandbox Code Playgroud) 我正在构建一个需要注入状态并且还需要提取查询参数的处理程序。
我一开始只提取状态并且有效。其代码如下所示:
#[derive(ValueEnum, Clone, Debug, serde::Deserialze, serde::Serialize)]
pub enum MyParams {
Normal,
Verbose,
}
#[derive(Debug)]
pub struct MyState {
port: u16,
}
pub async fn serve(self) {
let port = self.port;
let app = Router::new()
.route("/path", axum::routing::get(path))
.with_state(Arc::new(self));
let addr = SocketAddr::from(([127, 0, 0, 1], port));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
async fn path(State(middle_ware): State<Arc<MyState>>) -> impl IntoResponse {
let result = middle_ware.process().await;
(StatusCode::OK, Json(result))
}
Run Code Online (Sandbox Code Playgroud)
现在我想提取查询参数,所以我更新了代码如下:
async fn path(State(middle_ware): State<Arc<MyState>>, params: Query<MyParams>) -> impl IntoResponse {
println!("{:?}", params);
let …Run Code Online (Sandbox Code Playgroud) 我有问题,为什么发送时出现此错误request
Error: reqwest::Error {\n kind: Request, \n url: Url { \n scheme: "http", \n cannot_be_a_base: false, \n username: "", \n password: None, \n host: Some(Ipv4(127.0.0.1)), \n port: Some(3000), \n path: "/message", \n query: None, \n fragment: None \n }, \n source: hyper::Error(\n Connect, \n ConnectError(\n "tcp connect error", \n Os { \n code: 10048, \n kind: AddrInUse, \n message: "Only one usage of each socket address (protocol/network address/port) is normally permitted." \n }\n )\n )\n}\nRun Code Online (Sandbox Code Playgroud)\n它以“随机”速率发生,例如 15-45 …
这是我的最小可重现示例:
use axum::{extract::State, Json, Router};
use diesel::pg::Pg;
use diesel_async::{
pooled_connection::bb8::Pool,
AsyncConnection, AsyncPgConnection, RunQueryDsl,
};
fn main() {
// body omitted for brevity
}
pub type AsyncPool = Pool<AsyncPgConnection>;
fn router() -> Router<AsyncPool> {
use axum::routing::*;
Router::new().route("/create", post(create))
}
async fn create(State(db): State<AsyncPool>) -> Result<Json<()>, ()> {
let mut conn = db.get().await.unwrap();
let user = create_user(&mut conn).await?;
Ok(Json(user))
}
// actual return types omitted for brevity
async fn create_user(conn: &mut impl AsyncConnection<Backend = Pg>) -> Result<(), ()> {
// error doesn't …Run Code Online (Sandbox Code Playgroud) 尝试获取完整的请求 URI(方案 + 权限 + 路径)
我找到了讨论:https://github.com/tokio-rs/axum/discussions/1149其中说 Request.uri() 应该有它。所以我尝试了以下方法:
use axum::body::Body;
use axum::http::Request;
use axum::routing::get;
use axum::Router;
async fn handler(req: Request<Body>) -> &'static str {
println!("The request is: {}", req.uri());
println!("The request is: {}", req.uri().scheme_str().unwrap());
println!("The request is: {}", req.uri().authority().unwrap());
"Hello world"
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/test", get(handler));
axum::Server::bind(&"0.0.0.0:8080".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
Run Code Online (Sandbox Code Playgroud)
运行curl -v "http://localhost:8080/test"但我得到:
The request is: /test
thread 'tokio-runtime-worker' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:8:59 …Run Code Online (Sandbox Code Playgroud)