Rust in Everything
基于 dioxus 0.7.10

unexpected cfg condition value: server

主线位置:调用主线的起点。#[post] 展开出的服务端一半包在 #[cfg(feature = "server")] 里,inventory::submit! 登记也在里面(见 server function)。这个 cfg 求值时看的是你的 crate 的 feature,不是 dioxus 的,所以 Cargo.toml 写错一处,要么编不过,要么路由表里少了这条路由。

flowchart TB
  macro["#[post] 展开"] --> cfgq{"crate 声明了 server"}
  cfgq -->|没有| e1["编译错误 unexpected cfg"]
  cfgq -->|声明了| onq{"这次编译打开了吗"}
  onq -->|没有| e2["不登记 POST 得 405"]
  onq -->|打开| reg["Registering POST /api/double"]

本页的实验 crate 都是 dioxus = "=0.7.10",M2 Max、macOS 27、rustc 1.99.0,cargo build --offline 直接编(不经过 dx),最后一节用 dx 0.7.9。

现象

一:没声明 server feature

依赖里已经打开了 dioxus 的 server,但 crate 自己没有 [features] 段(pit2-nofeat):

[dependencies]
dioxus = { version = "=0.7.10", features = ["fullstack", "server"] }
use dioxus::prelude::*;

#[post("/api/double")]
async fn double(n: i32) -> Result<i32, ServerFnError> {
  Ok(n * 2)
}

fn main() {
  let _ = double;
}

cargo build 的输出:

error: unexpected `cfg` condition value: `server`
 --> src/main.rs:3:1
  |
3 | #[post("/api/double")]
  | ^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: no expected values for `feature`
  = note: using a cfg inside a attribute macro will use the cfgs from the destination crate and not the ones from the defining crate
  = help: try referring to `post` crate for guidance on how handle this unexpected cfg
  = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html> for more information about checking conditional configuration
  = note: 
          ==========================================================================================
            Using Dioxus Server Functions requires a `server` feature flag in your `Cargo.toml`.
            Please add the following to your `Cargo.toml`:
          
            ```toml
            [features]
            server = ["dioxus/server"]
            ```
          
            To enable better Rust-Analyzer support, you can make `server` a default feature:
            ```toml
            [features]
            default = ["web", "server"]
            web = ["dioxus/web"]
            server = ["dioxus/server"]
            ```
          ==========================================================================================
                  
note: the lint level is defined here
 --> src/main.rs:3:1
  |
3 | #[post("/api/double")]
  | ^^^^^^^^^^^^^^^^^^^^^^
  = note: this error originates in the attribute macro `post` (in Nightly builds, run with -Z macro-backtrace for more info)

error: could not compile `pit2-nofeat` (bin "pit2-nofeat") due to 1 previous error

unexpected_cfgs 平时只是警告,这里是错误,因为宏在生成的函数上加了 #[deny(unexpected_cfgs)]。

二:声明了,但这次编译没打开

pit2-halffeat 加上了 [features] server = ["dioxus/server"],其他不变,main 里 dioxus::launch(app)。依赖的 dioxus/server 一直开着,所以 cargo build(不带 --features)编出来的仍然是一个能启动的服务端。先在可执行文件旁边建一个空的 public/(原因见第三种),IP=127.0.0.1 PORT=18820 启动,再 curl -X POST /api/double -d '{"n":21}':

== cargo build (no --features)
2026-10-28T12:35:28.450491Z  WARN dioxus_server::config: No index.html found in public directory, using default index.html
HTTP/1.1 405 Method Not Allowed
allow: GET,HEAD
content-length: 0


== cargo build --features server
2026-10-28T12:35:33.407069Z  WARN dioxus_server::config: No index.html found in public directory, using default index.html
2026-10-28T12:35:33.412828Z  INFO dioxus_server::server: Registering: POST /api/double
HTTP/1.1 200 OK
content-type: application/json
content-length: 2

42

没打开时编译、启动都没有任何提示,只是少了那行 Registering。请求落进渲染页面的 fallback,fallback 只接 GET,所以是 405,Allow: GET,HEAD。浏览器里的 server function 调用得到的就是这个 405。

三:cargo 编的服务端旁边没有 public/

同一个二进制,删掉 public/ 再启动:

2026-10-28T12:35:16.639321Z  WARN dioxus_server::config: No index.html found in public directory, using default index.html

thread 'main' (2208197) panicked at ~/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/dioxus-server-0.7.10/src/server.rs:409:29:
Couldn't read public directory at "~/exp-dioxus/target-pit2/debug/public": No such file or directory (os error 2)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

缺 index.html 只是一条警告(换成不带 wasm 的最小模板),缺整个目录是 panic。

四:dx 和 dioxus 版本不一致

用 dx 0.7.9 构建依赖 dioxus 0.7.10 的应用,日志开头:

  0.00s  INFO Building project...
  0.54s ERROR 🚫dx and dioxus versions are incompatible!
                  • dx version: 0.7.9
                  • dioxus versions: [0.7.10]
  2.09s  INFO Compiled [  1/305]: cfg_if

之后照常编完(Client build completed successfully!),本卷所有实验都是这样构建的。这一行只是提示,不中止构建。

出处

  • 编译错误:route_impl_with_route 生成服务端那一半时,在函数上加 #[deny(unexpected_cfgs, reason = "...")],reason 就是上面那一大段说明。宏展开出的 #[cfg(feature = "server")] 在用户 crate 里求值,Cargo 只把 crate 自己声明过的 feature 名告诉 rustc 的 check-cfg,没声明就是「no expected values for feature」。
  • 405:服务端一半和 inventory::submit! 都在这个 cfg 后面。feature 没开,这个函数在二进制里只剩客户端一半,register_server_functions 遍历 inventory 时根本看不到它。fallback 是 get(render_handler)(一次请求的渲染)。
  • public/:public_path 没有 DIOXUS_PUBLIC_PATH 时取「可执行文件所在目录/public」,serve_dir_cached 对它 read_dir,失败就 panic!("Couldn't read public directory at ...")。dx build 会把 public/ 放在 server 旁边,cargo 不会。
  • 版本:dx 的 check_dioxus_version_against_cli 在工作区里所有 dioxus 版本的最小值大于 dx、最大值小于 dx、或预发布标记不同时 tracing::error!,然后返回,构建继续。补丁号差一位也会触发。

修法

Cargo.toml 照报错里的说明写,客户端和服务端各一个 feature,dioxus 本身不直接开 server / web:

[dependencies]
dioxus = { version = "=0.7.10", features = ["router", "fullstack"] }

[features]
default = []
web = ["dioxus/web"]
server = ["dioxus/server"]

dx build --platform web --fullstack 编客户端时打开 web、编服务端时打开 server,本卷的 pit2-fs 就是这份配置,服务端日志里每个函数都有一行 Registering。只用 cargo 编服务端时要 --features server,并且把 dx 产出的 public/ 放到可执行文件旁边,或者用 DIOXUS_PUBLIC_PATH 指过去。

只在服务端用到的依赖(数据库、tokio 的额外 feature)挂在 server 下面做成可选依赖,函数体里用到它们的代码放在 #[cfg(feature = "server")] 里,否则客户端那次编译会把它们编进 wasm 或者编不过。

版本那一行:把 dx 和 dioxus 对齐到同一个补丁版本,或者确认两者之间只差 CLI 的改动。本卷的情况是后者:0.7.9 到 0.7.10 只改了 packages/cli/(见卷首)。

怎么确认没中招

  • 服务端启动日志里,每个 server function 一行 INFO dioxus_server::server: Registering: <方法> <路径>。数一下和代码里的 #[post] / #[get] / #[server] 个数对不对得上。
  • 部署后对每个函数发一次正确方法的请求:405 带 Allow: GET,HEAD 就是没登记(登记了的路由方法不对时,Allow 是它自己的方法,比如 POST)。
  • 容器里跑 ls "$(dirname "$(readlink -f ./server)")/public",或者在启动脚本里设 DIOXUS_PUBLIC_PATH。
  • CI 里把 dx --version 和 cargo tree -i dioxus --depth 0 的输出比一下,不一致就失败,不靠那一行 ERROR。

小结

  • #[post] 生成的 cfg(feature = "server") 看的是你的 crate;没声明 server 是 #[deny(unexpected_cfgs)] 的编译错误,报错里附了正确的 Cargo.toml。
  • 声明了但这次编译没打开:服务端照常启动,函数不登记,POST 得 405,没有任何警告。认准启动日志里的 Registering 行。
  • cargo 直接编的服务端要自己准备 public/,否则 server.rs:409 panic。
  • dx 与 dioxus 版本不一致只打一行 ERROR,构建继续。

💬 关联讨论

发起讨论
加载中...