初始化模版工程
This commit is contained in:
59
http/http.ts
Normal file
59
http/http.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
|
||||
import { defaultGetResult, HttpDefine, HttpInjectInfo } from './type'
|
||||
|
||||
export async function http<R = any>(define: HttpDefine, inject?: HttpInjectInfo): Promise<R> {
|
||||
let sureDefined = define
|
||||
if (inject?.onRequest) {
|
||||
sureDefined = await inject.onRequest(sureDefined)
|
||||
}
|
||||
|
||||
const url = HttpDefine.getUrl(sureDefined)
|
||||
const headers = HttpDefine.getHeader(sureDefined)
|
||||
const body = HttpDefine.getBody(sureDefined)
|
||||
|
||||
const fetchOption: RequestInit = {
|
||||
method: (sureDefined.method || 'get').toUpperCase(),
|
||||
headers,
|
||||
credentials: sureDefined.credentials ?? 'same-origin',
|
||||
}
|
||||
|
||||
if (sureDefined.abort) {
|
||||
fetchOption.signal = sureDefined.abort.signal
|
||||
}
|
||||
|
||||
if (sureDefined.timeout) {
|
||||
const abort = sureDefined.abort || new AbortController()
|
||||
|
||||
setTimeout(() => {
|
||||
abort.abort()
|
||||
}, sureDefined.timeout)
|
||||
|
||||
fetchOption.signal = abort.signal
|
||||
}
|
||||
|
||||
if (
|
||||
!['get', 'head', 'options', 'GET', 'HEAD', 'OPTIONS'].includes(fetchOption.method!) &&
|
||||
body != null
|
||||
) {
|
||||
fetchOption.body = body
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(url, fetchOption)
|
||||
if (!response.ok) {
|
||||
const errorMessage = sureDefined.customErrorMessage || `HTTP error! status: ${response.status}`
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
if (inject?.onResponse) {
|
||||
await inject.onResponse(response, define)
|
||||
}
|
||||
|
||||
return defaultGetResult(response)
|
||||
} catch (error) {
|
||||
if (inject?.onError) {
|
||||
inject.onError(error as Error, define)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
60
http/index.ts
Normal file
60
http/index.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
|
||||
import type { HttpDefine, HttpInjectInfo } from './type'
|
||||
import { http } from './http'
|
||||
|
||||
export class HTTPClient {
|
||||
base: HttpDefine
|
||||
inject: HttpInjectInfo
|
||||
|
||||
constructor(base: HttpDefine, inject?: HttpInjectInfo) {
|
||||
this.base = base
|
||||
this.inject = inject || {}
|
||||
}
|
||||
|
||||
request<R = any>(config: HttpDefine): Promise<R> {
|
||||
return http({ ...this.base, ...config }, this.inject)
|
||||
}
|
||||
|
||||
get<R = any>(url: string, query?: any, config?: HttpDefine): Promise<R> {
|
||||
return this.request({ ...config, url, method: 'get', query })
|
||||
}
|
||||
|
||||
delete<R = any>(url: string, query?: any, config?: HttpDefine): Promise<R> {
|
||||
return this.request({ ...config, url, method: 'delete', query })
|
||||
}
|
||||
|
||||
head<R = any>(url: string, config?: HttpDefine): Promise<R> {
|
||||
return this.request({ ...config, url, method: 'head' })
|
||||
}
|
||||
|
||||
options<R = any>(url: string, config?: HttpDefine): Promise<R> {
|
||||
return this.request({ ...config, url, method: 'options' })
|
||||
}
|
||||
|
||||
post<R = any>(url: string, body?: any, config?: HttpDefine): Promise<R> {
|
||||
return this.request({
|
||||
...config,
|
||||
url,
|
||||
method: 'post',
|
||||
body,
|
||||
})
|
||||
}
|
||||
|
||||
put<R = any>(url: string, body?: any, config?: HttpDefine): Promise<R> {
|
||||
return this.request({
|
||||
...config,
|
||||
url,
|
||||
method: 'put',
|
||||
body,
|
||||
})
|
||||
}
|
||||
|
||||
patch<R = any>(url: string, body?: any, config?: HttpDefine): Promise<R> {
|
||||
return this.request({
|
||||
...config,
|
||||
url,
|
||||
method: 'patch',
|
||||
body,
|
||||
})
|
||||
}
|
||||
}
|
||||
22
http/request.ts
Normal file
22
http/request.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { HTTPClient } from '@/http'
|
||||
|
||||
const CLIENT_API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || '/api'
|
||||
|
||||
export const request = new HTTPClient(
|
||||
{
|
||||
baseURL: CLIENT_API_BASE_URL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
{
|
||||
onRequest: async config => config,
|
||||
onError: (error, config) => {
|
||||
console.error('request error', {
|
||||
method: config.method,
|
||||
url: config.url,
|
||||
error,
|
||||
})
|
||||
},
|
||||
},
|
||||
)
|
||||
141
http/type.ts
Normal file
141
http/type.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
|
||||
import { get, hasIn, isNil, isString, merge, omitBy } from 'lodash-es'
|
||||
import type { IStringifyOptions } from 'qs'
|
||||
import qs from 'qs'
|
||||
|
||||
export async function defaultGetResult(response: Response) {
|
||||
if (response.headers.get('content-type')?.includes('application/json')) {
|
||||
const jsonData = await response.json()
|
||||
if (hasIn(jsonData, 'data')) {
|
||||
return jsonData.data
|
||||
}
|
||||
return jsonData
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
export class HttpDefine implements Omit<RequestInit, 'body'> {
|
||||
baseURL?: string
|
||||
|
||||
url?: string
|
||||
|
||||
method?: string
|
||||
|
||||
abort?: AbortController
|
||||
|
||||
timeout?: number
|
||||
|
||||
credentials?: RequestCredentials
|
||||
|
||||
ignoreError?: boolean
|
||||
|
||||
noRedirect?: boolean
|
||||
|
||||
customErrorMessage?: string
|
||||
|
||||
ignoreShowLimitedModal?: boolean
|
||||
|
||||
headers?: Record<string, any> = {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
query?: Record<string, any>
|
||||
|
||||
// qs 序列化参数使用
|
||||
querySerializer?: IStringifyOptions
|
||||
|
||||
body?: Record<string, any> | FormData
|
||||
|
||||
static hasOrigin(url: string) {
|
||||
try {
|
||||
const parsedUrl = new URL(url)
|
||||
return parsedUrl.hostname !== ''
|
||||
} catch {
|
||||
// 如果 URL 无效,返回 false
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
static mergeUrl(baseURL: string, url: string) {
|
||||
if (this.hasOrigin(url)) return url
|
||||
if (!baseURL) return url
|
||||
let prefix = baseURL
|
||||
let last = url
|
||||
if (baseURL[baseURL.length - 1] === '/') {
|
||||
prefix = baseURL.substring(0, baseURL.length - 1)
|
||||
}
|
||||
if (url[0] === '/') {
|
||||
last = url.substring(1)
|
||||
}
|
||||
return `${prefix}/${last}`
|
||||
}
|
||||
|
||||
static getUrl<T extends HttpDefine>(define: T = {} as any) {
|
||||
const urlObj = new URL(
|
||||
this.mergeUrl(define.baseURL ?? '', define.url ?? ''),
|
||||
typeof window === 'object' ? window.origin : undefined,
|
||||
)
|
||||
|
||||
const searchStr = urlObj.searchParams.toString()
|
||||
const searchObj = merge(qs.parse(searchStr), define.query)
|
||||
const sureSearchStr = qs.stringify(searchObj, define.querySerializer)
|
||||
|
||||
const base = `${urlObj.origin}${urlObj.pathname}`
|
||||
if (sureSearchStr) {
|
||||
return `${base}?${sureSearchStr}`
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
static getHeader<T extends HttpDefine>(define: T = {} as any) {
|
||||
const sureHeaders = omitBy(
|
||||
merge({}, define.headers ?? {}),
|
||||
isNil,
|
||||
) as Record<string, any>
|
||||
|
||||
if (
|
||||
get(sureHeaders, 'Content-Type', '').includes('multipart/form-data') ||
|
||||
define.body instanceof FormData
|
||||
) {
|
||||
delete sureHeaders['Content-Type']
|
||||
} else if (!get(sureHeaders, 'Content-Type', '')) {
|
||||
sureHeaders['Content-Type'] = 'application/json'
|
||||
}
|
||||
|
||||
return sureHeaders
|
||||
}
|
||||
|
||||
static getBody<T extends HttpDefine>(define: T = {} as any) {
|
||||
if (isString(define.body)) {
|
||||
return define.body
|
||||
}
|
||||
|
||||
if (define.body instanceof FormData) {
|
||||
return define.body
|
||||
}
|
||||
|
||||
if (define.body == null) return null
|
||||
|
||||
if (
|
||||
get(define.headers, 'Content-Type', '').includes('multipart/form-data')
|
||||
) {
|
||||
const formData = new FormData()
|
||||
|
||||
Object.entries(define.body ?? []).forEach(([key, value]) => {
|
||||
formData.append(key, value)
|
||||
})
|
||||
|
||||
return formData
|
||||
}
|
||||
|
||||
return JSON.stringify(define.body || null)
|
||||
}
|
||||
}
|
||||
|
||||
export interface HttpInjectInfo {
|
||||
onRequest?: (define: HttpDefine) => Promise<HttpDefine>
|
||||
onResponse?: (response: Response, define: HttpDefine) => Promise<void>
|
||||
onError?: (error: Error, define: HttpDefine) => void
|
||||
getResult?: (response: Response, define: HttpDefine) => Promise<any>
|
||||
}
|
||||
Reference in New Issue
Block a user