Blazor ServerでSSRSレポートをPDF出力する方法|REST APIでHTTP 400になる原因とURL Access解決策

Blazor Server から SSRS(.rdl)レポートを「REST API で呼び出して PDF を返したい」と思い、/ReportServer/api/v2.0/Reports までは取得できたのに、Export で HTTP 400…というケースは珍しくありません。結論は、SSRS の REST API はレンダリング用途ではなく、PDF 出力は URL Access(rs:Format=PDF)で行うのが現実解です。

目次

結論:SSRS の REST API(v2.0)は「PDF をレンダリングして返す」用途に向かない

SSRS 2017 以降には REST API(/ReportServer/api/v2.0/…)が用意されていますが、主目的は「レポートサーバーのカタログ(フォルダー、レポート、データソース等)をプログラムから操作すること」です。つまり、一覧取得・作成・更新・削除・定義のダウンロードなどには使えても、Report Builder で作った .rdl を実行して PDF を返す“実行エンジン”としての API ではありません。

そのため、次のような URL を叩いて「PDF が返るはず」と期待しても、HTTP 400(Bad Request)に到達しやすく、Blazor 側で PDF をダウンロードできません。Microsoft Q&A でも、REST API での PDF レンダリング手段は用意されていない旨が案内されています。

https://localhost:440/ReportServer/api/v2.0/Reports(ItemPath='/BlazorSQL-Reports/Details')/Export(Format='PDF')

「/Reports が取れるのに /Export が 400」は、アプリ実装の問題というより“API の性格の違い”が原因です。ここを割り切ると、解決への最短ルートが見えてきます。

なぜレポート一覧は取得できるのに、Export で HTTP 400 になるのか

REST API は“カタログ管理 API”として設計されている

SSRS の REST API は、レポートサーバーカタログにあるオブジェクト(Reports / Folders / DataSources / Datasets / Subscriptions など)を CRUD するための仕組みとして説明されています。たとえば「フォルダーを辿る」「レポート定義をダウンロードする」「オブジェクトを作成・更新・削除する」といった用途です。

一方で、.rdl を“実行”して PDF・Excel に“レンダリング”するのは、別系統の仕組み(URL Access や SOAP の ReportExecution など)で実現する世界観です。REST API のエンドポイント名に Export が見えても、それが「従来のエクスポート(=レンダリング)」と一致するとは限りません。

「ReportServer」と「Reports」を取り違えると、URL が全部ズレる

SSRS には、よく似た URL が複数あります。ここを混同すると「一覧は見えるのに、実行ができない」「開くとポータルに飛ぶ」といった挙動になりがちです。

URL の例役割Blazor での使いどころ
https://<server>/ReportServer/…レポート実行・URL Access・API の入口(本体)PDF 出力や API 呼び出しで主に使う
https://<server>/Reports/…Web ポータル(人が操作する UI)埋め込み用途としては原則非推奨(UI が変わりやすい)

Export(Format=’PDF’) という形は、SSRS 側が想定していない可能性が高い

今回のように OData 風のパスで Export を呼ぶ場合、そもそもそのリソース・メソッドが“Report(.rdl)に対しては”提供されていない、あるいはパラメータの形が異なる、といった理由で 400 になり得ます。Microsoft 側からも「REST API で PDF をレンダリングする方法はない」旨の回答が出ているため、アプリ側でリトライやヘッダー調整を頑張っても、根本解決しないケースがほとんどです。

まずは「できること/できないこと」を表で整理する

やりたいことREST API(/api/v2.0)URL Access(rs:Format)おすすめの選択
レポート一覧を取得したい得意(GET /Reports 等)不得意(一覧用途ではない)REST API
.rdl 定義をダウンロードしたい可能(カタログ操作の範囲)不向きREST API
特定レポートを実行して PDF を返したい基本的に不向き(その用途の仕組みが不足)得意(rs:Format=PDF で直接レンダリング)URL Access

PDF 出力の正攻法:URL Access(rs:Format=PDF)を使う

SSRS には昔から「URL Access」という仕組みがあり、URL にパラメータを付けるだけでレポートの表示・レンダリング形式の指定ができます。Microsoft Learn でも、rs:Format を使って PDF を含む各形式に出力できることが明記されています。

最小構成:PDF を返す URL 例(ネイティブモード想定)

http://localhost:440/ReportServer?/BlazorSQL-Reports/Details&rs:Command=Render&rs:Format=PDF

この URL をブラウザで開けるなら、Blazor Server 側も同じ URL を“開く”か“サーバー側で取得して中継する”だけで、PDF の表示・ダウンロードが実現できます。

