بدء سريع
عميل HTTP آمن من نوع RxJS لـ TypeScript + (اختياري) محول Next.js/RSocket.
الميزات الأساسية
- ضمان استقرار نوع route (الطلب) عن طريق حقن نوع Paths بأسلوب OpenAPI المستمد من Swagger/OpenAPI / AsyncAPI / مخططات مخصصة.
- تقوم جميع واجهات برمجة التطبيقات بإرجاع RxJS Observable.
- النواة مستقلة عن الإطار (لا تعتمد على Next.js)
- الميزات الخاصة بـ Next.js مفصولة في نقطة الدخول @byeolnaerim/typed-rx-http/next → تحتاج Next.js فقط عند استيراد /next.
- الميزات الخاصة بـ RSocket مفصولة في نقطة الدخول @byeolnaerim/typed-rx-http/rsocket → تحتاج حزمة RSocket فقط عند استيراد /rsocket.
التثبيت
npm
npm i @byeolnaerim/typed-rx-http rxjsنقطة الدخول
Core (مستقل عن الإطار)
import {
createHttpClient,
createHeaderStore,
createCsrCache,
createSessionAuth,
HttpResponseError,
isHttpResponseError,
type ServiceArguments,
type CacheForService,
} from "@byeolnaerim/typed-rx-http";محول Next.js (اختياري)
import {
callApiSsrCache,
redirectToUnauthorizedOnServer401,
} from "@byeolnaerim/typed-rx-http/next";لا تستورد /next في المشاريع التي لا تستخدم Next.js.
محول RSocket (اختياري)
قم بتثبيت حزمة peer فقط في المشاريع التي تستخدم RSocket.
npm i rsocket-core rsocket-types rsocket-websocket-clientimport { createRSocketApi } from "@byeolnaerim/typed-rx-http/rsocket";
const api = createRSocketApi("ws://localhost:7000/rsocket", jwt);
api.mono<User>("user.findById", { id: 1 }).subscribe((user) => {
console.log(user);
});
api.stream<Notification>("notification.stream").subscribe((notification) => {
console.log(notification);
});لا تستورد /rsocket في المشاريع التي لا تستخدم RSocket.
كيفية استخدام النواة
1) إعداد نوع Paths (عادةً ما يكون OpenAPI paths)
Paths في createHttpClient<Paths>() تعبر عن "مواصفات الطلب (route)". تُسمى عادةً paths في الوثيقة، لكن ليس من الضروري أن تكون OpenAPI/Swagger، أو أن يكون اسمها paths.
ومع ذلك، تستخدم النواة داخليًا قيد OpenApiPathsLike، لذا يجب أن تكون Paths مشابهة للشكل أدناه مثل OpenAPI paths.
- المفتاح الأعلى: سلسلة مسار URL (مثل: "/users/{id}")
- المفتاح الفرعي: طريقة HTTP (get/post/put/delete/patch …)
- يوجد داخل كل طريقة حقول مثل parameters.query/path/header/cookie وrequestBody وresponses (أو never)
تشير النواة بشكل أساسي إلى الحقول أدناه لتكوين نوع ServiceArguments.
- url: keyof Paths
- method: keyof Paths[url]
- queryString: parameters.query
- pathVariable: parameters.path
- body: requestBody
مثال: مخرجات openapi-typescript عادة ما تكون وفقًا للمواصفات التالية (بعض الاختصارات).
export interface paths {
"/test/get-hello-world": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
200: {
content: {
"application/json": components["schemas"]["TestResponse"];
};
};
};
};
};
}// aliasing: OpenAPI `paths` -> `Paths`
import type { paths as Paths } from "./@types/ApiTypes";2) إنشاء HeaderStore
HeaderStore هو مخزن بسيط في الذاكرة لإدارة الرؤوس الأساسية في CSR.
import { createHeaderStore } from "@byeolnaerim/typed-rx-http";
export const headerStore = createHeaderStore({
"Content-Type": "application/json",
});3) إنشاء عميل HTTP
- headerStore اختياري، ولكن يُوصى بإضافته إذا كنت ترغب في استخدام الرؤوس الأساسية/مصادقة الجلسة في CSR.
- headersProvider يُستخدم عندما تحتاج إلى حساب الرؤوس لكل طلب مثل SSR/تعدد المستأجرين.
import { createHttpClient } from "@byeolnaerim/typed-rx-http";
import type { paths as Paths } from "./@types/ApiTypes";
const apiUrl = process.env.NEXT_PUBLIC_API_URL!;
export const client = createHttpClient<Paths>({
baseUrl: apiUrl,
headerStore,
});4) استدعاء API (آمن من حيث النوع)
نوع الطلب (url/method/pathVariable/queryString/body) يتم تحديده من النوع الذي تم حقنه في createHttpClient<Paths>() (عادةً ما يكون مسارات OpenAPI). نوع الاستجابة يتم اختياره من قبل المستدعي في callApi<R>() كـ R جنريك (النواة لا تستنتج تلقائيًا من responses).
export const getAdminList = () => {
return client.callApi<{ items: any[]; total: number }>({
url: "/api/account/search/admin/get-list",
method: "get",
queryString: { pageNumber: 1, pageSize: 20 },
});
};تغليف الاستجابة (ResponseWrapper) — اختياري
هذه المكتبة لا تفرض تغليف الاستجابة. يمكنك اختيار شكل الاستجابة بشكل جنريك لكل API.
استجابة مغلفة
type ResponseWrapper<T> = { data: T; message?: string; resultType?: string };
export const bidCreate = (body: unknown) => {
return client.callApi<ResponseWrapper<{ requestId: string }>>({
url: "/api/bid/create/bid/request",
method: "post",
body,
});
};استجابة غير مغلفة
export const bidCreate = (body: unknown) => {
return client.callApi<{ requestId: string }>({
url: "/api/bid/create/bid/request",
method: "post",
body,
});
};البث (NDJSON)
عندما يقدم الخادم NDJSON (JSON واحد في كل سطر)، استخدم callApiStream. إذا لم يكن هناك رأس Accept، يتم تعيين application/x-ndjson كقيمة افتراضية.
export const streamEvents = () => {
return client.callApiStream<{ eventName: string; content: unknown }>({
url: "/api/event/stream",
method: "get",
// headers: { Accept: "application/x-ndjson" }, // 선택
});
};ذاكرة التخزين المؤقت CSR (ذاكرة التخزين المؤقت للعميل)
الوظائف التي يوفرها createCsrCache<CacheName>():
callApiCsrCache(callApiFn, serviceArgs, cacheOptions)- removeCsrCache(cacheName) — يدعم جميع أسماء نوع الذاكرة + السلاسل النصية.
import { createCsrCache } from "@byeolnaerim/typed-rx-http";
import type { paths as Paths } from "./@types/ApiTypes";
type CacheNames = "adminList" | "profile";
const csrCache = createCsrCache<CacheNames>();
export const callApiClientCache = <R>(
args: ServiceArguments<Paths, any, any, R>,
cache: { cacheName: CacheNames; cacheTime: number; cacheSize?: number },
) => csrCache.callApiCsrCache(client.callApi, args as any, cache as any);
// 무효화
csrCache.removeCsrCache("adminList"); // 타입
csrCache.removeCsrCache("any-prefix"); // 문자열ملحق مصادقة قائم على الجلسة (اختياري)
createSessionAuth يفصل منطق مصادقة الجلسة عن النواة بحيث يمكن إضافته وإزالته كخيار.
العمليات:
- الحفاظ على Authorization في headerStore
- مزامنة الرمز باستخدام ensureToken$() (/api/auth/token)
- عند حدوث 401، حاول التحديث مرة واحدة (/api/auth/token/refresh) ثم أعد محاولة الطلب الأصلي.
- عند فشل التحديث، قم بتسجيل الخروج (/api/auth/logout) ثم نقل الخطأ.
- تغيير حالة تسجيل الدخول يتم معالجته خارجيًا بواسطة onLoginChange callback.
import { createSessionAuth } from "@byeolnaerim/typed-rx-http";
const auth = createSessionAuth({
headerStore,
onLoginChange: (loggedIn) => {
// 예: setLogin(loggedIn) 같은 외부 상태 업데이트(선택)
},
// tokenUrl / refreshUrl / logoutUrl 커스터마이징 가능
});
export const secureCall = () => {
return client
.callApi<{ ok: true }>({ url: "/api/secure", method: "get" })
.pipe(auth.withSessionAuth());
};إذا كنت بحاجة فقط لمزامنة الرمز بدون تحديث/إعادة محاولة:
return client.callApi(...).pipe(auth.withEnsureToken());معالجة الأخطاء
إذا لم يكن 2xx، يتم رمي HttpResponseError (بما في ذلك status و response و args و data).
التوافق مع الأنظمة القديمة: إذا كان جسم الخطأ على شكل { resultType: ... }، يتم رمي هذا الكائن كما هو.
import { isHttpResponseError } from "@byeolnaerim/typed-rx-http";
client.callApi(...).subscribe({
error: (e) => {
if (isHttpResponseError(e)) {
console.log(e.status, e.data);
}
},
});محول Next.js (/next)
redirectToUnauthorizedOnServer401
redirectToUnauthorizedOnServer401 هو التنفيذ الافتراضي (دالة مساعدة) الذي يقوم بإعادة التوجيه عند حدوث 401 في بيئة SSR لـ Next.js (App Router).
import { redirectToUnauthorizedOnServer401 } from "@byeolnaerim/typed-rx-http/next";قواعد التشغيل (ثابتة):
- وجهة إعادة التوجيه: /unauthorized
- queryString: redirect_uri=<الصفحة الحالية> + logout=true
- يتم قراءة الصفحة الحالية من رأس x-page-url (إذا لم يكن موجودًا، يتم استخدام /)
أي، استخدم قواعد المسار/الاستعلام أعلاه كما هي فقط عندما تتطابق مع المشروع. إذا كان المسار مختلفًا أو كانت قواعد الاستعلام مختلفة، يمكنك تنفيذ onServer401 مباشرة كما هو موضح أدناه.
import { headers } from "next/headers";
import { redirect } from "next/navigation";
export const onServer401 = async () => {
const h = await headers();
const pageUrl = h.get("x-page-url") || "/";
redirect(`/login?next=${encodeURIComponent(pageUrl)}`);
};callApiSsrCache
مساعد ذاكرة التخزين المؤقت SSR القائم على next/cache(unstable_cache) من Next.
- GET + cacheTime > 0 → force-cache + revalidate
- بخلاف ذلك → no-store
- حقن ملفات تعريف الارتباط / التفويض لكل طلب باستخدام headersProvider
- عند حدوث 401، يتم تنفيذ onServer401 إذا كان موجودًا (عادةً redirect())
import type { paths as Paths } from "./@types/ApiTypes";
import { firstValueFrom } from "rxjs";
import {
callApiSsrCache,
redirectToUnauthorizedOnServer401,
} from "@byeolnaerim/typed-rx-http/next";
const apiUrl = process.env.NEXT_PUBLIC_API_URL!;
const headersProvider = async () => {
const { cookies } = await import("next/headers");
const cookieStore = await cookies();
const cookieHeader = cookieStore
.getAll()
.map((c) => `${c.name}=${c.value}`)
.join("; ");
const accessToken = cookieStore.get("accessToken")?.value;
return {
...(cookieHeader ? { Cookie: cookieHeader } : {}),
...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),
};
};
const obs$ = callApiSsrCache<Paths, { ok: true }, "adminList">({
baseUrl: apiUrl,
serviceArguments: { url: "/api/secure", method: "get" },
cacheForService: { cacheName: "adminList", cacheTime: 10_000 },
headersProvider,
onServer401: redirectToUnauthorizedOnServer401,
});
const res = await firstValueFrom(obs$);مثال تكامل Next.js: الكود الكامل لمشروع rxjsHttpService/commonService
ملف rxjsHttpService.ts أدناه هو محول HTTP العام للمشروع المشار إليه في مثال المولد لاحقًا. هذا ليس ملفًا تم إنشاؤه بواسطة المكتبة، بل يتم إدارته مباشرة من قبل المشروع، ويصدر core client + session auth + CSR cache + SSR cache helper في مكان واحد. أدناه هو الكود الكامل.
rxjsHttpService.ts
tsimport {
createCsrCache,
createHeaderStore,
createHttpClient,
createSessionAuth,
} from "@byeolnaerim/typed-rx-http";
import type {
CacheForService,
ServiceArguments,
} from "@byeolnaerim/typed-rx-http";
import { defer, Observable } from "rxjs";
import { setLogin } from "@ui/handler/hooks/useAccounts";
import type { paths } from "./@types/ApiTypes";
import type { CacheNames } from "./@types/CacheNames";
export const redirectToUnauthorizedOnServer401 = async () => {
const { headers } = await import("next/headers");
const { redirect } = await import("next/navigation");
const h = await headers();
const pageUrl = h.get("x-page-url") || "/";
const redirectUri = encodeURIComponent(pageUrl);
redirect(`/unauthorized?redirect_uri=${redirectUri}&logout=true`);
};
export const headerStore = createHeaderStore({
"Content-Type": "application/json",
});
const headersProvider = async () => {
if (typeof window !== "undefined") {
/**
* 라이브러리쪽 코드가
* if (headersProvider) return headersProvider();
* if (headerStore) return headerStore.get();
* 이렇게 되어있음 같이 못씀, 방향자체는 맞음. 라이브러리쪽에서 marge해버리면 csr/ssr 구분못함
*/
return headerStore.get();
}
const { cookies } = await import("next/headers");
const cookieStore = await cookies();
const cookieHeader = cookieStore
.getAll()
.map((c) => `${c.name}=${c.value}`)
.join("; ");
const authorization = cookieStore.get("Authorization")?.value;
const accessToken = cookieStore.get("accessToken")?.value;
return {
...(cookieHeader ? { Cookie: cookieHeader } : {}),
...(authorization
? {
Authorization: authorization.startsWith("Bearer ")
? authorization
: `Bearer ${authorization}`,
}
: {}),
...(!authorization && accessToken
? {
Authorization: accessToken.startsWith("Bearer ")
? accessToken
: `Bearer ${accessToken}`,
}
: {}),
};
};
export const service = createHttpClient<paths>({
baseUrl: process.env.NEXT_PUBLIC_API_URL || "",
onServer401: redirectToUnauthorizedOnServer401,
defaultErrorMessage: () => {
return "알 수 없는 오류가 발생했습니다.";
},
headerStore,
headersProvider,
dropAuthWhenCacheControl: true,
});
const sessionAuth = createSessionAuth({
headerStore,
tokenUrl: "/api/auth/token",
refreshUrl: "/api/auth/token/refresh",
logoutUrl: "/api/auth/logout",
onLoginChange: (loggedIn) => {
if (typeof window !== "undefined") {
setLogin(loggedIn);
}
},
});
export const callApi = <
R,
TPath extends keyof paths = keyof paths,
TMethod extends keyof paths[TPath] & string = keyof paths[TPath] & string,
>(
serviceArguments: ServiceArguments<paths, TPath, TMethod, R>,
): Observable<R> => {
return defer(() =>
service.callApi<R, TPath, TMethod>(serviceArguments),
).pipe(sessionAuth.withSessionAuth());
};
export const callApiStream = <
R,
TPath extends keyof paths = keyof paths,
TMethod extends keyof paths[TPath] & string = keyof paths[TPath] & string,
>(
serviceArguments: ServiceArguments<paths, TPath, TMethod, R>,
): Observable<R> => {
return defer(() =>
service.callApiStream<R, TPath, TMethod>(serviceArguments),
).pipe(sessionAuth.withSessionAuth());
};
// CSR cache
const csrCache = createCsrCache<CacheNames>();
export const callApiClientCache = <
R,
TPath extends keyof paths = keyof paths,
TMethod extends keyof paths[TPath] & string = keyof paths[TPath] & string,
>(
serviceArguments: ServiceArguments<paths, TPath, TMethod, R>,
cacheForService: CacheForService<CacheNames>,
): Observable<R> => {
return csrCache.callApiCsrCache(callApi, serviceArguments, cacheForService);
};
export const removeCsrCache = csrCache.removeCsrCache;
export const callApiServerCache = <
R,
TPath extends keyof paths = keyof paths,
TMethod extends keyof paths[TPath] & string = keyof paths[TPath] & string,
>(
serviceArguments: ServiceArguments<paths, TPath, TMethod, R>,
cacheForService: CacheForService<CacheNames>,
): Observable<R> => {
if (typeof window !== "undefined") {
return callApiClientCache(serviceArguments, cacheForService);
}
return new Observable<R>((subscriber) => {
let closed = false;
import("@byeolnaerim/typed-rx-http/next")
.then(({ callApiSsrCache }) => {
if (closed) {
return;
}
const subscription = callApiSsrCache<paths, R, CacheNames>({
baseUrl: process.env.NEXT_PUBLIC_API_URL || "",
serviceArguments: serviceArguments as ServiceArguments<
paths,
keyof paths,
keyof paths[keyof paths] & string,
R
>,
cacheForService,
headersProvider,
onServer401: redirectToUnauthorizedOnServer401,
}).subscribe(subscriber);
subscriber.add(() => {
closed = true;
subscription.unsubscribe();
});
})
.catch((error) => {
if (!closed) {
subscriber.error(error);
}
});
return () => {
closed = true;
};
});
};مرجع API (core)
createHttpClient<Paths>(options)
الإرجاع:
callApi<R>(args): Observable<R>callApiStream<RChunk>(args): Observable<RChunk>uploadFile({ file, url, ifNoneMatch?, headers? }): Observable<Response>createSSEObservable<R>(args): Observable<R>
الخيارات:
baseUrl: stringheaderStore?: HeaderStoreheadersProvider?: () => Record<string, string> | Promise<Record<string, string>>- dropAuthWhenCacheControl?: boolean (القيمة الافتراضية: true)
onServer401?: () => void | Promise<void>
createCsrCache<CacheName>()
get(), set(), merge(), remove(), clear()
createCsrCache<CacheName>()
callApiCsrCache(callApiFn, serviceArgs, cacheForService)- removeCsrCache(cacheName) (النوع + السلسلة)
تصديرات Stream & utility
withSessionAuth(), withEnsureToken()ensureToken$(), refreshToken$(), logout$()
متطلبات وقت التشغيل
- استخدام fetch / Response API (rxjs/fetch)
- البث (NDJSON) يتطلب ReadableStream + TextDecoder
- SSE يتطلب EventSource
يتوفر في معظم المتصفحات الحديثة وبيئة تشغيل Next.js. قد تحتاج بيئات تشغيل Node المخصصة إلى polyfill.
Auto Node Script: إنشاء كود OpenAPI/Swagger (اختياري)
تقدم هذه الحزمة، بالإضافة إلى عميل HTTP في وقت التشغيل، سكربت Node لإنشاء كود النوع/الخدمة من JSON OpenAPI/Swagger. هذا السكربت هو ميزة اختيارية.
لا يحتاج المستخدمون العاديون لـ @byeolnaerim/typed-rx-http، /next، /rsocket إلى تشغيل هذا السكربت، ولا يحتاجون إلى تثبيت openapi-typescript.
import {
connectToSwaggerEventStream,
generateSwaggerFromHttp,
generateSwaggerFromFile,
} from "@byeolnaerim/typed-rx-http/auto-node-script/openapi";عزل الاعتماديات
تتطلب إنشاء أنواع OpenAPI استخدام openapi-typescript CLI. لكن هذه الحزمة لا تضيف openapi-typescript إلى الاعتماديات العامة.
تستخدم مكتبة @byeolnaerim/typed-rx-http نفسها TypeScript 6.0.3 كـ devDependencies.typescript. تحافظ نقاط الدخول إلى typed-rx-http، /next، /rsocket وبناء المكتبة على هذا المعيار TypeScript 6.0.3.
ومع ذلك، قد يتطلب openapi-typescript إصدارًا معينًا من TypeScript 5.x، لذا يتم تشغيل سكربت OpenAPI auto node فقط في بيئة تنفيذ مؤقتة منفصلة مع openapi-typescript و [email protected]. لا تغير هذه البيئة المؤقتة devDependencies.typescript 6.0.3 للمكتبة، ولا تستخدم إصدار typescript أو openapi-typescript المثبت في مشروع المستخدم.
القيمة الافتراضية هي إنشاء وتنفيذ الأوامر أدناه فقط في وقت تشغيل السكربت التلقائي.
npx -y -p openapi-typescript@latest -p [email protected] openapi-typescript swagger.json --output ./src/handler/service/@types/ApiTypes.d.tsلذا، لا تتغير طريقة الاستخدام نفسها. يمكنك استدعاء سكربت auto node كما هو معتاد، وسيتم استخدام بيئة TypeScript 5.9.3 المعزولة فقط في مرحلة إنشاء نوع OpenAPI. المستخدمون الذين لا يستخدمون السكربت التلقائي غير مقيدين بـ openapi-typescript أو TypeScript 5.9.3.
إذا لزم الأمر، يمكنك تثبيت الأوامر مباشرة باستخدام openApiTypescriptCommand.
generateSwaggerFromFile({
inputFile: "./swagger.json",
openApiTypescriptCommand:
"npx -y -p [email protected] -p [email protected] openapi-typescript ./swagger.json --output ./src/handler/service/@types/ApiTypes.d.ts",
});أو يمكنك تغيير إصدار الحزمة التي تشكل الأوامر الأساسية فقط.
generateSwaggerFromFile({
inputFile: "./swagger.json",
openApiTypescriptPackage: "[email protected]",
openApiTypescriptTypescriptPackage: "[email protected]",
});الملفات التي يتم إنشاؤها
الإعدادات الافتراضية تنشئ الملفات أدناه.
swagger.json
src/handler/service/@types/ApiTypes.d.ts
src/handler/service/auto/*Service.ts
src/handler/service/@types/auto/*Types.ts
src/handler/service/apiUnionArrays.tsapiUnionArrays.ts ينشئ مصفوفات ثابتة للـ enum في مخطط OpenAPI بالإضافة إلى enum للمعلمات في الاستعلام، المسار، الرأس، وملف تعريف الكوكيز. كما يعالج items.enum لمعاملات الاستعلام.
مراقبة EventStream
connectToSwaggerEventStream({
hostname: "localhost",
port: 8788,
path: "/oauth2/for-local/get-swagger",
serviceDir: "./src/handler/service",
typesDir: "./src/handler/service/@types",
commonServiceFile: "./src/handler/service/rxjsHttpService.ts",
});طلب HTTP لمرة واحدة
await generateSwaggerFromHttp({
hostname: "localhost",
port: 8788,
path: "/oauth2/for-local/get-swagger",
});إنشاء من ملف محلي
generateSwaggerFromFile({
inputFile: "./swagger.json",
});مثال على دمج مشروع قائم: WebFlux + إنشاء تلقائي لـ Swagger
من هنا، هو مثال على دمج مشروع يستخدم Swagger الخلفي وخدمة الإنشاء التلقائي معًا. يمكن استخدام طريقة الاستخدام الأساسية والميزات الاختيارية أعلاه باستخدام typed-rx-http فقط، بينما يتم تطبيق التدفق أدناه في مشروع يستخدم إنشاء الخدمة التلقائي القائم على Swagger.
1. كتابة نقطة نهاية REST في الخلفية.
يتم كتابة كود الخلفية كما هو معتاد. في هذا المثال، يتم استلام الاسم كمتغير مسار، والرسالة كمعامل استعلام. Monoويتم الرد.
TypedRxHttpExampleRouter.java
java@Bean
public RouterFunction<ServerResponse> typedRxHttpExample(
TypedRxHttpExampleHandler handler
) {
return route()
.GET("/typed-rx-http/example/{name}", handler::getExample)
.build();
}2. إنشاء خدمة الواجهة الأمامية من Swagger.
يتم استلامها من الخلفية لإنشاء ملف النوع والخدمة. يمكن استدعاء هذه العملية مرة واحدة عند تشغيل خادم التطوير أو ربطها بسكربت المراقبة. swagger.json3. إرسال الطلب باستخدام الوظيفة التي تم إنشاؤها.
generateSwagger.cjs
jsconst {
generateSwaggerFromHttp,
} = require("@byeolnaerim/typed-rx-http/auto-node-script/openapi");
generateSwaggerFromHttp({
hostname: "localhost",
port: 8123,
path: "/for-local/get-swagger",
swaggerFile: "./swagger.json",
serviceDir: "./src/handler/service",
typesDir: "./src/handler/service/@types",
typesFile: "./src/handler/service/@types/ApiTypes.d.ts",
commonServiceFile:
"./src/handler/service/rxjsHttpService.ts",
});يمكنك تغييرها أدناه.
يمكنك الضغط على الزر لفتح شاشة التنفيذ على اليمين. بعد تغيير القيم، جرب الضغط على زر الطلب. النتيجةو الجزء المرئي في المشروع الفعلي.كود الواجهة الأمامية لا يحتوي كود الشاشة على سلسلة URL أو واجهة استجابة. كلاهما موجود في Swagger.كود الخلفية
TypedRxHttpRequestExample.tsx
tsximport { byName } from "@handler/service/auto/TypedRxHttpExampleService";
import { firstValueFrom } from "rxjs";
const response = await firstValueFrom(
byName({
path: {
name: name.trim(),
},
params: {
message: message.trim() || undefined,
},
}),
);
setResponse(response);