İlerleme takibiyle Connect üzerinden toplu PDF üretimi
Bir bakışta
“Bir bakışta” başlıklı bölümBelge listesini tek bir istemci sürecinden, NextPDF Connect üzerinden (motorun bağımsız HTTP servis dağıtımı) tamamlanana kadar çalıştırın. Bu tarif, her işleme isteğini zaman uyumsuz iş uç noktası POST /api/v1/jobs üzerinden gönderir, her işi GET /api/v1/jobs/{id} ile uç duruma ulaşana kadar yoklar, sunucunun her iş için bildirdiği status ve progress alanlarını okur ve tamamlanan her PDF’yi GET /api/v1/jobs/{id}/result uç noktasından indirir.
İş yaşam döngüsü sabit ve küçüktür. Bir iş önce pending, ardından running olur ve sonunda tam olarak tek bir uç duruma geçer: completed, failed veya cancelled. Sunucu bu değeri izliyorsa durum yanıtı 0 ile 100 arasında bir progress tam sayısı taşır ve uç durumda olmayan her yoklamada, bir sonraki isteği ne zaman göndereceğinizi bildiren bir Retry-After üst bilgisi içerir. Her gönderimi bir Idempotency-Key ile anahtarlayın; böylece yeniden denenen bir gönderim, ikinci bir işleme işi başlatmak yerine aynı işi döndürür.
Bu tarif, kablo düzeyindeki yolu kullanır. REST yüzeyini doğrudan çağırır ve dile özgü bir SDK varsaymaz; bu nedenle aynı akışı herhangi bir HTTP istemcisine taşıyabilirsiniz.
Kurulum
“Kurulum” başlıklı bölümSunucu tarafı standart Connect dağıtımını kullanır:
composer require nextpdf/serverAşağıdaki üretim örneğindeki PHP istemcisi, PSR-18 ve PSR-17 uyumlu bir HTTP istemcisi ve ileti fabrikaları kullanır. Projenizin standart olarak kullandığı implementasyonları kurun; örneğin:
composer require psr/http-client psr/http-factoryKavramsal genel bakış
“Kavramsal genel bakış” başlıklı bölümZaman uyumsuz iş yüzeyi, gönderim ile alma işlemlerini birbirinden ayırır. Belge başına uzun süre açık kalan bir HTTP bağlantısı tutmazsınız. Bunun yerine bir iş gönderir, bir tanımlayıcı alır ve iş bitene kadar ucuz bir durum uç noktasını yoklarsınız. Bu yapı toplu işi yönetilebilir kılar: istemci, N adet bloke bağlantı tutmadan N adet bağımsız işi aynı anda izler.
Akışı üç uç nokta taşır:
POST /api/v1/jobs, eşzamanlı/api/v1/renderuç noktasıyla aynı işleme isteği gövdesini kabul eder: birpage_size, birorientationve sıralı biroperationsdizisi. Yeni bir iş için201 Createddöndürür;200 OKyanıtı ise birIdempotency-Key, daha önce gönderdiğiniz bir işle eşleştiğinde döner.GET /api/v1/jobs/{id}, geçerli iş kaydını döndürür. Uç durumda olmayan bir iş için ayrıca birRetry-Afterüst bilgisi (sunucu 2 saniyelik bir aralık kullanır) ve birpoll_urlalanı ayarlar. Sıkı döngüde yoklamak yerine bu üst bilgiye uyun.GET /api/v1/jobs/{id}/result, tamamlanan PDF’yiapplication/pdfolarak akış halinde gönderir. İş409 Conflictdöndürürse iş henüzcompleteddurumuna ulaşmamış demektir; bu nedenle bu uç noktayı yalnızca durum yoklaması uç durumu onayladıktan sonra çağırın.
Başarılı her yanıt aynı zarfı paylaşır: iş alanlarını içeren bir data nesnesi ve şunları içeren bir meta nesnesi: request_id, timestamp, duration_ms ve api_version. Okumanız gereken iş alanları data altında bulunur: data.status, data.progress, data.job_id ve tamamlanan bir işte data.result_url.
Geçerli sürüm için bir uyarı: sunucu, gönderilen bir işi POST yanıtını vermeden önce satır içi olarak işler. Uygulamada gönderim yanıtı zaten uç bir status taşıyabilir ve sonuç ilk yoklamada hazır olabilir. Burada belgelenen yoklama ve ilerleme sözleşmesi kararlı API yapısıdır. Sunucu, işleme arka ucu kuyruğa alınmış bir çalışan havuzuna geçerken bu yapıyı değiştirmeden korur; bu nedenle yoklama yapan bir istemci bugün doğru çalışır ve bu değişiklikten sonra da doğru kalır. Yoklama döngüsünü yazın. İlk yanıtın uç durumda olmadığını varsaymayın; uç durumda olduğunu da varsaymayın.
API yüzeyi
“API yüzeyi” başlıklı bölümSunucu OpenAPI belgesi ve JobHandler yönlendirmesi, Connect zaman uyumsuz iş REST yüzeyini tanımlar:
POST /api/v1/jobs: bir işleme işi gönderir. İsteğe bağlıIdempotency-Keyistek üst bilgisi. Gövde bir işleme isteğidir (operationszorunludur ve en az bir işlem içermelidir). Yanıtlar:201yeni,200idempotent yeniden yürütme,422geçersiz gövde,409idempotency çakışması,429hız sınırlandırması.GET /api/v1/jobs/{id}: durumu yoklar. İş kaydını içeren200yanıtı; uç durumda değilkenRetry-Afterüst bilgisi bulunur; iş yoksa veya başka bir istemciye aitse404.GET /api/v1/jobs/{id}/result: PDF’yi indirir. İş tamamlandığında200application/pdf; henüzcompleteddeğilse409; bilinmiyorsa404.DELETE /api/v1/jobs/{id}: birpendingveyarunningişi iptal eder veya bircompletedişi siler (204).
İş kaydı, data altında, sunucunun seri hale getirdiği şekliyle tam olarak şu alanları taşır.
job_id: tanımlayıcı (birjob_öneki ve 24 onaltılık karakter).status: şunlardan biri:pending,running,completed,failed,cancelled. İlk ikisi uç durumda değildir; son üçü uç durumdadır.created_atve ayarlandıysastarted_atvecompleted_at: ISO-8601 zaman damgaları.progress: 0 ile 100 arasında bir tam sayı, yalnızca sunucu bu iş için bu değeri izlediğinde bulunur; aksi halde yoktur (bilinmeyen olarak değerlendirin).error: bir ileti dizesi, yalnızcafailedbir işte bulunur.result_url: yalnızcacompletedbir işte bulunur; sonuç indirmesine giden yol.poll_url: yalnızca iş uç durumda değilken bulunur.
Kimlik doğrulama, Authorization üst bilgisindeki bir Bearer belirtecidir: Authorization: Bearer npk_live_{kid}_{secret}.
Kod örneği — hızlı başlangıç
“Kod örneği — hızlı başlangıç” başlıklı bölümBu örnek, tek bir işi kablo düzeyinde uçtan uca çalıştırır; böylece üç çağrıyı ve döndürdükleri alanları görebilirsiniz. İşi gönderir, bir kez yoklar ve indirir. Aşağıdaki üretim örneği toplu döngüyü, Retry-After beklemesini ve eksiksiz hata işlemeyi ekler.
# 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.pdfKod örneği — üretim
“Kod örneği — üretim” başlıklı bölümBu bağımsız istemci, bir grup işleme isteği gönderir, aynı anda kaç işin işlemde olduğunu sınırlar, her işi sunucunun Retry-After ile belirlediği aralıkta yoklar, sunucunun döndürdüğü progress değerini bildirir, tamamlanan her PDF’yi indirir ve başarısızlıkları kaydeder. Connect tariflerinin standart olarak kullandığı aktarım sözleşmesi olan bir PSR-18 HTTP istemcisi ve PSR-17 fabrikaları kullanır. Ayrıca her çağrının ortaya çıkarabileceği en özgül istisnayı yakalar: aktarım hatası için Psr\Http\Client\ClientExceptionInterface ve toplu işin devam etmesini engelleyen bir sunucu yanıtı için tipli bir BatchJobException. Hiçbir catch bloğu boş değildir. Her biri günlüğe kaydeder ve yeniden fırlatır ya da tanımlı bir sonucu kaydeder.
Satır içi $documents listesini kendi girdilerinizle değiştirin. Yapıcının PSR arayüzleri beklediği yerlere projenizin somut HTTP istemcisini ve fabrikalarını enjekte edin.
<?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";}Beklenen STDOUT, belge başına bir satırdır. Yollar çıktı dizininize bağlıdır:
invoice-0001 -> completed, written to /tmp/invoice-0001.pdfinvoice-0002 -> completed, written to /tmp/invoice-0002.pdfSınır durumları ve dikkat edilecek noktalar
“Sınır durumları ve dikkat edilecek noktalar” başlıklı bölüm- İş alanlarını üst seviyede değil,
dataaltında okuyun. Başarılı her yanıt bir{ "data": ..., "meta": ... }zarfına sarılır. Üzerinde işlem yaptığınız alanlardata.statusvedata.progressalanlarıdır;meta, destek ilişkilendirmesi içinrequest_idtaşır. progressbulunmayabilir. Sunucuprogressalanını yalnızca o iş için izliyorsa içerir. Eksik bir alanı sıfır değil “bilinmeyen” olarak değerlendirin ve döngünüzü her zaman bulunanstatusüzerinden yürütün.- Gönderim zaten uç durumda olabilir. Geçerli sürümde sunucu,
POSTyanıtını vermeden önce satır içi olarak işler; bu nedenle gönderim yanıtıstatus: completedtaşıyabilir ve sonuç ilk yoklamada hazır olabilir. Yoklama döngünüz, ilk başta birpendingdurumunda ısrar etmek yerine, sıfırıncı denemede bir uç durumu kabul etmelidir. Retry-Afterüst bilgisine uyun. Uç durumda olmayan durum yanıtlarıRetry-Afterayarlar (2 saniyelik bir aralık). Daha hızlı yoklama, istekleri boşa harcar ve bir429yanıtına yol açabilir. Değere körü körüne güvenmek yerine onu makul bir aralığa sıkıştırın.- Tamamlanmadan önce
/resultçağrısı bir409verir. Sonuç uç noktasını yalnızca durum yoklamasıcompletedgösterdikten sonra çağırın. Bir409 Conflict, işin bitmediği anlamına gelir; bir aktarım hatası değildir. - Idempotency-Key yinelenen işin oluşmasını önler. Aynı anahtarla yeniden denenen bir gönderim, özgün işi döndürür (
200,201yerine). Bir ağ yeniden denemesi asla ikinci bir işleme işi başlatmayacak şekilde, belge başına kararlı bir anahtar kullanın. Farklı bir gövdeyle yeniden kullanılan bir anahtar, bir409çakışmasıdır. - İşler sahip kapsamındadır. Bir API anahtarı altında gönderilen bir iş, başka bir anahtara görünmezdir; sahipler arası bir
GET,404döndürür,403değil. Gönderimde kullandığınız kimlik bilgisiyle yoklayın. - Bir
failediş, birerroriletisi taşır.data.erroralanını, uç birfaileddurumunda okuyun ve kaydedin. Körü körüne yeniden denemeyin.
Performans
“Performans” başlıklı bölümBir toplu işin maliyeti, işlemelerin toplamı artı yoklama yüküdür. İstemci tarafında iki kaldıracı denetlersiniz. İlk olarak, eşzamanlılığı sınırlayın: maxInFlight sınırı, aynı anda kaç işin izlendiğini belirler ve böylece istemcinin açık istek sayısını ve belleğini, toplu iş boyutundan bağımsız olarak sabit tutar. Bunu sunucunun çalışan sayısıyla eşleşecek şekilde ayarlayın, daha yüksek değil; çalışanlardan daha fazla işin işlemde olması yalnızca her işin kuyrukta beklemesini uzatır. İkinci olarak, yoklama aralığına uyun: her yoklama ucuz bir durum okumasıdır, ancak sıkı bir döngü istek hacmini artırır ve hız sınırlayıcıyı tetikler. Sunucunun 2 saniyelik Retry-After değeri doğru varsayılandır ve çalıştırıcı, tek bir yavaş işin meşgul döngüye girememesi veya pencereyi durduramaması için bunu 1 ila 30 saniyelik bir banta sıkıştırır.
Çok büyük toplu işler için her şeyi en başta göndermek yerine pencereler halinde işleyin (çalıştırıcı array_chunk kullanır). Bu, hem istemcinin izlediği durumu hem de sunucunun kuyruk derinliğini sınırlar; böylece hatalı biçimli veya aşırı büyük bir toplu iş, binlerce gönderimden sonra değil, tek bir pencere içinde başarısız olur.
Güvenlik notları
“Güvenlik notları” başlıklı bölüm- Bearer belirtecini günlüklerden ve URL’lerden uzak tutun. API anahtarı yalnızca
Authorizationüst bilgisinde taşınır. Onu asla bir sorgu dizesine, bir günlük satırına veya yazılı bir yapıta koymayın. Çalıştırıcı,job_idvestatusalanlarını günlüğe kaydeder, kimlik bilgisini asla kaydetmez. - Çıktı yollarını sunucunun denetlediği anahtarlardan türetin. Çalıştırıcı, her çıktı yolunu sunucu yanıtındaki bir değerden değil, kodunuzun seçtiği belge anahtarından sabit bir çıktı dizinine ekleyerek oluşturur. Bir iş alanını bir dosya sistemi yoluna eklemeyin; bu bir yol geçişi (path traversal) açığı oluşturur.
- İndirilen baytları doğrulayın. Çalıştırıcı, dosyayı yazmadan önce bir
200yanıtının/resultuç noktasından geldiğini ve%PDFüst bilgisini taşıdığını denetler. Başarılı bir indirme durumu, tek başına gövdenin bir PDF olduğunun kanıtı değildir. - Sonucu, incelenene kadar güvenilmez olarak değerlendirin. Tamamlanan bir iş, sunucunun bayt işlediği anlamına gelir; bu baytları iletmenin güvenli olduğu anlamına gelmez. Sonuçları bir istemciye veya akış aşağı bir sisteme teslim etmeden önce yapısal bir inceleme adımından geçirin.
- En az ayrıcalıklı bir anahtar kullanın. Zaman uyumsuz iş yüzeyi, çekirdek katmanlı (core-tier) işlemedir. Toplu işe yalnızca ihtiyaç duyduğu işlemleri kapsayan bir anahtar verin ve onu, gizli bilgi yönetimi politikanızın belirlediği takvimde değiştirin.
- Yoklama bütçesini sınırlayın.
maxPolls, takılı kalan bir işin istemciyi sonsuza kadar tutmasını engeller. Toplu iş, engellenmek yerine zaman aşımını bir sonuç olarak kaydeder; böylece tek bir kötü iş diğerlerine hizmet verilmesini engellemez.
Uygunluk
“Uygunluk” başlıklı bölümBu tarif, normatif bir standart iddiası ortaya koymaz. NextPDF Connect zaman uyumsuz iş REST uç noktalarını (POST /api/v1/jobs, GET /api/v1/jobs/{id}, GET /api/v1/jobs/{id}/result) tüketir ve sunucunun tanımladığı iş kaydı alanlarını (status, progress, error, result_url, poll_url) okur. İndirilen bir sonuçtaki %PDF üst bilgisi denetimi, yalnızca yanıtın PDF işaretçisiyle başladığını doğrular; bu bir geçerlilik veya uygunluk belirlemesi değildir. Bir belge kümesi genelinde standart denetimi için Enterprise toplu uygunluk aracını kullanın. Bkz. Connect üzerinden toplu standart denetimi; burada ele alınan işleme işlerinden farklı bir yüzeydir.
Ayrıca bakın
“Ayrıca bakın” başlıklı bölüm- Connect üzerinden merhaba dünya: toplu işlemeye geçmeden önceki en küçük tekil işleme örneği.
- Connect tarif kuralları: her Connect tarifinin paylaştığı aktarım, kimlik doğrulama ve uygunluk sözleşmesi.
- Connect üzerinden istisna duyarlı hata işleme: sunucunun hataları nasıl bildirdiği ve bir istemcinin nasıl tepki vermesi gerektiği.
- Connect üzerinden toplu standart denetimi: bu işleme işlerinden farklı olan Enterprise uygunluk yüzeyi.