REST API と SOAP API の強みを実コードで比較する
はじめに
REST API と SOAP API はどちらもシステム間連携で使われる API で、自分の周囲でREST が盲目的に持て囃されていると感じたのでまとめる。
今回は Spring Boot で作った同じユースケースの API を使い、REST と SOAP の強みを比較する。
扱う商品データは次の通り。
{
"id": 1,
"name": "REST入門書",
"price": 2200,
"category": "Book"
}
データ形式として、おそらくよく使われるであろうという理由で REST 側は JSON、SOAP 側は XML で同じ商品を扱う。
REST API とは
REST API は HTTP の URI、メソッド、ステータスコードを活用してリソースを操作する API 。
今回のサンプルでは商品一覧を次のように取得する。
GET /api/products?category=Book HTTP/1.1
Accept: application/json
実装は次のようになる。
@GetMapping
public ResponseEntity<List<Product>> findAll(@RequestParam(required = false) String category) {
return ResponseEntity.ok()
.cacheControl(CacheControl.maxAge(Duration.ofSeconds(30)))
.body(repository.findByCategory(category));
}
REST では、検索条件は ?category=Book のようにクエリパラメータ設定できる。参照系 API なら Cache-Control も HTTP ヘッダーとして返せる。便利だね。
SOAP API とは
SOAP API は XML ベースの API。SOAP Envelope という決まった構造の中にリクエストやレスポンスを入れて通信する。WSDL で API を定義し、XSD でデータ型を定義できる。REST がアーキテクチャスタイルであるのに対して、 SOAP はプロトコル( Simple Object Access Protocol )。ルールとして定められている。
REST でクエリパラメータにカテゴリを設定していたが、SOAP では XML 本文に検索条件であるカテゴリを書く。
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:p="http://example.com/restvssoap/products">
<soapenv:Header/>
<soapenv:Body>
<p:GetProductsRequest>
<p:category>Book</p:category>
</p:GetProductsRequest>
</soapenv:Body>
</soapenv:Envelope>
Endpoint の実装は次のようになる。
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "GetProductsRequest")
@ResponsePayload
public Source getProducts(@RequestPayload Element request) {
Document document = newDocument();
Element response = document.createElementNS(NAMESPACE_URI, "GetProductsResponse");
document.appendChild(response);
repository.findByCategory(optionalText(request, "category"))
.forEach(product -> response.appendChild(productElement(document, product)));
return new DOMSource(document);
}
SOAP では、REST のクエリパラメータに相当する条件も XML Schema の対象になるということ。きっちりしてるね。
REST API の強み
リクエストが軽く、データ量を抑えやすい
REST の大きな利点は、リクエストとレスポンスを SOAP に対して比較的短くできること。
今回のカテゴリ検索を REST で表すと、リクエストは実質これだけ。
GET /api/products?category=Book
SOAP では同じ検索に Envelope、名前空間、Body が必要になる。
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:p="http://example.com/restvssoap/products">
<soapenv:Header/>
<soapenv:Body>
<p:GetProductsRequest>
<p:category>Book</p:category>
</p:GetProductsRequest>
</soapenv:Body>
</soapenv:Envelope>
単純に文字数で見ると、REST の GET /api/products?category=Book は31 文字である。一方、上の SOAP XML は整形込みで約 300 文字ある。実際の HTTP ヘッダーを含めれば単純比較は変わるが、アプリケーションが扱うペイロードだけを見ると SOAP はかなり大きい。
この差の影響として、SOAP の「XML が冗長で通信量やログ量が増えやすい」という弱点を REST が避けられると考えることができ、モバイル回線や一覧画面の頻繁な再取得、マイクロサービス間の高頻度通信では、REST の短さは扱いやすいと言える。
HTTP の仕組みを使える
REST は HTTP の機能を API 設計にそのまま使いやすい。今回の REST 実装では、一覧取得に Cache-Control を付けている。
return ResponseEntity.ok()
.cacheControl(CacheControl.maxAge(Duration.ofSeconds(30)))
.body(repository.findByCategory(category));
これによりレスポンスヘッダーには次のような情報が付く。
Cache-Control: max-age=30
カテゴリ別商品一覧やマスタデータのように、短時間なら古くても許容できるデータでは恩恵が大きい。CDN、リバースプロキシ、ブラウザキャッシュを使いやすく、API サーバーへのアクセスを減らせる。
SOAP でも HTTP 上で通信できるが、操作は基本的に XML メッセージとして POST されるので、REST の GET と比べると HTTP キャッシュと噛み合わせにくい。
つまり、 REST は、SOAP で起きやすい「参照系 API なのに HTTP キャッシュに乗せづらい」というつらさを避けやすいということ。参照中心の API では、HTTP の既存機能をそのまま使えることは強みと言える。
フロントエンドと相性がよい
REST のレスポンスは JSON なので、JavaScript やモバイルアプリでそのまま扱いやすい。
[
{
"id": 1,
"name": "REST入門書",
"price": 2200,
"category": "Book"
}
]
SOAP のレスポンスは XML になる。
<GetProductsResponse xmlns="http://example.com/restvssoap/products">
<product>
<id>1</id>
<name>REST入門書</name>
<price>2200</price>
<category>Book</category>
</product>
</GetProductsResponse>
ブラウザ画面で商品名を表示するだけなら JSON の方が取り回しやすい。XML は名前空間やパース処理を意識することになり、画面向け API としては重くなりやすい。もちろん扱い方で変わるかもだが。
SOAP では xmlns:p="http://example.com/restvssoap/products" のような名前空間も重要になる。名前空間 URI を間違えると、リクエストは期待する Endpoint に届かない。REST なら GET /api/products?category=Book を見れば何をしているか分かりやすい。
今回のサンプルでも、REST は URL を組み立てるだけで呼べる。
restTemplate.getForObject(baseUrl + "/api/products/{id}", ProductResponse.class, id);
SOAP は XML ペイロードを組み立てて送る。
webServiceTemplate.sendSourceAndReceiveToResult(
endpointUrl,
new StreamSource(new StringReader(payload)),
result
);
REST は、SOAP で起きやすい「XML Envelope、名前空間、Fault(後述) を読み解く必要がありデバッグ辛い」という問題を避けやすい。小さな社内 API、画面向け API、素早く試したい API では、REST は便利。
SOAP API の強み
WSDL/XSD による縛りが強い
SOAP の大きな利点は、APIのルールを機械的に表現できること。
REST API 自体には WSDL のような定義ファイルが必須ではない。今回の REST 実装でも、Product の形は Java の record と README程度であり、実際のエンタープライズ開発では膨大な設計書があったりなかったりして、APIのインタフェースを探すだけで3日ということもあるかもしれない(あった)。
public record Product(long id, String name, BigDecimal price, String category) {
}
この状態でサーバー側が price を数値から文字列に変えたり、category を必須から任意に変えたりしても、クライアントがコンパイル時に気づけるとは限らない。
SOAP 側では products.xsd に型を定義している。
<xs:complexType name="Product">
<xs:sequence>
<xs:element name="id" type="xs:long"/>
<xs:element name="name" type="xs:string"/>
<xs:element name="price" type="xs:decimal"/>
<xs:element name="category" type="xs:string"/>
</xs:sequence>
</xs:complexType>
さらに今回のサンプルでは GetProductsRequest に任意の category を定義している。
<xs:element name="GetProductsRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="category" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
これにより、カテゴリ検索という仕様が XML Schema に明示される。別会社や別部署と連携する場合、「どの項目を送れるのか」「型は何か」「必須 or 任意」を WSDL/XSD で共有できる。
REST では ?category=Book と書けば済むが、そのパラメータが存在することは Swagger などを別途用意しない限り機械的には伝わりにくい。SOAP は、REST で起きやすい「仕様が README や実装に散らばり、管理が難しくなる」問題を WSDL/XSD で補える。
型安全な連携を作りやすい
SOAP では price を xs:decimal として定義している。
<xs:element name="price" type="xs:decimal"/>
金額、日付、識別子などの型ミスが業務事故につながるシステムでは、これは大きな要素。金融・保険・基幹連携のような誤りが許されないシステムで「曖昧な JSON で何となく受ける」ことが許されない場合、SOAP がプロトコルとして保障してくれることはとても安心できる。
今回はREST でも Java 側では BigDecimal を使っている。
public record CreateProductRequest(String name, BigDecimal price, String category) {
}
ただし HTTP として、JSON と Java 型の対応を別途説明する必要がある。SOAP はその部分を XSD と WSDL に寄せられる。
REST は柔軟に項目追加できるため、JSON に任意項目の description を追加しても、多くのクライアントはこれを意識する必要がない。これは便利だが、逆に言えば仕様変更が JSON の形に現れず、クライアントが気づきにくいとも言える。
SOAP は変更調整が重い代わりに、型や要素の差分が現れやすい。金融・保険・基幹連携ではこの「気づきやすさ」というのが非常に助かる。
業務エラーを SOAP Fault として扱える
今回の SOAP API では、REST と同じ InvalidProductException を SOAP Fault に変換している。
@Bean
public SoapFaultMappingExceptionResolver exceptionResolver() {
SoapFaultMappingExceptionResolver resolver = new SoapFaultMappingExceptionResolver();
SoapFaultDefinition faultDefinition = new SoapFaultDefinition();
faultDefinition.setFaultCode(SoapFaultDefinition.CLIENT);
faultDefinition.setFaultStringOrReason("Business validation failed");
resolver.setDefaultFault(faultDefinition);
return resolver;
}
価格 0 の商品登録では、REST は JSON エラーを返すが、SOAP は SOAP Envelope 内の Fault として返す。
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Client</faultcode>
<faultstring>Business validation failed</faultstring>
</SOAP-ENV:Fault>
SOAP クライアントは Fault を 標準的な エラー応答として扱える。
REST でも JSON エラーは読みやすい。今回の REST API では価格 0 の商品登録に対して次を返す。
{
"code": "INVALID_PRODUCT",
"message": "Product price must be greater than zero"
}
ただし、この形式はプロジェクト独自であり、 code にするのか errorCode にするのか、HTTP ステータスと業務エラーをどう対応させるのかを決めないと、API ごとにエラー処理が異なることになる。
SOAP はそのばらつきを SOAP Fault という標準的な形に寄せられる。REST の自由さは、逆に考えると自由すぎて SOAP 以上の管理意識が必要になるとも言える。
REST vs SOAP
| 観点 | REST API | SOAP API |
|---|---|---|
| リクエスト | URI と HTTP メソッドで表現しやすい | XML Envelope が必要 |
| レスポンス | JSON が中心で軽い | XML で冗長 |
| 定義 | OpenAPI 等(任意) | WSDL/XSD が標準的 |
| 型安全性 | 実装や追加ツール次第 | XSD で明確にしやすい |
| キャッシュ | GET と HTTP ヘッダーを使いやすい | POST XML 中心で扱いづらい |
| エラー | JSON 形式を独自設計しやすい | SOAP Fault として標準化しやすい |
| デバッグ | curl やブラウザで試しやすい | XML 名前空間や Fault の理解が必要 |
| 想定ユースケース | Web API、モバイル、マイクロサービス | 金融、保険、基幹連携、レガシーシステム |
今回のサンプルケースでの REST vs SOAP
| ケース | REST での恩恵/トラブル | SOAP での恩恵/トラブル |
|---|---|---|
| 商品一覧を画面に表示する | JSON が軽く画面で扱いやすい | XML が冗長で画面向けには扱いづらい |
| カテゴリで絞り込む |
?category=Book で自然に表現できる |
XML 本文と XSD 定義が必要 |
| 参照系をキャッシュする |
Cache-Control: max-age=30 を使える |
HTTP キャッシュを使いづらい |
| 価格 0 の不正商品を拒否する | JSON エラー形式を自分で決める必要がある | SOAP Fault として標準化できる |
| 別会社連携時のインタフェース | OpenAPI などを別途整備する必要がある | WSDL/XSD を連携しやすい |
| 項目追加や仕様変更をする | 柔軟だが破壊的変更に気づきにくい | 変更調整は重いが変更差分に気づきやすい |
使い分けの指針
新規の Web API、SPA、モバイルアプリ、外部公開 API、マイクロサービスでは REST API が第一候補になりやすい。軽量で扱いやすく、HTTP や JSON のエコシステムも豊富。
一方、WSDL や XSD による厳密な定義、既存の基幹システムとの互換性、SOAP Fault や WS-* 系仕様との整合性が必要な場合は SOAP API が候補になる。
まとめ
重要なのは、REST が新しくて SOAP が古いという単純な比較ではなく、REST は軽量で柔軟な API に向き、SOAP は定義と標準仕様を重視する連携に向くということ。手放しで REST 信奉することはエンタープライズ開発時に大きな障害を生み出すことになる。そしてそれに気づいた時には、すでに後戻りできない段階まで来ている可能性が高い。
もちろん何も考えず SOAP のまま作り続けるのもよくないので、しっかりと考えるべき(己への戒め)。