ReportViewer.aspx を使うパターン(埋め込みで UI を調整したいとき)

環境によっては、次のように ReportViewer.aspx を経由した方が「パスの書き方が安定する」「rc:Toolbar など UI 系パラメータが効かせやすい」ことがあります。特にレポート名やフォルダー名にスペースが入る場合、%2f 形式(URL エンコード)を使うのが定石です。

http://localhost:440/ReportServer/Pages/ReportViewer.aspx?%2fBlazorSQL-Reports%2fDetails&rs:Command=Render&rs:Format=PDF&rc:Toolbar=false

rs:Format で指定できる主な形式

rs:Format は、PDF 以外にも Excel(xlsx)や Word(docx)など、サーバーに入っているレンダリング拡張に応じた形式を受け付けます。代表的な値として PDF、EXCELOPENXML、WORDOPENXML などが挙げられています。

Blazor Server での実装パターン(埋め込み+PDF 取得)

Blazor Server はサーバーサイドで .NET が動くため、ブラウザに直接 SSRS の認証情報を持たせずに実装しやすいのが強みです。要件(社内 AD 認証か、外部公開か、PDF を“表示”したいのか“ダウンロード”したいのか)に合わせて、次のパターンから選ぶと整理しやすくなります。

パターン概要メリット注意点
リンクで開く(新規タブ/同一タブ)URL Access の PDF URL をそのまま開く実装が最小、SSRS 標準動作に乗れる認証が別ドメインだとユーザー体験が悪化しやすい
iframe で埋め込むPDF URL または ReportViewer ページを iframe に設定「画面内で完結」しやすいSameSite/Cookie/認証/ヘッダー制限で詰まることがある
Blazor(ASP.NET Core)でプロキシして返すサーバー側で SSRS から PDF を取得し、同一オリジンで返すCORS・認証・URL 露出をコントロールしやすい実装量が増える。権限制御と負荷対策が必要

実装例:特定レポート(Details)を PDF として返す“中継エンドポイント”

「特定レポートだけを PDF で取得したい」という要件に最もハマるのが、Blazor Server(=ASP.NET Core)側に中継 API を作る方法です。ポイントは次の通りです。

  • ユーザーには /ssrs/details.pdf のような“自アプリの URL”だけを見せる
  • サーバー側で URL Access を呼び、PDF バイナリをそのままストリーミングする
  • 任意のレポートパスを渡せないようにし、必要なレポートだけ許可する(重要)

Minimal API でのサンプル(概念実装)

下記は考え方を掴むための簡易例です。実運用では IHttpClientFactory を使う、SSRS 側の認証方式に合わせる、監査ログを残す等の補強を推奨します。

using System.Text;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// SSRS の ReportServer URL(例): https://localhost:440/ReportServer
var ssrsReportServerBase = "https://localhost:440/ReportServer";

// 「Details」だけを許可する(ユーザー入力で自由に変えられないようにする)
var detailsItemPath = "/BlazorSQL-Reports/Details";

app.MapGet("/ssrs/details.pdf", async (HttpContext context) =>
{
    // 例:レポートパラメータをクエリから受け取る(必要なものだけ許可する)
    var orderId = context.Request.Query["orderId"].ToString();

    // URL Access の URL を組み立て(レポートパスは URL エンコードして安全に)
    var encodedPath = Uri.EscapeDataString(detailsItemPath);
    var sb = new StringBuilder();
    sb.Append(ssrsReportServerBase);
    sb.Append('?');
    sb.Append(encodedPath);
    sb.Append("&rs:Command=Render&rs:Format=PDF");

    if (!string.IsNullOrWhiteSpace(orderId))
    {
        sb.Append("&OrderId=");
        sb.Append(Uri.EscapeDataString(orderId));
    }

    var ssrsUrl = sb.ToString();

    // Windows 認証(同一ドメイン内で SSRS にアクセスできる前提の例)
    // 環境により、UseDefaultCredentials ではなく NetworkCredential が必要な場合もあります。
    var handler = new HttpClientHandler
    {
        UseDefaultCredentials = true,
    };

    using var http = new HttpClient(handler);

    using var resp = await http.GetAsync(ssrsUrl, HttpCompletionOption.ResponseHeadersRead);
    if (!resp.IsSuccessStatusCode)
    {
        context.Response.StatusCode = (int)resp.StatusCode;
        await context.Response.WriteAsync($"SSRS returned {(int)resp.StatusCode}");
        return;
    }

    context.Response.ContentType = "application/pdf";
    context.Response.Headers["Content-Disposition"] = "inline; filename=Details.pdf";

    await resp.Content.CopyToAsync(context.Response.Body);
});

