WSDL to OpenAPI — how each SOAP operation becomes a REST endpoint
· The Vizroute team
The rules Vizroute uses to turn a WSDL into an OpenAPI design — verbs, paths, identifiers, status codes and faults — and the cases it flags for a person to decide.
A WSDL and an OpenAPI file describe the same thing in two shapes. SOAP is a list of operations, each a verb with an envelope: GetAccountBalance, CloseAccount, TransferFunds. REST is a set of resources, each with a handful of methods: GET /accounts/{accountId}/balance. Converting one to the other is mostly a naming problem, and most of it can be done by reading the names carefully.
Vizroute does that reading when you open a WSDL. Paste it, upload a .wsdl or .xml file, or give its URL to the API Map, and you get a proposed REST design drawn as a map. This post sets out the rules it follows, so you know which parts of the proposal to trust and which to check.
What you get from a real WSDL
Here is the bank service that ships as a sample, with every operation and the endpoint proposed for it:
| SOAP operation | Method | Proposed path |
|---|---|---|
| GetAccountBalance | GET | /accounts/{accountId}/balance |
| GetAccountTransactions | GET | /accounts/{accountId}/transactions |
| FindAccountsByCustomer | GET | /customers/{customerId}/accounts |
| OpenAccount | POST | /accounts |
| UpdateAccountAddress | PUT | /accounts/{accountId}/address |
| CloseAccount | DELETE | /accounts/{accountId} |
| TransferFunds | POST | /funds/transfer |
| ValidateIban | POST | /ibans/validate |
Each of those rows came from one of the rules below.
The method comes from the first word
The operation name is split into words, and the first word picks the HTTP method:
| First word | Method |
|---|---|
| Get, Find, Fetch, List, Search, Lookup, Check, Count | GET |
| Create, Add, Insert, Register, Submit, Open, Issue | POST |
| Update, Set, Modify, Change, Save, Replace, Assign | PUT |
| Patch, Adjust, Amend | PATCH |
| Delete, Remove, Cancel, Close, Revoke, Terminate | DELETE |
That is why CloseAccount becomes a DELETE. Closing an account is not always a deletion in your domain, which is exactly the kind of call the proposal leaves visible for you to change.
Some verbs are not reads or writes of a resource at all. Validate, Transfer, Calculate, Approve, Reject, Refund and some fifty others are treated as commands. A command stays a POST, and the verb becomes the last segment of the path: TransferFunds is POST /funds/transfer. Forcing a transfer onto PUT or PATCH would describe it worse than SOAP did.
When no verb matches, as with SayHello, there is a fallback. If every input is a plain value, there are at most four of them, and there is a result, the operation is proposed as a GET. Otherwise it is a POST, the safe default for an action. Either way the operation is flagged for review, with the reason written down.
The path comes from the rest of the name and the input fields
The words after the verb name the resource. The last word is made plural for the collection, so OpenAccount becomes POST /accounts.
An identifier comes from the request, not the name. If an input field is named after the resource plus Id, Number, Code, Ref or Key, it becomes a path parameter. In GetAccountBalance, the request has an accountId field, so the account is the resource, {accountId} goes in the path, and the word left over, Balance, becomes a sub-resource: GET /accounts/{accountId}/balance.
A "By", "For" or "Of" clause names a filter, or a parent. FindAccountsByCustomer takes a customerId, and since that identifies a customer, the accounts are nested under it: GET /customers/{customerId}/accounts. If the clause matches an ordinary field instead of an identifier, the field becomes a query parameter.
Plain input fields on a GET or DELETE become query parameters. Structured criteria do not fit in a query string, so a GET whose request carries nested objects or lists is proposed as POST /…/search with the criteria as the body. That one is always flagged: it works, but it is a compromise you may prefer to flatten.
Status codes and errors
The success status follows from what the operation returns:
- A creation that returns something answers 201 Created. One that returns nothing answers 202 Accepted.
- An operation whose response element is empty answers 204 No Content.
- A one-way operation, with no output message at all, answers 202 Accepted.
- If the response wrapper holds a single field named like
ResultorReturn, that field is returned directly instead of the wrapper.
SOAP faults become HTTP errors by their names:
| Fault name contains | Status |
|---|---|
| NotFound, Unknown, Missing, NoSuch | 404 |
| Invalid, Validation, Malformed, Required | 400 |
| Unauthorized, Credential, Token, Expired | 401 |
| Forbidden, Permission, Denied | 403 |
| Conflict, Duplicate, AlreadyExists, Locked | 409 |
| Insufficient, Limit, Quota, Funds | 422 |
| Timeout, Unavailable, Busy | 503 |
| anything else | 500 |
An InsufficientFundsFault on TransferFunds is therefore a 422, which is what a REST client would expect for a request that is well formed but cannot be carried out.
What it will not decide for you
Every proposal carries a confidence figure and a list of reasons. The confidence drops when the rules had to guess:
- no recognised verb;
- several identifiers in one request, where it has to pick one for the path;
- a DELETE or a PUT with no identifier, which would act on a whole collection;
- a search squeezed into a POST.
Those are the rows to read first. The rest usually need nothing more than a glance.
Some things a name cannot tell you at all. Whether "close" means delete or a status change, whether two operations are really the same resource, or how your teams name things already: that is a review, not a rule. The proposal gets you to that review with the mechanical part done.
Try it on your own WSDL
Open the API Map, choose Upload, and pick your .wsdl file. The map shows the proposed resources, and the Raw Spec tab shows the OpenAPI document behind them. If your WSDL imports its schemas from separate XSD files, the SOAP Workbench (where your site has it switched on) takes the WSDL and its XSDs together. Everything is read in your browser, and nothing is uploaded.
The user guide lists every format the API Map reads.