Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cors/pkg.generated.mbti
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub fn append_cors_headers(@mocket.MocketEvent, origin? : String, methods? : Str

pub fn append_cors_preflight_headers(@mocket.MocketEvent, origin? : String, methods? : String, allow_headers? : String, credentials? : Bool, max_age? : Int) -> Unit

pub fn handle_cors(origin? : String, methods? : String, allow_headers? : String, expose_headers? : String, credentials? : Bool, max_age? : Int) -> async (@mocket.MocketEvent, async () -> &@mocket.Responder noraise) -> &@mocket.Responder noraise
pub fn handle_cors(origin? : String, methods? : String, allow_headers? : String, expose_headers? : String, credentials? : Bool, max_age? : Int) -> async (@mocket.MocketEvent, async () -> &@mocket.Responder) -> &@mocket.Responder

pub fn is_preflight_request(@mocket.MocketEvent) -> Bool

Expand Down
11 changes: 9 additions & 2 deletions dispatch.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub async fn dispatch_http(
url : String,
headers : Map[StringView, StringView],
raw_body : Bytes,
) -> HttpResponse noraise {
) -> HttpResponse {
let (params, handler) = match mocket.find_route(http_method, url) {
Some((h, p)) => (p, h)
_ => ({}, handle_not_found())
Expand All @@ -15,7 +15,14 @@ pub async fn dispatch_http(
res: HttpResponse::new(OK),
params,
}
let responder = mocket.execute_middlewares(event, handler)
let responder = mocket.execute_middlewares(event, handler) catch {
err => {
if @async.is_cancellation_error(err) {
raise err
}
mocket.handle_request_error(event, err)
}
}
responder.options(event.res)
let buf = Buffer()
responder.output(buf)
Expand Down
27 changes: 27 additions & 0 deletions error.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,30 @@ pub suberror NetworkError

///|
pub suberror ExecError

///|
// 请求错误处理器:接收事件和未捕获的错误,返回一个响应。
// 用于记录日志、返回自定义错误响应等。
pub type ErrorHandler = (MocketEvent, Error) -> &Responder

///|
// 默认错误处理器:返回 500 Internal Server Error 并附带错误信息。
fn default_error_handler(_event : MocketEvent, err : Error) -> &Responder {
HttpResponse::new(InternalServerError).body(err.to_string())
}

///|
// 将未捕获的请求错误转换为响应,优先使用用户注册的错误处理器。
fn Mocket::handle_request_error(
self : Mocket,
event : MocketEvent,
err : Error,
) -> &Responder {
(self.error_handler)(event, err)
}

///|
// 注册请求错误处理器,用于记录日志或返回自定义错误响应。
pub fn Mocket::on_error(self : Mocket, handler : ErrorHandler) -> Unit {
self.error_handler = handler
}
51 changes: 51 additions & 0 deletions error_wbtest.mbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
///|
suberror TestRequestError {
TestRequestError(String)
} derive(Debug)

///|
async test "dispatch converts unhandled handler errors to 500" {
let app = new()
app.get("/boom", _ => raise TestRequestError("boom"))
let response = dispatch_http(app, "GET", "/boom", {}, b"")
inspect(response.status_code.to_int(), content="500")
let body : String = response.read_body()
assert_true(body.length() > 0)
}

///|
async test "dispatch uses custom error handler" {
let app = new()
let log : Array[String] = []
app.on_error((event, err) => {
log.push("\{event.req.http_method} \{event.req.url}: \{err}")
HttpResponse::new(Custom(599)).body("custom")
})
app.post("/custom", _ => raise TestRequestError("custom"))

let response = dispatch_http(app, "POST", "/custom", {}, b"")
inspect(response.status_code.to_int(), content="599")
let body : String = response.read_body()
@test.assert_eq(body, "custom")
@test.assert_eq(log.length(), 1)
assert_true(log[0].contains("POST /custom"))
assert_true(log[0].contains("custom"))
}

///|
async test "handlers can still catch errors locally" {
let app = new()
app.get("/local", _ => {
let text = try {
raise TestRequestError("local")
} catch {
TestRequestError(msg) => msg
}
text
})

let response = dispatch_http(app, "GET", "/local", {}, b"")
inspect(response.status_code.to_int(), content="200")
let body : String = response.read_body()
@test.assert_eq(body, "local")
}
2 changes: 1 addition & 1 deletion examples/route/pkg.generated.mbti
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
}

// Values
pub fn not_found_middleware() -> async (@mocket.MocketEvent, async () -> &@mocket.Responder noraise) -> &@mocket.Responder noraise
pub fn not_found_middleware() -> async (@mocket.MocketEvent, async () -> &@mocket.Responder) -> &@mocket.Responder

// Errors

Expand Down
2 changes: 1 addition & 1 deletion handler.mbt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
///|
pub type HttpHandler = async (MocketEvent) -> &Responder noraise
pub type HttpHandler = async (MocketEvent) -> &Responder
2 changes: 2 additions & 0 deletions index.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub(all) struct Mocket {
ws_channels : Map[String, Map[String, Unit]]
ws_client_port : Map[String, Int]
max_body_size : Int
mut error_handler : ErrorHandler
}

///|
Expand All @@ -42,6 +43,7 @@ pub fn new(base_path? : String = "", max_body_size? : Int = 1048576) -> Mocket {
ws_channels: {},
ws_client_port: {},
max_body_size,
error_handler: default_error_handler,
}
}

Expand Down
12 changes: 6 additions & 6 deletions middleware.mbt
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
///|
pub type MiddlewareNext = async () -> &Responder noraise
pub type MiddlewareNext = async () -> &Responder

///|
// 中间件类型:接受 HttpEvent 和 next 函数,返回 HttpBody
pub type Middleware = async (MocketEvent, MiddlewareNext) -> &Responder noraise
pub type Middleware = async (MocketEvent, MiddlewareNext) -> &Responder

///|
priv struct MiddlewareTrieEntry {
Expand Down Expand Up @@ -126,7 +126,7 @@ async fn Mocket::execute_middlewares(
self : Mocket,
event : MocketEvent,
final_handler : HttpHandler,
) -> &Responder noraise {
) -> &Responder {
if self.middlewares.is_empty() {
return final_handler(event)
}
Expand All @@ -141,7 +141,7 @@ pub async fn execute_middlewares(
middlewares : Array[(String, Middleware)],
event : MocketEvent,
final_handler : HttpHandler,
) -> &Responder noraise {
) -> &Responder {
if middlewares.is_empty() {
return final_handler(event)
}
Expand All @@ -168,14 +168,14 @@ async fn execute_middleware_chain(
index : Int,
event : MocketEvent,
final_handler : HttpHandler,
) -> &Responder noraise {
) -> &Responder {
if index >= middlewares.length() {
// 所有中间件都执行完毕,调用最终处理器
final_handler(event)
} else {
// 执行当前中间件
let current_middleware = middlewares[index]
let next = async fn() noraise {
let next = async fn() {
execute_middleware_chain(middlewares, index + 1, event, final_handler)
}
current_middleware(event, next)
Expand Down
4 changes: 3 additions & 1 deletion mocket.js.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,9 @@ pub fn listen_ffi(mocket : Mocket, address : String) -> Unit {
}

// 执行中间件链和处理器
let responder = mocket.execute_middlewares(event, handler)
let responder = mocket.execute_middlewares(event, handler) catch {
err => mocket.handle_request_error(event, err)
}
// let boundary = "----------------moonbit-" + port.to_string()
responder.options(event.res)
res.write_head(
Expand Down
9 changes: 8 additions & 1 deletion mocket.native.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,14 @@ async fn handle_http_request(
request_route_path(request.path),
string_headers_to_views(request.headers),
raw_body,
)
) catch {
err => {
if @async.is_cancellation_error(err) {
raise err
}
HttpResponse::new(InternalServerError).body(err.to_string())
}
}
send_native_response(request, conn, response)
}

Expand Down
7 changes: 6 additions & 1 deletion native/mongoose/mongoose.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,12 @@ fn handle_request(
async_run(async fn() noraise {
let response = @mocket.dispatch_http(
mocket, http_method, url, headers, raw_body,
)
) catch {
_ =>
@mocket.HttpResponse::new(@mocket.InternalServerError).body(
"Internal Server Error",
)
}
res.status(response.status_code.to_int())
response.headers.each((key, value) => {
res.set_header(to_cbytes(key), to_cbytes(value))
Expand Down
4 changes: 2 additions & 2 deletions performance_wbtest.mbt
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
///|
#warnings("-unused_async")
async fn benchmark_route_handler(_event : MocketEvent) -> &Responder noraise {
async fn benchmark_route_handler(_event : MocketEvent) -> &Responder {
"ok"
}

///|
async fn benchmark_middleware(
_event : MocketEvent,
next : MiddlewareNext,
) -> &Responder noraise {
) -> &Responder {
next()
}

Expand Down
Loading