はじめに
Delphi は XE5 (2013 年) において REST (Representational State Transfer) クライアントライブラリが実装されたのですが、
DataSnap という文字列を見て 「あぁ、Professional Edition では使えないのか」 と勝手に思い込んでいました。
そんなことはなかったぜ! (今更感)
REST クライアントライブラリの使い方
概要は米澤さんの記事をどうぞ。
Qiita の記事を取得するコードの生成
Qiita には REST API が用意されていますので、ライブラリの使用例として、私が書いた Qiita の記事を取得してみます。
使用する Qiita の API は GET /api/v2/users/:user_id/items です。記事一覧を取得する API になります。:user_id はプレースホルダーで、実際には Qiita のユーザーID が入ります。
REST クライアントライブラリのドキュメントが若干分かりにくいので、まずは REST Debugger を使ってみます。Delphi IDE の [ツール | REST デバッガ] または、$(BDS)\BIN\ にある RESTDebugger.exe を直接起動します。
・[要求] タブ
次のように指定します。
| 項目 | 値 |
|---|---|
| メソッド | GET |
| URL | https://qiita.com |
| コンテンツタイプ | application/json |
・[パラメータ] タブ
次のように指定します。
| 項目 | 値 | 備考 |
|---|---|---|
| リソース | /api/v2/users/ht_deko/items | ht_deko は私の Qiita ユーザーID |
| 要求パラメータ | [GET/POST /E] page=1 [GET/POST /E] per_page=10 |
とりあえず 10 件取得してみる設定 |
要求パラメータは [追加] ボタンを押して追加します。
・実行
[要求の送信] ボタンを押すと応答が返ります。
[コンポーネントのコピー] ボタンを押すとこの接続のコンポーネントがクリップボードにコピーされます。
object RESTClient1: TRESTClient
BaseURL = 'https://qiita.com'
Params = <>
end
object RESTRequest1: TRESTRequest
AssignedValues = [rvConnectTimeout, rvReadTimeout]
Client = RESTClient1
Params = <
item
Name = 'page'
Value = '1'
end
item
Name = 'per_page'
Value = '10'
end>
Resource = 'api/v2/users/ht_deko/items'
Response = RESTResponse1
end
object RESTResponse1: TRESTResponse
end
普通はこれを VCL フォームや FMX フォームに貼り付けて使う事になります。
REST クライアントの最小限のコード
要は TRESTClient / TRESTRequest / TRESTResponse があればいいので、
uses
..., REST.Types, REST.Client;
...
var Request := TRESTRequest.Create(nil);
try
Request.Client := TRESTClient.Create(Request);
Request.Response := TRESTResponse.Create(Request);
{ 要求 (リクエスト) に関するパラメータ等の処理 }
...
Request.Execute;
{ 応答 (レスポンス) の処理 }
...
finally
Request.Free;
end;
こんな感じのコードで REST API を扱える事になります。コンポーネントクラス (TComponent) の派生なので、TRESTRequest / TRESTResponse の破棄を TRESTRequest に任せる事ができます。
See also:
- REST.Client.TRESTClient (DocWiki)
- REST.Client.TRESTRequest (DocWiki)
- REST.Client.TRESTResponse (DocWiki)
具体的な使用例
以前、Qiita の記事をバックアップするコードを書いた事があります。
記事内のコードを REST クライアントライブラリ対応に書き換えてみました。
program GetQiitaItems;
{$APPTYPE CONSOLE}
{$WARN GARBAGE OFF}
uses
System.SysUtils, System.Classes, System.Rtti, System.IOUtils, System.RegularExpressions,
System.Net.HttpClientComponent, REST.Client, REST.Types, System.JSON.Readers, System.JSON.Builders;
const
EXP1 = 'https://qiita-image-store\.s3\..*amazonaws\.com/0/.+/(?<filename>.+\.(png|gif|jpg))';
EXP2 = 'https://qiita\.com/%s/(items|private)/(?<filename>[0-9a-f]+)';
PER_PAGE = 100;
type
{ TReplaceMethodClass }
TReplaceMethodClass = class
function NewPath(const AMatch: TMatch): String;
function RelativePath(const AMatch: TMatch): String;
end;
function TReplaceMethodClass.NewPath(const AMatch: TMatch): String;
begin
result := './images/' + AMatch.Groups.Item['filename'].Value;
end; { NewPath }
function TReplaceMethodClass.RelativePath(const AMatch: TMatch): String;
begin
result := './' + AMatch.Groups.Item['filename'].Value + '.md';
end; { RelativePath }
begin
if ParamCount < 1 then
begin
Writeln('Usage:');
Writeln(TPath.GetFileNameWithoutExtension(ParamStr(0)), ' <UserID>');
Writeln;
Writeln('UserID not specified.');
Exit;
end;
// 格納先の作成
var Dir := TPath.GetDirectoryName(ParamStr(0));
var SrcDir := TPath.Combine(Dir, 'source');
var ImgDir := TPath.Combine(SrcDir, 'images');
TDirectory.CreateDirectory(ImgDir);
// パラメータ
var User_ID := ParamStr(1); // パラメータで与えられた文字列をユーザIDとして扱う
var Page := 1; // カレントページ
var Request := TRESTRequest.Create(nil);
var FileRequest := TNetHTTPRequest.Create(nil);
var ResponseData := TMemoryStream.Create;
var Body := TStringList.Create;
var ReplaceMethod := TReplaceMethodClass.Create;
try
Request.Method := rmGET;
Request.Client := TRESTClient.Create(Request);
Request.Client.BaseURL := 'https://qiita.com';
Request.Client.ContentType := 'application/json';
Request.Response := TRESTResponse.Create(Request);
Request.Resource := '/api/v2/users/{:user_id}/items';
FileRequest.Client := TNetHTTPClient.Create(FileRequest);
while True do
begin
// GET /api/v2/users/:user_id/items
// https://qiita.com/api/v2/docs#get-apiv2usersuser_iditems
Request.Params.Clear;
Request.Params.AddItem(':user_id' , User_ID , TRESTRequestParameterKind.pkURLSEGMENT);
Request.Params.AddItem('page' , Page.ToString );
Request.Params.AddItem('per_page' , PER_PAGE.ToString);
Request.Execute;
if (Request.Response.StatusCode <> 200) or // HTTP 応答ステータスが 200 OK 以外 または
(Request.Response.Content.Length < 100) then // 100 文字以下の応答 はエラーとする
Break;
// JSON データの読み込み
var Iterator := TJSONIterator.Create(Request.Response.JSONReader);
try
Iterator.Recurse;
while Iterator.Next do
begin
// データの取得
Iterator.Recurse;
(* BODY *)
Iterator.Next('body');
Body.Text := Iterator.AsString;
(* ID *)
Iterator.Next('id');
var Id := Iterator.AsString;
(* TAGS *)
Iterator.Next('tags');
var Tags := '';
Iterator.Recurse;
while Iterator.Next do
begin
Iterator.Recurse;
Iterator.Next('name');
Tags := Tags + Iterator.AsString + ' ';
Iterator.Return;
end;
Iterator.Return;
(* TITLE *)
Iterator.Next('title');
var Title := Iterator.AsString;
(* URL *)
Iterator.Next('url');
var Url := Iterator.AsString;
// TITLE と URL と Tags を標準出力
Writeln('Title: ', Title );
Writeln('URL: ' , Url );
Writeln('Tags: ' , Tags.Trim);
// 画像ファイルの取得
for var Match in TRegEx.Matches(Body.Text, EXP1) do
begin
var ImageURL := Match.Groups.Item[0].Value;
// 画像 URL を標準出力
Writeln(' - ', ImageURL);
// 画像ファイルの取得と保存
ResponseData.Clear;
FileRequest.Get(ImageURL, ResponseData);
ResponseData.SaveToFile(TPath.Combine(ImgDir, TPath.GetFileName(ImageURL)));
end;
// // Qiita 互換のヘッダ (任意)
// Body.WriteBOM := False; // BOM なし
// Body.LineBreak := #$0A; // LF 改行
// var Header := '';
// Header := Header + '---' + Body.LineBreak;
// Header := Header + 'title: ' + Title + Body.LineBreak;
// Header := Header + 'tags: ' + Tags.Trim + Body.LineBreak;
// Header := Header + 'author: '+ User_ID + Body.LineBreak;
// Header := Header + 'slide: ' + 'false' + Body.LineBreak;
// Header := Header + '---' + Body.LineBreak;
// Body.Text := Header + Body.Text;
// 画像ファイルのパス変更 (任意)
Body.Text := TRegEx.Replace(Body.Text, EXP1, ReplaceMethod.NewPath);
// 自身の投稿を相対パスへ (任意)
Body.Text := TRegEx.Replace(Body.Text, Format(EXP2, [User_ID]), ReplaceMethod.RelativePath);
// Markdown ファイルを出力
Body.SaveToFile(TPath.Combine(SrcDir, Id + '.md'), TEncoding.UTF8);
Writeln;
Iterator.Return;
end;
finally
Iterator.Free;
end;
Inc(Page); // 次のページへ
end;
finally
ReplaceMethod.Free;
Body.Free;
ResponseData.Free;
FileRequest.Free;
Request.Free;
end;
end. { Main }
アクセストークンを使った記事の取得
アクセストークンを使った記事の取得に使用する Qiita の API は GET api/v2/authenticated_user/items です。
先述のコードを次のように書き換えます。
...
try
Request.Method := rmGET;
Request.Client := TRESTClient.Create(Request);
Request.Client.BaseURL := 'https://qiita.com';
Request.Client.ContentType := 'application/json';
Request.AddAuthParameter('Authorization', 'Bearer ' + '[ここにアクセストークン]', TRESTRequestParameterKind.pkHTTPHEADER, [TRESTRequestParameterOption.poDoNotEncode]); // 追加
Request.Response := TRESTResponse.Create(Request);
// Request.Resource := '/api/v2/users/{:user_id}/items'; // 変更
Request.Resource := '/api/v2/authenticated_user/items'; // 変更
FileRequest.Client := TNetHTTPClient.Create(FileRequest);
while True do
begin
// GET /api/v2/authenticated_user/items
// https://qiita.com/api/v2/docs#get-apiv2authenticated_useritems
Request.Params.Clear;
// Request.Params.AddItem(':user_id' , User_ID , TRESTRequestParameterKind.pkURLSEGMENT); // 削除
Request.Params.AddItem('page' , Page.ToString );
Request.Params.AddItem('per_page' , PER_PAGE.ToString);
Request.Execute;
...
おわりに
思い込みはよくないですね。
REST Debugger は Embarcadero 社のフリーツールとしても公開されており、RAD Studio / Delphi / C++ Builder をインストールしなくても使う事ができます。
また、Windows 10 (1803) 以降では curl が標準装備になっており、こちらを使って REST API のテストを行う事もできます。
curl "https://qiita.com/api/v2/authenticated_user/items?page=1&per_page=100" -H "Content-Type: application/json" -H "Authorization: Bearer [ここにアクセストークン]"
See also:
- Delphi で Rest クライアント (シンクソフト Delphi 開発ブログ)
- Delphi で Slack の投稿ボットを作る (日本語問題) (WorkToolSmith)
- Delphi 10.2 Tokyo Enterprise と RAD Server で REST JSON な Web API を作る (Qiita: @kazinoue)
- REST+JSON はコンポーネントを使ってかんたんに取得★テーブル化 (Qiita: @kazaiso)
- Delphi / C++Builder で REST API をカンタンに実装できる RAD Server で JSON を返す方法を3種類作ってみる (Qiita: @kazinoue)
- FDBatchMove を使って RESTで取得した JSON データをデータベースに書き込む (Qiita: @CYonezawa)
- Delphi 10 で Twilio api を叩いて SMS メッセージを送る (Qiita: @kenken2go)
- [Delphi] JSON の処理は Serializer を使うのがナウい (Qiita: @pik)
- cURL (Wikipedia)
- (RESTful) API でアプリをパワーアップする 20 の方法 (LearnDelphi.org)
- Delphi で Apilayer REST API を使用する (blogs.embarcadero.com)











