Pembuatan PDF batch melalui Connect dengan pelacakan progres
Sekilas
Bagian berjudul “Sekilas”Jalankan daftar dokumen sampai selesai dari satu proses klien melalui NextPDF Connect, distribusi layanan HTTP mandiri untuk engine ini. Resep ini mengirim setiap permintaan render ke endpoint async-job POST /api/v1/jobs, melakukan polling setiap job dengan GET /api/v1/jobs/{id} sampai mencapai status terminal, membaca bidang status dan progress yang dilaporkan server untuk setiap job, lalu mengunduh setiap PDF yang selesai dari GET /api/v1/jobs/{id}/result.
Siklus hidup job tetap dan ringkas. Sebuah job berstatus pending, lalu running, kemudian tepat satu status terminal: completed, failed, atau cancelled. Respons status membawa bilangan bulat progress dari 0 hingga 100 ketika server melacaknya, serta header Retry-After pada setiap polling non-terminal yang memberi tahu kapan Anda harus mengirim permintaan berikutnya. Berikan Idempotency-Key pada setiap pengiriman agar pengiriman ulang mengembalikan job yang sama, bukan memulai render kedua.
Resep ini bekerja di level wire. Resep ini memanggil permukaan REST secara langsung dan tidak mengasumsikan adanya software development kit (SDK) khusus bahasa, sehingga Anda dapat memindahkan alur yang sama ke klien HTTP apa pun.
Instalasi
Bagian berjudul “Instalasi”Sisi server menggunakan distribusi Connect standar:
composer require nextpdf/serverKlien PHP dalam contoh produksi di bawah ini menggunakan klien Hypertext Transfer Protocol (HTTP) dan message factory yang sesuai dengan PSR-18 dan PSR-17. Pasang implementasi yang sudah menjadi standar di proyek Anda, misalnya:
composer require psr/http-client psr/http-factoryIkhtisar konseptual
Bagian berjudul “Ikhtisar konseptual”Permukaan async-job memisahkan pengiriman dari pengambilan. Anda tidak perlu mempertahankan satu koneksi HTTP panjang yang terus terbuka untuk tiap dokumen. Sebaliknya, Anda mengirim sebuah job, menerima sebuah pengidentifikasi, dan melakukan polling ke endpoint status yang murah sampai job selesai. Pola ini membuat batch mudah dikelola: klien melacak N job independen sekaligus tanpa N koneksi yang tertahan.
Tiga endpoint membawa alur ini:
POST /api/v1/jobsmenerima badan permintaan render yang sama dengan endpoint sinkron/api/v1/render: sebuahpage_size, sebuahorientation, dan sebuah larikoperationsyang berurutan. Endpoint ini mengembalikan201 Createduntuk job baru, atau200 OKketika sebuahIdempotency-Keycocok dengan job yang sudah Anda kirim sebelumnya.GET /api/v1/jobs/{id}mengembalikan rekaman job saat ini. Untuk job non-terminal, endpoint ini juga menyetel headerRetry-After(server menggunakan interval 2 detik) dan bidangpoll_url. Patuhi header tersebut, bukan melakukan polling dalam loop yang rapat.GET /api/v1/jobs/{id}/resultmengalirkan PDF yang sudah selesai sebagaiapplication/pdf. Endpoint ini mengembalikan409 Conflictjika job belum mencapaicompleted, jadi panggil hanya setelah polling status mengonfirmasi status terminal.
Setiap respons yang berhasil berbagi satu amplop: objek data berisi bidang job, dan objek meta berisi request_id, timestamp, duration_ms, dan api_version. Bidang job yang Anda baca berada di bawah data: data.status, data.progress, data.job_id, dan pada job yang sudah selesai data.result_url.
Satu catatan untuk rilis saat ini: server memproses job yang dikirim secara inline sebelum menjawab POST. Dalam praktiknya, respons pengiriman mungkin sudah membawa status terminal, dan hasilnya mungkin sudah siap pada polling pertama. Kontrak polling-dan-progres yang didokumentasikan di sini adalah bentuk Application Programming Interface (API) yang stabil. Server mempertahankannya tanpa perubahan saat backend pemrosesan beralih ke pool worker antrean, sehingga klien yang melakukan polling sudah benar hari ini dan tetap benar setelah perubahan tersebut. Tulis loop polling. Jangan berasumsi respons pertama bersifat non-terminal, dan jangan pula berasumsi ia bersifat terminal.
Permukaan API
Bagian berjudul “Permukaan API”Dokumen OpenAPI server dan perutean JobHandler mendefinisikan permukaan REST async-job Connect:
POST /api/v1/jobs: mengirim job render. Header permintaanIdempotency-Keybersifat opsional. Badan adalah permintaan render (operationswajib ada dan harus memuat setidaknya satu operasi). Respons:201baru,200replay idempoten,422badan tidak valid,409konflik idempotensi,429pembatasan laju.GET /api/v1/jobs/{id}: polling status. Respons200dengan rekaman job; headerRetry-Afterada selama non-terminal;404jika job tidak ada atau milik klien lain.GET /api/v1/jobs/{id}/result: mengunduh PDF.200application/pdfketikacompleted;409ketika belum selesai;404jika tidak dikenal.DELETE /api/v1/jobs/{id}: membatalkan jobpendingataurunning, atau menghapus jobcompleted(204).
Rekaman job di bawah data membawa bidang-bidang ini, persis seperti yang diserialisasi server.
job_id: pengidentifikasi (awalanjob_dan 24 karakter heksadesimal).status: salah satu daripending,running,completed,failed,cancelled. Dua yang pertama bersifat non-terminal; tiga yang terakhir bersifat terminal.created_at, dan setelah disetel,started_atdancompleted_at: stempel waktu ISO-8601.progress: bilangan bulat 0 hingga 100, hanya ada ketika server melacaknya untuk job tersebut; jika tidak, bidang ini tidak ada (perlakukan sebagai tidak diketahui).error: string pesan, hanya ada pada jobfailed.result_url: hanya ada pada jobcompleted; jalur menuju unduhan hasil.poll_url: hanya ada selama job bersifat non-terminal.
Autentikasi menggunakan bearer token di header Authorization: Authorization: Bearer npk_live_{kid}_{secret}.
Contoh kode — Mulai cepat
Bagian berjudul “Contoh kode — Mulai cepat”Contoh ini menjalankan satu job dari ujung ke ujung di level wire sehingga Anda dapat melihat ketiga panggilan dan bidang yang dikembalikannya. Contoh ini mengirim, melakukan polling sekali, dan mengunduh. Contoh produksi di bawah ini menambahkan loop batch, penantian Retry-After, dan penanganan kesalahan lengkap.
# 1. Submit an async render job. Capture the job_id from data.job_id.curl -sS -X POST "$NEXTPDF_CONNECT_URL/api/v1/jobs" \ -H "Authorization: Bearer $NEXTPDF_CONNECT_TOKEN" \ -H 'Content-Type: application/json' \ -H "Idempotency-Key: invoice-2026-04-0001" \ -d '{"page_size":"A4","orientation":"portrait","operations":[{"type":"add_text","text":"Invoice 0001"}]}'
# 2. Poll status. Read data.status and data.progress; honour Retry-After.curl -sS "$NEXTPDF_CONNECT_URL/api/v1/jobs/job_0123456789abcdef01234567" \ -H "Authorization: Bearer $NEXTPDF_CONNECT_TOKEN"
# 3. Once data.status is "completed", download the PDF binary.curl -sS "$NEXTPDF_CONNECT_URL/api/v1/jobs/job_0123456789abcdef01234567/result" \ -H "Authorization: Bearer $NEXTPDF_CONNECT_TOKEN" \ -o invoice-0001.pdfContoh kode — Produksi
Bagian berjudul “Contoh kode — Produksi”Klien standalone ini mengirim sebuah batch permintaan render, membatasi jumlah job yang berjalan sekaligus, melakukan polling setiap job pada irama yang disetel server melalui Retry-After, melaporkan nilai progress yang dikembalikan server, mengunduh setiap PDF yang selesai, dan mencatat kegagalan. Klien ini menggunakan klien HTTP PSR-18 dan factory PSR-17, yaitu kontrak transport standar untuk resep Connect. Klien ini juga menangkap eksepsi paling spesifik yang dapat dilemparkan oleh tiap panggilan: Psr\Http\Client\ClientExceptionInterface untuk kegagalan transport, dan BatchJobException bertipe untuk respons server yang menghentikan batch agar tidak berlanjut. Tidak ada blok catch yang kosong. Masing-masing mencatat dan melempar ulang, atau merekam hasil yang terdefinisi.
Ganti daftar $documents inline dengan input Anda sendiri. Suntikkan klien HTTP konkret dan factory milik proyek Anda pada bagian konstruktor yang mengharapkan antarmuka PSR.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use Psr\Http\Client\ClientExceptionInterface;use Psr\Http\Client\ClientInterface;use Psr\Http\Message\RequestFactoryInterface;use Psr\Http\Message\StreamFactoryInterface;
/** * Raised when a Connect job response prevents the batch from proceeding. * * Distinct from the PSR-18 transport exception: this means the request was * delivered and the server answered, but the answer is one the batch * cannot act on (a non-success status code, or a job that ended in a * terminal failure). */final class BatchJobException extends RuntimeException{}
/** * Drives a batch of async render jobs over the NextPDF Connect REST surface. * * The client submits each render request, polls every job on the cadence * the server requests through Retry-After, and downloads each completed * PDF. It enforces bounded concurrency so a large batch never opens more * in-flight jobs than the host should track at once. */final readonly class ConnectBatchRunner{ /** * @param non-empty-string $baseUrl Connect base URL, no trailing slash * @param non-empty-string $bearerToken Connect API key (npk_live_...) * @param positive-int $maxInFlight Concurrent jobs cap * @param positive-int $maxPolls Per-job poll attempts before giving up */ public function __construct( private ClientInterface $httpClient, private RequestFactoryInterface $requestFactory, private StreamFactoryInterface $streamFactory, private string $baseUrl, private string $bearerToken, private int $maxInFlight = 8, private int $maxPolls = 150, ) {}
/** * Render every document in the batch and write each completed PDF. * * @param array<non-empty-string, array<string, mixed>> $documents * Map of stable document key to render request body. The key * doubles as the Idempotency-Key, so a re-run of the same batch * does not duplicate server-side work. * @param non-empty-string $outputDir Directory for the written PDFs * * @throws BatchJobException When the batch cannot proceed at all * @throws ClientExceptionInterface When the transport cannot send a request * * @return array<non-empty-string, string> Map of document key to a * human-readable outcome line */ public function run(array $documents, string $outputDir): array { $this->assertWritableDir($outputDir);
$outcomes = [];
// Process in bounded windows so the in-flight job count never // exceeds the configured cap, regardless of batch size. foreach (array_chunk($documents, $this->maxInFlight, preserve_keys: true) as $window) { $jobIds = [];
foreach ($window as $key => $body) { $jobIds[$key] = $this->submit($key, $body); }
foreach ($jobIds as $key => $jobId) { $record = $this->pollToTerminal($jobId); $outcomes[$key] = $this->finish($key, $record, $outputDir); } }
return $outcomes; }
/** * Submit one render job and return its identifier. * * @param non-empty-string $idempotencyKey Stable per-document key * @param array<string, mixed> $body Render request body * * @throws BatchJobException * @throws ClientExceptionInterface * * @return non-empty-string The job_id from data.job_id */ private function submit(string $idempotencyKey, array $body): string { $request = $this->requestFactory ->createRequest('POST', $this->baseUrl . '/api/v1/jobs') ->withHeader('Authorization', 'Bearer ' . $this->bearerToken) ->withHeader('Content-Type', 'application/json') ->withHeader('Idempotency-Key', $idempotencyKey) ->withBody($this->streamFactory->createStream($this->encode($body)));
$response = $this->httpClient->sendRequest($request); $status = $response->getStatusCode();
// 201 new job; 200 idempotent replay. Anything else stops the batch. if ($status !== 201 && $status !== 200) { throw new BatchJobException( sprintf('Submit for "%s" returned HTTP %d.', $idempotencyKey, $status), ); }
$data = $this->decodeData($response->getBody()->__toString()); $jobId = $data['job_id'] ?? null;
if (!is_string($jobId) || $jobId === '') { throw new BatchJobException( sprintf('Submit for "%s" returned no job_id.', $idempotencyKey), ); }
return $jobId; }
/** * Poll one job until it reaches a terminal state. * * Honours the Retry-After header on every non-terminal poll. Gives up * after maxPolls attempts and reports the wait as a failure so the * batch records it rather than blocking forever. * * @param non-empty-string $jobId * * @throws BatchJobException * @throws ClientExceptionInterface * * @return array<string, mixed> The terminal job record (data object) */ private function pollToTerminal(string $jobId): array { $url = $this->baseUrl . '/api/v1/jobs/' . rawurlencode($jobId);
for ($attempt = 0; $attempt < $this->maxPolls; $attempt++) { $request = $this->requestFactory ->createRequest('GET', $url) ->withHeader('Authorization', 'Bearer ' . $this->bearerToken);
$response = $this->httpClient->sendRequest($request); $status = $response->getStatusCode();
if ($status !== 200) { throw new BatchJobException( sprintf('Poll for job "%s" returned HTTP %d.', $jobId, $status), ); }
$data = $this->decodeData($response->getBody()->__toString()); $jobStatus = is_string($data['status'] ?? null) ? $data['status'] : 'unknown'; $progress = is_int($data['progress'] ?? null) ? $data['progress'] : null;
$this->logProgress($jobId, $jobStatus, $progress);
// Terminal states: completed, failed, cancelled. if (in_array($jobStatus, ['completed', 'failed', 'cancelled'], strict: true)) { return $data; }
// Non-terminal: wait the interval the server asked for. $this->waitRetryAfter($response->getHeaderLine('Retry-After')); }
throw new BatchJobException( sprintf('Job "%s" did not finish within %d polls.', $jobId, $this->maxPolls), ); }
/** * Act on a terminal job record: download a completed PDF, or report. * * @param non-empty-string $key Document key * @param array<string, mixed> $record Terminal job record (data object) * @param non-empty-string $outputDir Where to write the PDF * * @throws BatchJobException * @throws ClientExceptionInterface * * @return string A human-readable outcome line */ private function finish(string $key, array $record, string $outputDir): string { $jobStatus = is_string($record['status'] ?? null) ? $record['status'] : 'unknown'; $jobId = is_string($record['job_id'] ?? null) ? $record['job_id'] : '';
if ($jobStatus !== 'completed') { // A failed job carries an error message; surface it, do not swallow. $error = is_string($record['error'] ?? null) ? $record['error'] : 'no detail';
return sprintf('%s -> %s (%s)', $key, $jobStatus, $error); }
$path = rtrim($outputDir, '/\\') . DIRECTORY_SEPARATOR . $key . '.pdf'; $this->download($jobId, $path);
return sprintf('%s -> completed, written to %s', $key, $path); }
/** * Download a completed job result and write it to a server-derived path. * * @param non-empty-string $jobId * @param non-empty-string $path Caller-controlled output path * * @throws BatchJobException * @throws ClientExceptionInterface */ private function download(string $jobId, string $path): void { $request = $this->requestFactory ->createRequest('GET', $this->baseUrl . '/api/v1/jobs/' . rawurlencode($jobId) . '/result') ->withHeader('Authorization', 'Bearer ' . $this->bearerToken);
$response = $this->httpClient->sendRequest($request);
if ($response->getStatusCode() !== 200) { throw new BatchJobException( sprintf('Result download for job "%s" returned HTTP %d.', $jobId, $response->getStatusCode()), ); }
$bytes = $response->getBody()->__toString();
if (!str_starts_with($bytes, '%PDF')) { throw new BatchJobException( sprintf('Result for job "%s" is not a PDF.', $jobId), ); }
if (file_put_contents($path, $bytes) === false) { throw new BatchJobException(sprintf('Could not write result to "%s".', $path)); } }
/** * Sleep for the server-requested interval, with a safe floor and ceiling. */ private function waitRetryAfter(string $retryAfter): void { $seconds = ctype_digit($retryAfter) ? (int) $retryAfter : 2; // Clamp to a sane band so a hostile header cannot stall or busy-loop. $seconds = max(1, min(30, $seconds)); sleep($seconds); }
/** * Emit a progress line. Replace with your logger. */ private function logProgress(string $jobId, string $jobStatus, ?int $progress): void { $pct = $progress === null ? 'n/a' : $progress . '%'; fwrite(STDERR, sprintf("[%s] status=%s progress=%s\n", $jobId, $jobStatus, $pct)); }
/** * Decode a response envelope and return its data object. * * @throws BatchJobException When the body is not the expected envelope * * @return array<string, mixed> */ private function decodeData(string $json): array { try { /** @var mixed $decoded */ $decoded = json_decode($json, true, 32, JSON_THROW_ON_ERROR); } catch (JsonException $e) { throw new BatchJobException('Response body is not valid JSON.', previous: $e); }
if (!is_array($decoded) || !isset($decoded['data']) || !is_array($decoded['data'])) { throw new BatchJobException('Response is missing the data envelope.'); }
/** @var array<string, mixed> $data */ $data = $decoded['data'];
return $data; }
/** * @param array<string, mixed> $body * * @throws BatchJobException */ private function encode(array $body): string { try { return json_encode($body, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES); } catch (JsonException $e) { throw new BatchJobException('Render request body is not encodable.', previous: $e); } }
/** * @param non-empty-string $dir * * @throws BatchJobException */ private function assertWritableDir(string $dir): void { if (!is_dir($dir) || !is_writable($dir)) { throw new BatchJobException(sprintf('Output directory "%s" is not writable.', $dir)); } }}
// ---------------------------------------------------------------------------// Wiring. Provide your project's concrete PSR-18 client and PSR-17 factories.// ---------------------------------------------------------------------------
/** @var ClientInterface $httpClient *//** @var RequestFactoryInterface $requestFactory *//** @var StreamFactoryInterface $streamFactory */
$baseUrl = getenv('NEXTPDF_CONNECT_URL');$token = getenv('NEXTPDF_CONNECT_TOKEN');
if ($baseUrl === false || $baseUrl === '' || $token === false || $token === '') { fwrite(STDERR, "Set NEXTPDF_CONNECT_URL and NEXTPDF_CONNECT_TOKEN.\n"); exit(2);}
/** @var array<non-empty-string, array<string, mixed>> $documents */$documents = [ 'invoice-0001' => [ 'page_size' => 'A4', 'orientation' => 'portrait', 'operations' => [ ['type' => 'add_text', 'text' => 'Invoice 0001'], ], ], 'invoice-0002' => [ 'page_size' => 'A4', 'orientation' => 'portrait', 'operations' => [ ['type' => 'add_text', 'text' => 'Invoice 0002'], ], ],];
$runner = new ConnectBatchRunner( httpClient: $httpClient, requestFactory: $requestFactory, streamFactory: $streamFactory, baseUrl: rtrim($baseUrl, '/'), bearerToken: $token, maxInFlight: 8,);
try { $outcomes = $runner->run($documents, getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: sys_get_temp_dir());} catch (BatchJobException $e) { fwrite(STDERR, 'Batch stopped: ' . $e->getMessage() . "\n"); exit(1);} catch (ClientExceptionInterface $e) { fwrite(STDERR, 'Transport failure: ' . $e->getMessage() . "\n"); exit(1);}
foreach ($outcomes as $line) { echo $line, "\n";}STDOUT yang diharapkan adalah satu baris per dokumen. Jalurnya bergantung pada direktori keluaran Anda:
invoice-0001 -> completed, written to /tmp/invoice-0001.pdfinvoice-0002 -> completed, written to /tmp/invoice-0002.pdfKasus tepi & jebakan
Bagian berjudul “Kasus tepi & jebakan”- Baca bidang job di bawah
data, bukan di tingkat atas. Setiap respons yang berhasil dibungkus dalam amplop{ "data": ..., "meta": ... }.data.statusdandata.progressadalah bidang yang Anda gunakan;metamembawarequest_iduntuk korelasi dukungan. progressbisa tidak ada. Server menyertakanprogresshanya ketika server melacaknya untuk job tersebut. Perlakukan bidang yang hilang sebagai “tidak diketahui”, bukan sebagai nol, dan kendalikan loop Anda daristatus, yang selalu ada.- Pengiriman mungkin sudah bersifat terminal. Pada rilis saat ini, server merender secara inline sebelum menjawab
POST, sehingga respons pengiriman dapat membawastatus: completeddan hasilnya mungkin sudah siap pada polling pertama. Loop polling Anda harus menerima status terminal pada percobaan nol, bukan bersikeras menunggupendingterlebih dahulu. - Patuhi
Retry-After. Respons status non-terminal menyetelRetry-After(interval 2 detik). Polling lebih cepat menyia-nyiakan permintaan dan dapat memicu429. Batasi nilainya ke rentang yang wajar, bukan mempercayainya secara membabi buta. /resultsebelum selesai adalah409. Panggil endpoint hasil hanya setelah polling status menunjukkancompleted.409 Conflictberarti job belum selesai; ini bukan kesalahan transport.- Idempotency-Key mencegah pekerjaan ganda. Pengiriman ulang dengan kunci yang sama mengembalikan job asli (
200alih-alih201). Gunakan kunci per dokumen yang stabil agar percobaan ulang jaringan tidak pernah memulai render kedua. Kunci yang digunakan ulang dengan badan yang berbeda adalah konflik409. - Job memiliki cakupan pemilik. Job yang dikirim di bawah satu kunci API tidak terlihat oleh kunci lain;
GETlintas pemilik mengembalikan404, bukan403. Lakukan polling dengan kredensial yang sama dengan yang Anda gunakan saat mengirim. - Job
failedmembawa pesanerror. Bacadata.errorpada status terminalfaileddan rekam. Jangan mencoba ulang secara membabi buta.
Kinerja
Bagian berjudul “Kinerja”Biaya sebuah batch adalah total render ditambah overhead polling. Dua hal mengendalikan sisi klien. Pertama, batasi konkurensi: batas maxInFlight menetapkan berapa banyak job yang dilacak sekaligus, sehingga jumlah permintaan terbuka dan memori klien tetap datar tanpa memandang ukuran batch. Setel sesuai jumlah worker server, jangan lebih tinggi; lebih banyak job berjalan daripada worker hanya memperpanjang waktu antrean setiap job. Kedua, hormati interval polling: setiap polling adalah pembacaan status yang murah, tetapi loop yang rapat meningkatkan volume permintaan dan memicu pembatasan laju. Retry-After 2 detik dari server adalah default yang tepat, dan runner membatasinya ke rentang 1 hingga 30 detik sehingga satu job lambat tidak dapat melakukan busy-loop atau menghentikan window.
Untuk batch yang sangat besar, proses dalam window (runner menggunakan array_chunk) alih-alih mengirim semuanya di muka. Itu membatasi status yang dilacak klien sekaligus kedalaman antrean server, sehingga batch yang tidak valid atau terlalu besar gagal di dalam satu window, bukan setelah ribuan pengiriman.
Catatan keamanan
Bagian berjudul “Catatan keamanan”- Jaga token bearer agar tidak masuk ke log dan URL. Kunci API hanya dikirim melalui header
Authorization. Jangan pernah menempatkannya di query string, baris log, atau artefak tertulis. Runner mencatatjob_iddanstatus, tidak pernah kredensial. - Turunkan jalur keluaran dari kunci yang dikendalikan kode Anda. Runner membangun setiap jalur keluaran dari kunci dokumen yang dipilih kode Anda, digabungkan ke direktori keluaran tetap, dan tidak pernah dari nilai dalam respons server. Jangan menyisipkan bidang job ke dalam jalur sistem berkas karena itu akan membuka path traversal.
- Validasi byte yang diunduh. Runner memeriksa
200dari/resultuntuk header%PDFsebelum menulis berkas. Status unduhan yang berhasil saja tidak membuktikan bahwa badan tersebut adalah PDF. - Perlakukan hasil sebagai tidak tepercaya hingga diperiksa. Job yang selesai berarti server merender byte, bukan berarti byte tersebut aman untuk diteruskan. Jalankan hasil melalui langkah inspeksi struktural sebelum Anda menyerahkannya ke klien atau sistem hilir.
- Gunakan kunci dengan hak istimewa minimum. Permukaan async-job adalah render tier inti. Berikan batch kunci yang dicakupkan secara tepat pada operasi yang dibutuhkannya, dan rotasikan sesuai jadwal yang ditetapkan kebijakan manajemen rahasia Anda.
- Batasi anggaran polling.
maxPollsmenghentikan job yang macet agar tidak menahan klien selamanya. Batch merekam batas waktu sebagai sebuah hasil, bukan memblokir, sehingga satu job buruk tidak menghalangi layanan bagi yang lain.
Konformansi
Bagian berjudul “Konformansi”Resep ini tidak membuat klaim standar normatif. Resep ini mengonsumsi endpoint REST async-job NextPDF Connect (POST /api/v1/jobs, GET /api/v1/jobs/{id}, GET /api/v1/jobs/{id}/result) dan membaca bidang rekaman job yang didefinisikan server (status, progress, error, result_url, poll_url). Pemeriksaan header %PDF pada hasil yang diunduh hanya mengonfirmasi bahwa respons diawali dengan penanda PDF; ini bukan penilaian validitas atau konformansi. Untuk pemeriksaan standar di sekumpulan dokumen, gunakan alat kepatuhan batch Enterprise. Lihat Pemeriksaan standar batch melalui Connect, permukaan yang berbeda dari job rendering yang dibahas di sini.
Lihat juga
Bagian berjudul “Lihat juga”- Hello world melalui Connect: render tunggal paling sederhana sebelum Anda membuat batch.
- Konvensi resep Connect: kontrak transport, autentikasi, dan konformansi yang digunakan bersama oleh setiap resep Connect.
- Penanganan kesalahan yang sadar eksepsi melalui Connect: bagaimana server melaporkan kesalahan dan bagaimana klien semestinya bereaksi.
- Pemeriksaan standar batch melalui Connect: permukaan kepatuhan Enterprise, berbeda dari job render ini.