Before a fetch
is sent, the browser will send a request method OPTIONS
to confirm that the API will accept the request from a script with a particular origin.
Chrome shows that the my Axum server is rejecting my client's request with 405. My router looks something like this:
let app = Router::new()
.layer(TraceLayer::new_for_http())
.layer(CorsLayer::permissive())
.route("/api", post(server));
Router::layer says All requests to the router will be processed by the layer’s corresponding middleware. but I'm not sure its doing its job.
The .layer()
function is a builder so returns a new Router
, with the inner routers wrapped. The route /api
will be tested first and be rejected 405 because only request method POST
is supported - not OPTIONS
.
In summary, you need your CorsLayer
"outside" your route so it can answer OPTIONS
requests.
Note the example in the documentation:
// All requests to `first_handler` and `second_handler` will be sent through
// `ConcurrencyLimit`
let app = Router::new().route("/", get(first_handler))
.route("/foo", get(second_handler))
.layer(ConcurrencyLimitLayer::new(64))
// Request to `GET /bar` will go directly to `third_handler` and
// wont be sent through `ConcurrencyLimit`
.route("/bar", get(third_handler));
By the way, your TraceLayer
is not tracing your API calls for the same reason!
Try this, and you'll see the OPTIONS
request logged, and the POST
should hit your server
:
let app = Router::new()
.route("/api", post(server))
.layer(CorsLayer::permissive())
.layer(TraceLayer::new_for_http());