Files
v-hono/error_handler.v

228 lines
6.7 KiB
V

module hono
import net.http
import x.json2
// 错误类型枚举
pub enum ErrorType {
bad_request = 400
unauthorized = 401
forbidden = 403
not_found = 404
method_not_allowed = 405
conflict = 409
payload_too_large = 413
unsupported_media_type = 415
unprocessable_entity = 422
internal_server_error = 500
not_implemented = 501
bad_gateway = 502
service_unavailable = 503
}
// 标准化错误响应结构
pub struct ErrorResponse {
pub:
error string
message string
code int
timestamp string
path string
details map[string]string
}
// 错误处理器接口
pub interface IErrorHandler {
handle_error(mut c Context, error_type ErrorType, message string, details map[string]string) http.Response
}
// 默认错误处理器
pub struct DefaultErrorHandler {}
// 实现错误处理器接口
pub fn (eh DefaultErrorHandler) handle_error(mut c Context, error_type ErrorType, message string, details map[string]string) http.Response {
error_response := ErrorResponse{
error: get_error_name(error_type)
message: message
code: int(error_type)
timestamp: get_current_timestamp()
path: c.path
details: details
}
c.status(int(error_type))
return c.json(json2.encode[ErrorResponse](error_response))
}
// 获取错误名称
fn get_error_name(error_type ErrorType) string {
return match error_type {
.bad_request { 'Bad Request' }
.unauthorized { 'Unauthorized' }
.forbidden { 'Forbidden' }
.not_found { 'Not Found' }
.method_not_allowed { 'Method Not Allowed' }
.conflict { 'Conflict' }
.payload_too_large { 'Payload Too Large' }
.unsupported_media_type { 'Unsupported Media Type' }
.unprocessable_entity { 'Unprocessable Entity' }
.internal_server_error { 'Internal Server Error' }
.not_implemented { 'Not Implemented' }
.bad_gateway { 'Bad Gateway' }
.service_unavailable { 'Service Unavailable' }
}
}
// 获取当前时间戳
fn get_current_timestamp() string {
// 简化的时间戳实现
return '2025-12-26T00:00:00Z'
}
// Context 扩展方法 - 便捷的错误处理
pub fn (mut c Context) error_response(error_type ErrorType, message string) http.Response {
return c.error_response_with_details(error_type, message, map[string]string{})
}
pub fn (mut c Context) error_response_with_details(error_type ErrorType, message string, details map[string]string) http.Response {
handler := DefaultErrorHandler{}
return handler.handle_error(mut c, error_type, message, details)
}
// 常用错误处理快捷方法
pub fn (mut c Context) bad_request(message string) http.Response {
return c.error_response(.bad_request, message)
}
pub fn (mut c Context) unauthorized(message string) http.Response {
return c.error_response(.unauthorized, message)
}
pub fn (mut c Context) forbidden(message string) http.Response {
return c.error_response(.forbidden, message)
}
pub fn (mut c Context) not_found(message string) http.Response {
return c.error_response(.not_found, message)
}
pub fn (mut c Context) internal_error(message string) http.Response {
return c.error_response(.internal_server_error, message)
}
pub fn (mut c Context) validation_error(message string, field_errors map[string]string) http.Response {
return c.error_response_with_details(.unprocessable_entity, message, field_errors)
}
// 参数验证错误处理
pub fn (mut c Context) missing_parameter(param_name string) http.Response {
return c.bad_request('Missing required parameter: ${param_name}')
}
pub fn (mut c Context) invalid_parameter(param_name string, reason string) http.Response {
details := {
'parameter': param_name
'reason': reason
}
return c.error_response_with_details(.bad_request, 'Invalid parameter: ${param_name}', details)
}
// 资源相关错误处理
pub fn (mut c Context) resource_not_found(resource_type string, resource_id string) http.Response {
details := {
'resource_type': resource_type
'resource_id': resource_id
}
return c.error_response_with_details(.not_found, '${resource_type} not found', details)
}
pub fn (mut c Context) resource_conflict(resource_type string, reason string) http.Response {
details := {
'resource_type': resource_type
'reason': reason
}
return c.error_response_with_details(.conflict, 'Resource conflict: ${reason}', details)
}
// 文件操作错误处理
pub fn (mut c Context) file_operation_error(operation string, filename string, reason string) http.Response {
details := {
'operation': operation
'filename': filename
'reason': reason
}
return c.error_response_with_details(.internal_server_error, 'File operation failed: ${operation}', details)
}
// 数据库操作错误处理
pub fn (mut c Context) database_error(operation string, reason string) http.Response {
details := {
'operation': operation
'reason': reason
}
return c.error_response_with_details(.internal_server_error, 'Database operation failed', details)
}
// 验证结果处理
pub fn handle_validation_result[T](mut c Context, result !T, param_name string) !T {
return result or {
// 这里不能直接返回 http.Response,需要在调用处处理
return error('Validation failed for ${param_name}: ${err}')
}
}
// 全局错误处理中间件
pub fn error_handling_middleware() fn (mut Context, fn (mut Context) http.Response) http.Response {
return fn (mut c Context, next fn (mut Context) http.Response) http.Response {
// 捕获 panic 并转换为错误响应
// V语言目前不支持 try-catch,这里提供结构化的错误处理框架
response := next(mut c)
// 如果响应状态码是错误状态,确保响应格式一致
if c.status_code >= 400 {
// 如果响应体不是标准错误格式,转换为标准格式
if !response.body.contains('"error"') {
error_type := match c.status_code {
400 { ErrorType.bad_request }
401 { ErrorType.unauthorized }
403 { ErrorType.forbidden }
404 { ErrorType.not_found }
409 { ErrorType.conflict }
422 { ErrorType.unprocessable_entity }
500 { ErrorType.internal_server_error }
else { ErrorType.internal_server_error }
}
return c.error_response(error_type, response.body)
}
}
return response
}
}
// 错误日志记录
pub struct ErrorLogger {
mut:
enabled bool = true
}
pub fn (mut logger ErrorLogger) log_error(error_response ErrorResponse, request_info string) {
if !logger.enabled {
return
}
println('[ERROR] ${error_response.timestamp} - ${error_response.code} ${error_response.error}')
println(' Path: ${error_response.path}')
println(' Message: ${error_response.message}')
if error_response.details.len > 0 {
println(' Details: ${error_response.details}')
}
println(' Request: ${request_info}')
}
// 创建错误日志实例
pub fn new_error_logger() ErrorLogger {
return ErrorLogger{
enabled: true
}
}