Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(service): ✨ http错误提示 #184

Merged
merged 4 commits into from
Jan 15, 2025
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
39 changes: 38 additions & 1 deletion src/common/exception.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,43 @@
export enum ErrorType {
Network = 'NETWORK_ERROR',
Authentication = 'AUTH_ERROR',
Validation = 'VALIDATION_ERROR',
Server = 'SERVER_ERROR',
Client = 'CLIENT_ERROR',
Unknown = 'UNKNOWN_ERROR'

Check warning on line 7 in src/common/exception.ts

View check run for this annotation

Codecov / codecov/patch

src/common/exception.ts#L1-L7

Added lines #L1 - L7 were not covered by tests
}

export interface ErrorDetails {
type: ErrorType
code?: number
details?: Record<string, any>
}

export class AppException extends Error {
constructor(message: string) {
public readonly type: ErrorType
public readonly code?: number
public readonly details?: Record<string, any>

Check warning on line 19 in src/common/exception.ts

View check run for this annotation

Codecov / codecov/patch

src/common/exception.ts#L17-L19

Added lines #L17 - L19 were not covered by tests

constructor(message: string, errorDetails?: Partial<ErrorDetails>) {

Check warning on line 21 in src/common/exception.ts

View check run for this annotation

Codecov / codecov/patch

src/common/exception.ts#L21

Added line #L21 was not covered by tests
super(message)
this.name = 'AppException'
this.type = errorDetails?.type || ErrorType.Unknown
this.code = errorDetails?.code
this.details = errorDetails?.details

Check warning on line 26 in src/common/exception.ts

View check run for this annotation

Codecov / codecov/patch

src/common/exception.ts#L24-L26

Added lines #L24 - L26 were not covered by tests

// Show error message to user if window.$message is available
if (window.$message) {
window.$message.error(message)
}
}

Check warning on line 32 in src/common/exception.ts

View check run for this annotation

Codecov / codecov/patch

src/common/exception.ts#L29-L32

Added lines #L29 - L32 were not covered by tests

public toJSON() {
return {
name: this.name,
message: this.message,
type: this.type,
code: this.code,
details: this.details
}

Check warning on line 41 in src/common/exception.ts

View check run for this annotation

Codecov / codecov/patch

src/common/exception.ts#L34-L41

Added lines #L34 - L41 were not covered by tests
}
}
46 changes: 40 additions & 6 deletions src/services/http.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { fetch } from '@tauri-apps/plugin-http'
import { AppException, ErrorType } from '../common/exception'

Check warning on line 2 in src/services/http.ts

View check run for this annotation

Codecov / codecov/patch

src/services/http.ts#L2

Added line #L2 was not covered by tests

/**
* @description 请求参数
Expand Down Expand Up @@ -32,10 +33,12 @@
*/
class FetchRetryError extends Error {
status: number
type: ErrorType

Check warning on line 36 in src/services/http.ts

View check run for this annotation

Codecov / codecov/patch

src/services/http.ts#L36

Added line #L36 was not covered by tests
constructor(message: string, status: number) {
super(message)
this.status = status
this.name = 'FetchRetryError'
this.type = status >= 500 ? ErrorType.Server : ErrorType.Network

Check warning on line 41 in src/services/http.ts

View check run for this annotation

Codecov / codecov/patch

src/services/http.ts#L41

Added line #L41 was not covered by tests
}
}

Expand Down Expand Up @@ -144,33 +147,55 @@

// 若响应不 OK 并且状态码属于需重试列表,则抛出 FetchRetryError
if (!response.ok) {
const errorType = getErrorType(response.status)

Check warning on line 150 in src/services/http.ts

View check run for this annotation

Codecov / codecov/patch

src/services/http.ts#L150

Added line #L150 was not covered by tests
if (!retryOn || retryOn.includes(response.status)) {
throw new FetchRetryError(`HTTP error! status: ${response.status}`, response.status)
}
// 如果是非重试状态码,则抛出普通错误
throw new Error(`HTTP error! status: ${response.status}`)
// 如果是非重试状态码,则抛出带有适当错误类型的 AppException
throw new AppException(`HTTP error! status: ${response.status}`, {
type: errorType,
code: response.status,
details: { url, method: options.method }
})

Check warning on line 159 in src/services/http.ts

View check run for this annotation

Codecov / codecov/patch

src/services/http.ts#L155-L159

Added lines #L155 - L159 were not covered by tests
}

// 解析响应数据
const responseData = options.isBlob ? await response.arrayBuffer() : await response.json()

// 若有success === false,需要重试
if (responseData && responseData.success === false) {
throw new Error(url)
throw new AppException(responseData.message || url, {
type: ErrorType.Server,
code: response.status,
details: responseData
})

Check warning on line 171 in src/services/http.ts

View check run for this annotation

Codecov / codecov/patch

src/services/http.ts#L167-L171

Added lines #L167 - L171 were not covered by tests
}

// 若请求成功且没有业务错误
if (fullResponse) {
return { data: responseData, resp: response }
}
return responseData
} catch (err) {
console.error(`Attempt ${currentAttempt + 1} failed →`, err)
} catch (error) {
console.error(`Attempt ${currentAttempt + 1} failed →`, error)

Check warning on line 180 in src/services/http.ts

View check run for this annotation

Codecov / codecov/patch

src/services/http.ts#L179-L180

Added lines #L179 - L180 were not covered by tests

// 检查是否仍需重试
if (!shouldRetry(currentAttempt, retries, abort)) {
console.error(`Max retries reached or aborted. Request failed → ${url}`)
throw err instanceof Error ? err : new Error(String(err)) // 不再重试,抛出最终错误
if (error instanceof FetchRetryError) {
throw new AppException(error.message, {
type: error.type,
code: error.status,
details: { url, attempts: currentAttempt + 1 }
})
}
if (error instanceof AppException) {
throw error
}
throw new AppException(String(error), {
type: ErrorType.Unknown,
details: { url, attempts: currentAttempt + 1 }
})

Check warning on line 198 in src/services/http.ts

View check run for this annotation

Codecov / codecov/patch

src/services/http.ts#L185-L198

Added lines #L185 - L198 were not covered by tests
}

// 若需继续重试
Expand All @@ -181,6 +206,15 @@
}
}

// 辅助函数:根据HTTP状态码确定错误类型
function getErrorType(status: number): ErrorType {
if (status >= 500) return ErrorType.Server
if (status === 401 || status === 403) return ErrorType.Authentication
if (status === 400 || status === 422) return ErrorType.Validation
if (status >= 400) return ErrorType.Client
return ErrorType.Network
}

Check warning on line 216 in src/services/http.ts

View check run for this annotation

Codecov / codecov/patch

src/services/http.ts#L210-L216

Added lines #L210 - L216 were not covered by tests

// 第一次执行,attempt=0
return attemptFetch(0)
}
Expand Down
Loading