#axum
154 messages · Page 1 of 1 (latest)
I noticed xD oh well, wasn't too hard to migrate
I mean you can use the main branch if you are not using any other crates in the axum subspace
but if you do, and they break because of these breaking changes, it would be unwise to approach their developers about it
I actually forked like 4 crates and patched them xD
Idk what's wrong with with me this weekend owo
You seem to be suffering from a mild case of eckdeetation for starters
But in general the reason it’s recommended to use a stable release is that main is bound to be inconsistent and may have further changes leading up to the next release, meaning potentially wasted time when patching dependent crates
I'm not sure what eckdeetation is xD
But the main purposes was so I could add the dylib label to the crate, so I could make my stuff compile faster
It’s a neurological issue
The symptom is there inability to avoid sticking ‘xD’ everywhere even when no one is talking about Olympus’s xD Picture Card flash memory format
Lmao
Makes the sufferer of the disease appear unprofessional and generally places them on the ‘most likely under 14’ scale
Very good owo easier to surprise people
Anyone has any idea why are symbols become undefined when I make axum or other crates as dylib?
Example
= note: wild: error: Undefined symbol aws_lc_0_37_1_ECDSA_SIG_get0_r, referenced by
target/debug/deps/libjsonwebtoken-3d9c9373e20257c9.rlib @ jsonwebtoken-3d9c9373e20257c9.jsonwebtoken.bab6b9b0e81178a1-cgu.040.rcgu.o
Maybe I'll try different linker or something :/
That looks like TLS
Could you advise on how to build an architecture with Axum? Or maybe there are some resources I could read?
I built a website with following architecture:
read from fs -> compile handlebars pages store in a map -> repeat every few mins -> router fallback to handlebars pages -> specific routes for custom stuff
serve over nginx reverse proxy and serve static files over fs from nginx
if anybody is interested I've just released a new version of axum-oidc-client with two-tier authentication cache. Redis is no longer mandatory
can axum work with different async runtimes besides tokio? like with https://github.com/compio-rs/compio ?
If you were a programmer you could probably check the source in the repository
you could also ask ‘can tower work with different async runtimes’ just as well
it can, but not with the axum::serve function
if you see any programmers ask them to check xD
ah 🤔 thx
The official answer is
axum is designed to work with tokio and hyper. Runtime and transport layer independence is not a goal, at least for the time being.
You could probably run it the way you’d do for wasm
there also probably isn't a great reason to run axum with compio
wanted io_uring
tokio has experimental uring support for fs ops, you have to enable a feature and --cfg tokio_unstable (I think)
and if you aren't doing fs ops, kqueue/epoll/(mio AFD) is very very likely sufficient
oh interesting 🤔 thx
hyper is runtime agnostic, but you'll have to implement a couple traits
writing your own accept loop is kinda annoying but possible
Is there (or if not, can someone recommend please) a recommended reading order for these examples?
I don't think they are meant to be read in any specific order, or have anything to do with each other to begin with
it's just various... examples of how to do things with axum
Are there any examples around of uploading a file in multiple parts to axum and reassembling it?
That's a multipart upload, not uploading a file in multiple parts.
Uploading in multiple parts is probably an out of scope thing, like, you just need to combine stuff and how is axum supposed to be involved in that?
It’s not a feature in HTTP
I’d look for generic examples, not axum specific
I have.
Didn't find anything useful.
It’s not an easy problem if you look at it from edge cases/security points of view
Precisely why I was looking for examples.
What I saw in the wild in the 90s’ web was using existing libraries and tools for splitting files
Because that was important when you dealt in floppies and Zmodem
I've seen mentioned quite often that uploading multiple chunks in parallel can be faster than a single serial upload.
i think S3 supports this. I’d take a look at some s3 implementations if I were you
though s3 is also quite complicated, so it might be kind of difficult to understand
I think that's right, S3 supports it using Content-Range maybe?
That’s not guaranteed and often is a superstition unless your ISP is doing per-connection throttling
Or unless you’re uploading to multiple different endpoints at once and they have bandwidth issues
You generally should avoid this approach on ADSL and DOCSIS links (and on mobile data)
Due to bin sharing
Parallel uploads mean higher PPS which increases latency on asymmetrical links with bin sharing
I had suspected it wasn't obviously better, given how rare it seems. I'm presently stuck in a situation where I need to implement this to show someone else the results from trying it.
one reason it might be better to chunk in some way is if you have a very unreliable connection
right, because it limits what you need to retry
Yeah like dial up terminal with Zmodem or worse, as I mentioned above ahaha
I had so much ‘fun’ in springs at home when our NTT ISDN line got wet
Hi everyone, I wrote a tool(https://github.com/xidl/xidl) that can generate axum scaffolding from IDL, is anyone interested?
What it does is generate some scaffolding from IDL. Users can generate routes by implementing the corresponding trait. In addition, it also supports generating openapi and jsonrpc. If anyone is interested, welcome to star!
Here is the actual effect:
use xidlc_examples::hello_world::HelloWorld;
use xidlc_examples::hello_world::HelloWorldSayHelloRequest;
use xidlc_examples::hello_world::HelloWorldServer;
struct HelloWorldImpl;
#[async_trait::async_trait]
impl HelloWorld for HelloWorldImpl {
async fn sayHello(
&self,
req: xidl_rust_axum::Request<HelloWorldSayHelloRequest>,
) -> Result<(), xidl_rust_axum::Error> {
let HelloWorldSayHelloRequest { name } = req.data;
println!("Hello, {}!", name);
Ok(())
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = "127.0.0.1:3000";
println!("axum hello_world server listening on {addr}");
xidl_rust_axum::Server::builder()
.with_service(HelloWorldServer::new(HelloWorldImpl))
.serve(addr)
.await?;
Ok(())
}
Here is a simple example:
#progma xidlc package Smart City Public APIs
#progma xidlc version v2.0.0
interface SmartCityHttpApi {
@get(path = "/v1/stops/{stop_id}{?line,lang}")
string get_stop_eta(
string stop_id,
string line,
string locale,
out uint32 eta_seconds,
out string destination
);
@post(path = "/v1/parking/lots/{lot_id}/reserve")
string reserve_lot(
@path("lot_id") string lot_id,
string plate_number,
uint32 minutes,
out string reservation_state,
out string expires_at
);
readonly attribute string api_version;
attribute boolean maintenance_mode;
};
You can play it with https://xidl.github.io/xidl or install by cargo install xidlc
Wait a second, what are you doing here xD
Hi, @everyone
I am building Rust Axum backend for Solana trading platform.
I used Postgresql as database but orm implementation was the difficult.
I used sea-orm but its a little confusing.
Who has expertise in sea-orm integration especially migration part?
This is entirely unrelated to axum so you are probably better off finding the SeaORM community resources
I find that a lot of people who go for SeaORM do it based on previous ‘experience’ with stuff like Django and often generally have trouble with documentation and lower level concepts (usually they are also crypt0-curr3ncy shills)
Thanks for your recommendation.
Yes, its true, I am crypto fan
Hey all! I've created a very small PR extending Axum's documentation. If any of you could review it and give me feedback, that would be very helpful and welcome. Here is a link to the PR: https://github.com/tokio-rs/axum/pull/3683
Thanks in advance!
hi, I am implementing a container registry using axum, and the specification needs to support slashes in the repository name (e.g. user/repo)
so I tried to do .route("/{*repository}/blobs/{digest}", _)but it panicked with the error Catch-all parameters are only allowed at the end of a route.
how should I go about doing this
"/{user}/{repo}/blobs/{digest}"?
my main concern is that the specification allows for practically unlimited nesting ("/a/b/c/d/blobs/{digest}")
then you'll just have to use a trailing {*path} and parse it yourself ¯_(ツ)_/¯
🥲
bit offtopic,
I want to have constants for my routes like:
pub const ADMIN: &str = "/admin";
pub const ADMIN_DASHBOARD: &str = "/admin/dashboard";
pub const ADMIN_PASSWORD: &str = "/admin/password";
but this is making composing things hard.
concat! macro doesn't work either.
ps: i am doing this and not typed path from axum-extras because:
- I want to see at one glance all my routes
- typed path dont have the ability to chain different HTTP method like:
.route(LOGIN, get(login_form).post(login)) - constants can be used in other context like
Redirect
I've been wanting something similar and have settled with using typed paths for now. At least for case 3, you can do Redirect::to(&MyTypedPath{id: 1}.to_string()). But it's very clunky still and doesn't use rust's type system. Perhaps a TypedRedirect::to is needed?
Has anyone ever brought up the idea of adding an enum-based router to axum? I see this for example, but it uses a macro: https://crates.io/crates/axum-routes
What do you guys think about this WithRejection extractor that maps the rejection type without the use of PhandomData like the one in axum-extra
trait Foo<R> {
type Inner;
fn into_inner(self) -> Self::Inner;
}
impl<R, T> Foo<R> for T {
type Inner = T;
fn into_inner(self) -> Self::Inner {
self
}
}
struct WithRejection<I: Foo<R>, R>(I::Inner);
impl<S, I, R> FromRequestParts<S> for WithRejection<I, R>
where
I: Foo<R> + FromRequestParts<S>,
R: IntoResponse + From<I::Rejection>,
S: Sync,
{
type Rejection = R;
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
I::from_request_parts(parts, state)
.await
.map_err(Into::into)
.map(I::into_inner)
.map(WithRejection)
}
}
impl<S, I, R> FromRequest<S> for WithRejection<I, R>
where
I: Foo<R> + FromRequest<S>,
R: IntoResponse + From<I::Rejection>,
S: Sync,
{
type Rejection = R;
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
I::from_request(req, state)
.await
.map_err(Into::into)
.map(I::into_inner)
.map(WithRejection)
}
}
async fn fetch(WithRejection(Json(stuff)): WithRejection<Json<Stuff>, Error>) {}
I wrote a very interesting lsp. 🙂
you can install it by this id: cathaysia.vscode-idl-language
and here is a simple testcase:
interface HttpServer {
/// host name
attribute string host;
/// port number
readonly attribute uint16 port;
// Get user info
@get(path = "/v1/user/{id}")
UserInfo get_user_info(
uint64 id
);
};
what does that have to do with lsp? or axum?
HttpServer can be used to generate axum-based code (client and server).
I just wanted to show you this code lens, I think it very useful.
i dont like how with typed route i can't use the fluent api of untyped. I like how it has route information at type level tho.
an ideal solution imo would aim to fulfill these:
- fluent api
- type safe routes that can be used in any adjacent context(like redirect)
- route tree (glace view)
the macro would have been cool if it generated the names from enum fields.
Hello, I'd like to inform you that if you are interested I've just released the new version of https://crates.io/crates/axum-oidc-client that supports SQL based 2nd level cache (postgres, mysql and sqlite) and OIDC autodiscovery configuration. I've also written an exaustive set of examples. Any feedback is appreciated
Hey folks, new to Rust and Axum
I'm looking for the idiomatic way to transform an axum::http::Uri. For instance, I'd like to do something like uri.add_query_param("success", true) (or similar). This would update an existing Uri by adding things in it.
I've been searching the docs, but it does not seem like there is something like that in the library. Any pointers? (I wrote a helper function for now, but I find it annoyingly unsafe for something that I think should be safe)
My website code looks something like this below. The log middleware records a lot of messages for unmatched routes, people trying to hack my site hitting php urls, etc. I thought when I called layer(...) that the layer applied to everything before that point in the router. Does it also apply to the default fallback route? How would I make it so it did not log the fallback handler? Actually, I probably want to log it but to a different place.
let app = Router::new()
.route("/", get(homepage))
.route("/some-other-page", ...)
.nest("/app-apis", app_apis_router(pool.clone()))
.layer(middleware::from_fn_with_state(pool.clone(), log_to_db_mw))
.into_make_service_with_connect_info::<SocketAddr>();
Router::route_layer() instead of layer
layer runs after routing, but it runs regardless of whether there was actually a match or not
route_layer only runs if there was a match
Weird I'm using the macro and still tells me to use it. Not sure what is wrong with my handler function here:
[... router ...].fallback(my_fallback_handler)
#[axum::debug_handler]
async fn my_fallback_handler(State(pool): State<PgPool>, req: Request) -> Response { ...
Error:
error[E0277]: the trait bound `fn(State<Pool<...>>, ...) -> ... {my_fallback_handler}: Handler<_, ()>` is not satisfied
--> web-site/src/lib.rs:153:19
|
153 | .fallback(my_fallback_handler)
| -------- ^^^^^^^^^^^^^^^^^^^ the trait `Handler<_, ()>` is not implemented for fn item `fn(State<Pool<Postgres>>, Request<Body>) -> ... {my_fallback_handler}`
| |
| required by a bound introduced by this call
|
= note: Consider using `#[axum::debug_handler]` to improve the error message
show the [... router ...] part of the code
there's nothing wrong with the handler, but with how you register it
you either forgot to call .with_state(…) or called it before .fallback(…)
let app = Router::new()
.route("/", get(homepage))
.route("/workout-timer", get(|| resource!("../resources/workout-timer.html"))) .nest("/app-apis", app_apis_router(pool.clone()))
.route("/version", get(version))
.nest_service("/styles", ServeDir::new(format!("{}/styles", static_dir)))
.nest_service("/images", ServeDir::new(format!("{}/images", static_dir)))
.route_layer(middleware::from_fn_with_state(pool.clone(), log_to_db_middleware))
.fallback(my_fallback_handler)
I tried adding a with_state(pool.clone()), in the chain, but it's not compiling...
with the same error or a different error
.route_layer(middleware::from_fn_with_state(pool.clone(), log_to_db_middleware))
.with_state(pool.clone())
.fallback(my_fallback_handler)
Error:
error[E0308]: mismatched types
--> web-site/src/lib.rs:154:21
|
154 | .with_state(pool.clone())
| ---------- ^^^^^^^^^^^^ expected `()`, found `Pool<Postgres>`
| |
| arguments to this method are incorrect
|
= note: expected unit type `()`
found struct `Pool<Postgres>`
If put the with_state after fallback, it's the same error as before.
put it after and remove #[axum::debug_handler]
error[E0277]: the trait bound `fn(State<Pool<...>>, ...) -> ... {my_fallback_handler}: Handler<_, ()>` is not satisfied
--> web-site/src/lib.rs:154:19
|
154 | .fallback(my_fallback_handler)
| -------- ^^^^^^^^^^^^^^^^^^^ the trait `Handler<_, ()>` is not implemented for fn item `fn(State<Pool<Postgres>>, Request<Body>) -> ... {my_fallback_handler}`
| |
| required by a bound introduced by this call
|
= note: Consider using `#[axum::debug_handler]` to improve the error message
pub async fn save_response(
repo: &Repository,
idempotency_key: &IdempotencyKey,
user_id: &str,
response: Response,
) -> anyhow::Result<()> {
let status_code = response.status();
let headers = {
let mut h = Vec::with_capacity(response.headers().len());
for (key, value) in response.headers().iter() {
h.push(HeaderPair {
key: key.as_str().to_owned(),
value: value.as_bytes().to_owned(),
});
}
h
};
repo.save_response(
user_id,
idempotency_key,
status_code.as_u16(),
headers,
to_bytes(response.into_body(), usize::MAX).await?.to_vec(),
)
.await?;
Ok(())
}
I want to refactor this function to return response back to caller. but the to_bytes function consumes response.
the repo.save_response expects Vec<u8> (bytes) as the last arg.
how do i get around it?
i found a relevant method, into_parts that solved it.
how could i make it os every route but one (/login) requires an token (eg via authentication header)?
Add a middleware on the router struct, then add a the token endpoint after.
hello, I've just released https://crates.io/crates/axum-oidc-client v.0.4.1 . This version adds jwt layer and many fixes and improvements. The examples have been updated as well
Axum plus tower_http middleware question: (please let me know if there is a better place I should ask this)
When applying both CorsLayer and TraceLayer to a router, if I apply through ServiceBuilder, they must be applied in a particular order (Trace wrapping Cors) in order for it to compile.
However, if I use successive router.layer calls, the ordering does not matter. Does this mean the ordering doesn't actually matter, or do I need to apply them in the order enforced by the ServiceBuilder?
Example: the router.layer(ServiceBuilder...) call fails to compile with:
the trait bound `ResponseBody<Body, NeverClassifyEos<...>>: Default` is not satisfied
required for `Cors<Trace<Route, SharedClassifier<ServerErrorsAsFailures>>>` to implement `tower::Service<axum::http::Request<axum::body::Body>>`
use axum::{Router, routing::get};
use tower::ServiceBuilder;
use tower_http::cors::CorsLayer;
use tower_http::trace::TraceLayer;
#[tokio::main]
async fn main() {
let mut router = Router::new().route("/", get(|| async { "Hello, World!" }));
router = router.layer( // this fails with the compilation error above
ServiceBuilder::new()
.layer(CorsLayer::new())
.layer(TraceLayer::new_for_http()),
);
router = router // this is fine, despite applying the same middleware order
.layer(TraceLayer::new_for_http())
.layer(CorsLayer::new());
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, router).await.unwrap();
}
this is with axum 0.8.8, tower 0.5.3, tower-http 0.6.8
Cors requires that your response body type implements Default (so it can generate empty responses), but TraceLayer's Body type doesn't. calling Router::layer() multiple times works because the body is "normalized" to axum's body type, which does implement Default
the order matters in both cases wrt. which middleware will run first
you can use ServiceBuilder and retain the order if you map the body type to something that implements Default, or just use Router::layer()
got it - thank you!
has anyone here looked into exposing fallible router merging? basically just exposing the errors that the private merge methods return and not converting it to a panic in Router::merge
it's not clear to me if it's just that simple or if there's some special reason why it hasn't already been exposed as a fallible try_merge method or something yet
Is there anything to do when getting the "too many argument" warning but slapping an allow attribute to it?
I know that I could do something like this:
State(AppState {
database,
localization,
config,
..
}): State<AppState>,
but it would clone the other AppState fields unnecessarily.
I don't see other options.
if you have an appstate struct of structs and your state is an Arc, nothing inside it will be cloned
Hmm right, I think that everything is arc inside
then only Arcs will be cloned (unless you Arc the outer struct, then only that Arc will be cloned)
cloning Arc is very cheap since it is just a reference counter
I'll pass the main state directly then, thanks. A few more arc clones is negligible
@blazing nova I guess it's not possible to "merge" the extensions together, ie to have them all at once as a tuple?
They're not defined in the same middleware, so I cannot do it myself by hand.
you could write your own extractor that does that
Oh, I see. Thanks for your answer, it's something I'll consider.
Does anyone happen to have an example or sample project which implements mTLS with x509?
I've tried doing it myself, but i can never get it to work. I either run into lifetime issues or traits requirements not being met.
I feel like this question is more for #1399401580859752468 or such? axum does not know anything about TLS
Perhaps you are right, i wasn't sure where to ask since it seems to involve, axum, tower and hyper at the same time.
it does not involve the first two in any way
tbh it does not really involve hyper either
it's a rustls thing, but hyper does deal with it
I see, thanks for the clarification. My knowledge about all of this is very fleeting at best.
we also have a mTLS example for rama: https://github.com/plabayo/rama/blob/main/examples/mtls_tunnel_and_service.rs
Hey! I built and open-source api-error at work, it is a proc macro to define HTTP errors in a thiserror way and it can automatically implement IntoResponse for it and can be used like this.
#[derive(Debug, thiserror::Error, ApiError)]
enum AppError {
// Unnamed fields with positional formatting
#[error("Database error: {0}")]
#[api_error(status_code = 500, message = "Database operation failed: {0}")]
Database(String),
// Named fields with named formatting
#[error("Validation failed on {field}")]
#[api_error(status_code = 422, message = "Field `{field}` has invalid value")]
Validation { field: String, value: String },
}
Currently the way the error is serialized through the IntoResponse impl is quite opinionated, there is only a JSON message field and the status is set on the headers. However I guess a everyone will have it's own kind of implementation. Therefore I'm quite curious about how you usually serialize your HTTP errors? Based on this we might think on a way to provide custom IntoResponse impl through a static maybe.
I ignore if it's well-known: if a state is built for the whole webserver lifetime and won't change, you can leak it. I've used it without issue, and I love it:
#[derive(Debug, Clone)]
pub struct AppState {
pub database: Database,
pub cache: Cache,
pub localization: Localization,
pub config: &'static AppConfig,
pub webauthn: &'static Webauthn,
}
#[derive(Clone, Copy)]
pub struct Localization {
pub loader: &'static ArcLoader,
language_options: &'static [(Language, &'static [LanguageOption])],
}
It's the easiest for memory management.
Hey, I submitted a new project in the economic system, but there has been no review
What happened to axum (server) struct/enum?
if I recall correctly, axum::Server used to be just a re-export of hyper::Server. since hyper's 1.0 release that type is gone though, so axum got rid of it too. simple as that
Yeah i saw that, what are the replacements if you know?
the connection builders in https://docs.rs/hyper-util/0.1.20/hyper_util/server/conn/auto/
or, for axum, axum::serve
if you search for axum::Server, you'll find the relevant changelog entry
and the PR it links to
Nice, a changelog
how to generate opaque tokens with axum?
like what cryptographic method should you use ideally
axum does not handle cryptography, it is up to you
you might find useful information on OWASP and elsewhere
Hi all I'm new here. I'm evaluating to contribute to axum, so right now just studing the code. But, just a questione, how do you track the v0.9 issues and roadmap? Is there a specific page or label or something else?
questions about developing axum itself are probably better asked in #axum-internals, that's where maintainers tend to look more often
this channel is more for usage questions
or an issue/discussion on github, of course
<@&1207366540299735151> ^ :(
in other channels too
🔨
You forgot to delete all messages while banning 🫡
dang, i set it to do previous 24 hours
weird
might just be a discord moment
yeah, i started deleting manually but it seems to have finally kicked in
yay
hello, i run into a strange implementation:
how do i use either::Either as a Path extractor, i want to extract something like this:
pub async fn get_food(Path(usernameOrId): Path<Either<String, Uuid>>) -> Result<impl IntoResponse, Error>
how?
I'd rather use something like this:
#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum UuidOrString {
Uuid(uuid::Uuid), // Has to be before String one, otherwise it would always deserialize to the string variant
Str(String),
}
pub async fn get_food(
Path(param): Path<UuidOrString>,
) -> Result<impl IntoResponse, Error> {
println!("param: {param:?}");
Ok(StatusCode::NO_CONTENT)
}
If based on your param name usernameOrId, the meanings are different, then this of course completely breaks down as soon as as you encounter someone making a username that deserializes into a UUID and you really should have separate routes instead.