Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Utwórz bibliotekę klienta TypeScript/Node.js, która integruje się z zestawem Microsoft Entra SDK for AgentID w celu uzyskania tokenów i wywołania podrzędnych interfejsów API. Następnie zintegruj tego klienta z aplikacjami Express.js lub NestJS w celu obsługi uwierzytelnionych żądań interfejsu API.
Wymagania wstępne
- Konto Azure z aktywną subskrypcją. Utwórz konto bezpłatnie.
- Node.js (wersja 14 lub nowsza) z zainstalowanym programem npm na komputerze deweloperskim.
- Zestaw Microsoft Entra SDK dla AgentID wdrożony i uruchomiony w Twoim środowisku. Aby uzyskać instrukcje dotyczące instalacji, zobacz Przewodnik instalacji.
- Podrzędne interfejsy API skonfigurowane w zestawie SDK z podstawowymi adresami URL i wymaganymi zakresami.
- Odpowiednie uprawnienia w usłudze Microsoft Entra ID — Twoje konto musi mieć uprawnienia do rejestrowania aplikacji i udzielania uprawnień interfejsu API.
Konfiguracja
Przed utworzeniem biblioteki klienta zainstaluj wymagane zależności do tworzenia żądań HTTP:
npm install node-fetch
npm install --save-dev @types/node-fetch
Implementacja biblioteki klienta
Utwórz klasę klienta wielokrotnego użytku, która opakowuje wywołania HTTP do zestawu Microsoft Entra SDK for AgentID. Ta klasa obsługuje przekazywanie tokenów, konfigurację żądań i obsługę błędów:
// sidecar-client.ts
import fetch from 'node-fetch';
export interface SidecarConfig {
baseUrl: string;
timeout?: number;
}
export class SidecarClient {
private readonly baseUrl: string;
private readonly timeout: number;
constructor(config: SidecarConfig) {
this.baseUrl = config.baseUrl || process.env.SIDECAR_URL || 'http://localhost:5000';
this.timeout = config.timeout || 10000;
}
async getAuthorizationHeader(
incomingToken: string,
serviceName: string,
options?: {
scopes?: string[];
tenant?: string;
agentIdentity?: string;
agentUsername?: string;
}
): Promise<string> {
const url = new URL(`${this.baseUrl}/AuthorizationHeader/${serviceName}`);
if (options?.scopes) {
options.scopes.forEach(scope =>
url.searchParams.append('optionsOverride.Scopes', scope)
);
}
if (options?.tenant) {
url.searchParams.append('optionsOverride.AcquireTokenOptions.Tenant', options.tenant);
}
if (options?.agentIdentity) {
url.searchParams.append('AgentIdentity', options.agentIdentity);
if (options.agentUsername) {
url.searchParams.append('AgentUsername', options.agentUsername);
}
}
const response = await fetch(url.toString(), {
headers: { 'Authorization': incomingToken },
signal: AbortSignal.timeout(this.timeout)
});
if (!response.ok) {
throw new Error(`SDK error: ${response.statusText}`);
}
const data = await response.json();
return data.authorizationHeader;
}
async callDownstreamApi<T>(
incomingToken: string,
serviceName: string,
relativePath: string,
options?: {
method?: string;
body?: any;
scopes?: string[];
}
): Promise<T> {
const url = new URL(`${this.baseUrl}/DownstreamApi/${serviceName}`);
url.searchParams.append('optionsOverride.RelativePath', relativePath);
if (options?.method && options.method !== 'GET') {
url.searchParams.append('optionsOverride.HttpMethod', options.method);
}
if (options?.scopes) {
options.scopes.forEach(scope =>
url.searchParams.append('optionsOverride.Scopes', scope)
);
}
const fetchOptions: any = {
method: options?.method || 'GET',
headers: { 'Authorization': incomingToken },
signal: AbortSignal.timeout(this.timeout)
};
if (options?.body) {
fetchOptions.headers['Content-Type'] = 'application/json';
fetchOptions.body = JSON.stringify(options.body);
}
const response = await fetch(url.toString(), fetchOptions);
if (!response.ok) {
throw new Error(`SDK error: ${response.statusText}`);
}
const data = await response.json();
if (data.statusCode >= 400) {
throw new Error(`API error ${data.statusCode}: ${data.content}`);
}
return JSON.parse(data.content) as T;
}
}
// Usage
const sidecar = new SidecarClient({ baseUrl: 'http://localhost:5000' });
// Get authorization header
const authHeader = await sidecar.getAuthorizationHeader(token, 'Graph');
// Call API
interface UserProfile {
displayName: string;
mail: string;
userPrincipalName: string;
}
const profile = await sidecar.callDownstreamApi<UserProfile>(
token,
'Graph',
'me'
);
integracja Express.js
Zintegruj bibliotekę klienta z aplikacją Express.js, tworząc oprogramowanie pośredniczące w celu wyodrębnienia tokenu przychodzącego i programów obsługi tras wywołujących podrzędne interfejsy API:
import express from 'express';
import { SidecarClient } from './sidecar-client';
const app = express();
app.use(express.json());
const sidecar = new SidecarClient({ baseUrl: process.env.SIDECAR_URL! });
// Middleware to extract token
app.use((req, res, next) => {
const token = req.headers.authorization;
if (!token && !req.path.startsWith('/health')) {
return res.status(401).json({ error: 'No authorization token' });
}
req.userToken = token;
next();
});
// Routes
app.get('/api/profile', async (req, res) => {
try {
const profile = await sidecar.callDownstreamApi(
req.userToken,
'Graph',
'me'
);
res.json(profile);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.get('/api/messages', async (req, res) => {
try {
const messages = await sidecar.callDownstreamApi(
req.userToken,
'Graph',
'me/messages?$top=10'
);
res.json(messages);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(8080, () => {
console.log('Server running on port 8080');
});
Integracja z usługą NestJS
W przypadku aplikacji NestJS utwórz usługę, która opakowuje bibliotekę klienta. Tę usługę można wstrzykiwać do kontrolerów w celu obsługi uwierzytelnionych żądań:
import { Injectable } from '@nestjs/common';
import { SidecarClient } from './sidecar-client';
@Injectable()
export class GraphService {
private readonly sidecar: SidecarClient;
constructor() {
this.sidecar = new SidecarClient({
baseUrl: process.env.SIDECAR_URL!
});
}
async getUserProfile(token: string) {
return await this.sidecar.callDownstreamApi(
token,
'Graph',
'me'
);
}
async getUserMessages(token: string, top: number = 10) {
return await this.sidecar.callDownstreamApi(
token,
'Graph',
`me/messages?$top=${top}`
);
}
}
Najlepsze rozwiązania
W przypadku korzystania z zestawu Microsoft Entra SDK dla identyfikatora AgentID z języka TypeScript postępuj zgodnie z tymi rozwiązaniami, aby tworzyć niezawodne i obsługiwane aplikacje:
-
Ponowne użycie wystąpienia klienta: utwórz pojedyncze
SidecarClientwystąpienie i użyj go ponownie w całej aplikacji, zamiast tworzyć nowe wystąpienia na żądanie. Zwiększa to wydajność i użycie zasobów. - Ustaw odpowiednie limity czasu: skonfiguruj limity czasu żądania na podstawie opóźnienia podrzędnego interfejsu API. Zapobiega to zawieszaniu się aplikacji przez czas nieokreślony, jeśli zestaw SDK lub usługa podrzędna działa wolno.
- Implementowanie obsługi błędów: Dodaj prawidłową obsługę błędów i logikę ponawiania prób, szczególnie w przypadku błędów przejściowych. Rozróżnianie błędów klienta (4xx) i błędów serwera (5xx) w celu określenia odpowiednich odpowiedzi.
- Użyj interfejsów TypeScript: zdefiniuj interfejsy TypeScript dla odpowiedzi interfejsów API, aby zapewnić bezpieczeństwo typów i przechwytywać błędy w czasie kompilacji, a nie w czasie wykonywania.
- Włącz buforowanie połączeń: użyj agentów HTTP, aby umożliwić ponowne użycie połączeń między żądaniami, co zmniejsza obciążenie i zwiększa przepływność.
Inne przewodniki językowe
Dalsze kroki
Zacznij od scenariusza: