123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- import {
- NoopObject,
- addTrailingSlash,
- queryString,
- removeHeadingSlash,
- } from "@dage/utils";
- import { createHeader } from "./Header";
- import { stringReplace, variableReplace } from "./replace";
- import {
- DageRequest,
- DageRequestConfig,
- DageResponse,
- Interceptor,
- PaginationParams,
- PaginationResponse,
- } from "./types";
- import {
- detectContentType,
- headersToObject,
- methodsHasBody,
- serializeBody,
- serializeResponseBody,
- } from "./utils";
- import { ResponseError } from "./error";
- export interface ServiceAdapter {
- baseURL: string;
- fetch: typeof fetch;
- /**
- * 拦截器
- */
- interceptor?: Interceptor;
- /**
- * 全局变量
- */
- globalVariables?: Record<string, any>;
- }
- const DEFAULT_INTERCEPTOR: Interceptor = (_, next) => {
- return next();
- };
- export function createService() {
- let adapter: ServiceAdapter;
- let initialized = false;
- /**
- * 是否初始化完成
- * @returns
- */
- function isInitialized() {
- return initialized;
- }
- /**
- * 获取 Base URL
- */
- function getBaseURL() {
- if (!initialized) {
- throw new Error("未初始化");
- }
- return adapter.baseURL;
- }
- /**
- * 解析响应
- */
- function parseResponse(
- response: DageResponse | PaginationResponse
- ): DageResponse | PaginationResponse {
- if (response == null) {
- throw {
- message: "未能识别响应",
- code: -1,
- };
- }
- if (response.errorMessage) {
- throw new ResponseError({
- ...response,
- errorMessage: response.errorMessage ?? "请求失败",
- errorCode: response.errorCode ?? -1,
- });
- }
- return response;
- }
- /**
- * 初始化
- * @param options
- */
- function initial(options: ServiceAdapter) {
- if (initialized && process.env.NODE_ENV === "development") {
- console.error("[@dage/service] 已经初始化过了, 不需要重复初始化");
- }
- adapter = options;
- adapter.baseURL = addTrailingSlash(options.baseURL);
- initialized = true;
- }
- async function normalizeRequest<R = any, P extends {} = any>(
- url: string,
- body?: P,
- config: DageRequestConfig = NoopObject
- ): Promise<PaginationResponse<R> | DageResponse<R>> {
- if (!initialized) {
- throw new Error("请先调用 initial 初始化");
- }
- const method = config.method ?? "POST";
- const searchParams: Record<string, string> = {};
- const reqBody = body ?? NoopObject;
- // 路由参数替换
- url = stringReplace(url, reqBody);
- // 查询字符串处理
- const appendSearchParams = (
- obj: Record<string, string>,
- params = searchParams
- ) => {
- Object.assign(params, obj);
- };
- if (config.searchParams) {
- appendSearchParams(config.searchParams);
- }
- // 报头处理
- const headers: Record<string, string> = createHeader();
- if (config.headers) {
- Object.keys(config.headers).forEach((key) => {
- headers[key] = config.headers![key];
- });
- }
- if (methodsHasBody(method) && !headers["Content-Type"]) {
- const contentType = detectContentType(reqBody);
- if (contentType) {
- headers["Content-type"] = contentType;
- }
- }
- const requestPayload: DageRequest = {
- name: url,
- method,
- body: reqBody,
- searchParams,
- headers,
- meta: config.meta ?? NoopObject,
- };
- // 变量替换
- if (adapter.globalVariables) {
- variableReplace(requestPayload.body, adapter.globalVariables);
- variableReplace(requestPayload.searchParams, adapter.globalVariables);
- variableReplace(requestPayload.headers, adapter.globalVariables);
- }
- const interceptor = adapter.interceptor ?? DEFAULT_INTERCEPTOR;
- // next 方法被拦截器调用的次数,如果调用次数过多,说明程序存在bug,不排除有无限循环的可能
- let nextCallTime = 0;
- const response = await interceptor(requestPayload, async () => {
- nextCallTime++;
- // 最多 3 次
- if (nextCallTime > 3) {
- throw new Error(
- "拦截器调用 next 次数过多, 请检查代码,可能存在无限循环"
- );
- }
- if (requestPayload.body && requestPayload.method === "GET") {
- appendSearchParams(requestPayload.body, requestPayload.searchParams);
- }
- // 构建 href
- let href = url.startsWith("http")
- ? url
- : adapter.baseURL + removeHeadingSlash(url);
- if (Object.keys(requestPayload.searchParams).length) {
- const temp = queryString.parseUrl(href);
- href = queryString.stringifyUrl({
- url: temp.url,
- query: { ...temp.query, ...requestPayload.searchParams },
- fragmentIdentifier: temp.fragmentIdentifier,
- });
- }
- /**
- * 主体处理
- */
- const finalBody =
- requestPayload.body && methodsHasBody(requestPayload.method)
- ? serializeBody(requestPayload.headers, requestPayload.body)
- : undefined;
- const res = await adapter.fetch(href, {
- method: requestPayload.method,
- headers: requestPayload.headers,
- body: finalBody,
- mode: "cors",
- });
- if (!res.ok) {
- console.error(res);
- throw new Error(`[@dage/service]请求 ${url} 失败: ${res.status}`);
- }
- const resData = await serializeResponseBody(res, config);
- // @ts-expect-error
- const clone: DageResponse = {
- ...resData,
- // 原始请求信息
- __raw__: {
- statusCode: res.status,
- data: resData,
- header: headersToObject(res.headers),
- },
- };
- return clone as DageResponse | PaginationResponse;
- });
- // 解析协议
- return parseResponse(response);
- }
- /**
- * 请求方法,默认使用 POST 方法
- */
- async function request<R = any, P extends {} = any>(
- url: string,
- body?: P,
- config?: DageRequestConfig
- ): Promise<R> {
- return (await normalizeRequest(url, body, config)).data;
- }
- const requestByPost = request;
- async function requestByGet<R = any, P extends {} = any>(
- url: string,
- body?: P,
- config?: DageRequestConfig
- ): Promise<R> {
- return (await normalizeRequest(url, body, { method: "GET", ...config }))
- .data;
- }
- /**
- * 列表接口请求, 默认为 POST 请求
- */
- async function requestPagination<R = any, P extends PaginationParams = any>(
- name: string,
- body?: P,
- config?: DageRequestConfig
- ): Promise<PaginationResponse<R>> {
- return (await normalizeRequest(name, body, {
- method: "POST",
- ...config,
- })) as PaginationResponse<R>;
- }
- return {
- getBaseURL,
- parseResponse,
- initial,
- isInitialized,
- request,
- requestByPost,
- requestByGet,
- requestPagination,
- };
- }
- const DEFAULT_SERVICE = createService();
- /**
- * 获取 Base URL
- * @returns
- */
- export const getBaseURL = DEFAULT_SERVICE.getBaseURL;
- /**
- * 解析响应
- * @param response
- * @returns
- */
- export const parseResponse = DEFAULT_SERVICE.parseResponse;
- /**
- * 是否初始化完成
- * @returns
- */
- export const isInitialized = DEFAULT_SERVICE.isInitialized;
- /**
- * 初始化
- * @param options
- */
- export const initial = DEFAULT_SERVICE.initial;
- export const request = DEFAULT_SERVICE.request;
- export const requestByPost = DEFAULT_SERVICE.requestByPost;
- export const requestByGet = DEFAULT_SERVICE.requestByGet;
- export const requestPagination = DEFAULT_SERVICE.requestPagination;
|