app.Run();

Blazor 側(ページ)から呼び出す例

同一オリジン(自アプリ内)の URL なので、PDF を “表示” したい場合は iframe、 “ダウンロード” したい場合はリンクや JS で開くだけです。

@page "/details-pdf"

<h1>Details レポート(PDF)</h1>

<p>
  <a href="/ssrs/details.pdf?orderId=123" target="_blank" rel="noopener">
    PDF を開く
  </a>
</p>

<iframe src="/ssrs/details.pdf?orderId=123"
        style="width:100%; height:80vh; border:1px solid #ccc;">
</iframe>

パラメータ付きレポートの PDF を“安全に”生成するコツ

URL Access では「レポートパラメータ」は通常のクエリとして渡します(例:&OrderId=123)。便利な反面、ユーザーが自由にパラメータを変えられると、意図しないデータ閲覧につながる可能性があります。中継エンドポイントを作る場合は、次の観点でガードすると安全です。

  • 受け付けるパラメータを固定し、型(数値/日付/列挙)で検証する
  • ログインユーザーの権限に応じて、許可された値だけに絞る(例:自部署の OrderId のみ)
  • “レポートパス”を外部入力にしない(今回の Details のように固定する)

URL Access のパラメータ整理(rs: と rc: を使い分ける)

URL Access のパラメータは大きく rs:(レポートサーバー向け)と rc:(HTML Viewer 向け)に分かれます。Microsoft Learn のパラメータリファレンスでも、接頭辞の違いが説明されています。

接頭辞例用途メモ
rs:rs:Command=Renderサーバー側の実行・レンダリング制御Render は表示/出力の基本コマンド
rs:rs:Format=PDF出力形式の指定PDF のほか xlsx/docx なども指定可能
rc:rc:Toolbar=falseHTML Viewer の見た目制御埋め込みで“見せたくない UI”を消すときに便利
rc:rc:Parameters=falseパラメータ領域の表示制御埋め込みでパラメータ欄を隠したいときに使う

よくある落とし穴と対処法(HTTP 400 以外も含む)

URL Access に切り替えると 400 問題は解消しやすい一方で、今度は認証や URL の組み立てで詰まりがちです。現場で遭遇しやすいポイントを、原因→対処でまとめます。

症状主な原因対処
HTTP 400(URL Access 側でも 400)レポートパスの書き方/URL エンコードが不正、必須パラメータ不足ReportServer の URL(/ReportServer と /Reports の違い)を再確認し、パスは %2f 形式でエンコードして試す
HTTP 401 / 403SSRS の認証(Windows 認証など)を通過できていない同一ドメイン/同一ゾーンでの統合認証、またはサーバー側プロキシで資格情報を管理する
PDF は返るが iframe で真っ白/表示されないブラウザの PDF ビューア設定、Mixed Content、iframe 制限https 統一、Content-Disposition を inline にする、新規タブ表示に切り替える
特定のユーザーだけ表示できないSSRS 側のフォルダー/レポート権限、データソース資格情報SSRS のセキュリティ(Browser/Content Manager 等)と、データソースの資格情報設定を見直す
負荷が高く、タイムアウト/メモリ不足になりやすいレポートが重い、同時実行が多い、サーバーリソース不足中継側でアクセス制限(同時数/頻度)を入れる、必要ならキャッシュや非同期生成を検討する

補足:将来的に REST API で“レンダリング”できるようになる可能性は?

SSRS は新バージョンで改善が続いており、REST API のサポート自体も「OpenAPI 準拠の RESTful API」として案内されています。また、SQL Server 2022 Reporting Services(SSRS 2022)の提供についても公式に触れられています。

ただし、現時点の公開情報や Q&A の回答を見る限り、REST API を “レポート実行→PDF 返却” の主ルートとして期待するよりも、URL Access を使う設計の方が堅実です。将来の方針は SQL Server/SSRS のリリースノートに依存するため、要件が長期運用前提なら「URL Access+アプリ側の中継」で吸収できる作りにしておくと、仕様変更にも追従しやすくなります。

まとめ:Blazor Server × SSRS で「特定レポートを PDF 取得」する最短ルート

  • SSRS REST API(v2.0)はカタログ操作向けで、PDF レンダリング用途には向きにくい
  • PDF を返したいなら URL Access(rs:Format=PDF)を使う
  • Blazor Server では「中継エンドポイント」を作ると、認証・CORS・URL 露出をまとめて解決しやすい
  • 特定レポートだけ許可(allowlist)し、必要なパラメータだけを受け付ける設計にすると安全

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次