Routes - quandis/qbo3-Documentation GitHub Wiki
QBO supports a full REST API using custom routes. By default, QBO core method signatures are mapped to concrete classes via web handlers that implement an abstract HttpAsyncHandler
, which implements Microsoft's native HttpTaskAsyncHandler
. For example, Message functionality is invoked with the following relative URLs:
POST /Message/Message.ashx/Save?Subject=My First Message
GET /Message/Message.ashx/Summary?ID=1
POST /Message/Message.ashx/[email protected]&Subject=Hello World
POST /Message/Message.ashx/Delete?ID=1
The examples in this page include query string parameters for brevity. The core
QboRouteHandler
class supports form encoded parameters as well. Sensitive data should always be passed as part of the request body, and not the query string.
Custom routes can be used to support a standard RESTful API, like this:
PUT /message?Subject=My First Message
GET /message/1
POST /message/[email protected]&Subject=Hello World
DELETE /message/1
Routes can be defined from Design > Configuration > Routes.
Property | Required | Description |
---|---|---|
Name |
yes | Name of the route. Routes are loaded in alphabetic order by Name. |
Url |
yes | Url to match route to. Parameters may be specified in the route name with curly braces. |
Defaults |
no | Default parameters to add to any requests. |
HttpMethods |
no | Http methods supported by the route: get |post |delete |patch |put (may be comma delimited) |
MethodSignature |
no | A method signature to invoke; defaults to {class}/{operation} if not defined. |
Handler |
no | Injects an alternate IQboHttpHandler , to hook custom functionality into a route. |
ExceptionHandler |
no | Injects an alternate IExceptionHandler to handle routes for a route. |
The QboRouteHandler
requires two parameters:
Parameter | Description |
---|---|
class |
Name of the class to be invoked. E.g. Attachment , Message , Organization , etc. |
operation |
Name of the operation (method, service, or statement) to be invoked. |
These parameters may be define in the Url
, as a DefaultParameter
, or as part of the MethodSignature
. All other route parameters will be passed as parameters to the method signature being invoked.
Route parameters may have the following type constraints applied:
Constraint | Example | Description |
---|---|---|
int | {userId:int} | Will only bind to the route if the userId parameter is an integer. (Regex \d+) |
date | {payment:date} | Will only bind to the route if the payment parameter is an date formatted as YYYY-MM-DD . |
isodate | {created:isodate} | Will only bind to the route if the created parameter is an date formatted as YYYY-MM-DDTHH:MM:SE . |
Below are the standard routes that are part of the core QBO configuration:
<?xml version="1.0"?>
<Routes>
<CustomRoutes>
<CustomRoute Name="Default-1-Queue" Url="api/{class}/queue/{operation}"/>
<CustomRoute Name="Default-2-Parent" Url="api/{Object}/{ObjectID:int}/{class}/{operation}"/>
<CustomRoute Name="Default-3-Singleton" Url="api/{class}/{operation}/{id:int}"/>
<CustomRoute Name="Default-4-Default" Url="api/{class}/{id:int}"/>
<CustomRoute Name="Default-5-Any" Url="api/{class}/{operation}"/>
</CustomRoutes>
</Routes>
Route ordering is important; the CustomRouteConfiguration
will order routes alphabetically. If you wish to add routes that are applied ahead of QBO's default routes, ensure your route name precedes the word 'Default' (e.g 'Custom-*'). If you wish your custom routes to be applied after QBO's default routes, ensure your route name succeed the word 'Default' (e.g. 'Post-*').
api/{class}/queue/{operation}
This will queue a method signature {class}/{operation}
to a queue. Optional parameters include:
- Queue: name of the queue to use; defaults to
DefaultQueue
api/{Object}/{ObjectID:int}/{class}/{operation}
Parent routes are typically used to invoke a Search
or List
operation for a generic object against a parent. Examples include:
api/Organization/12345/Attachment/Search
api/Loan/23456/Message/Search
api/Decision/34567/DecisionStep/Search
This pattern simply translates the method signature to {class}/{operation}?Object={Object}&ObjectID={ObjectID}
.
QBO uses a pattern of
Object/ObjectID
(akaObject/ID
) to identify a 'soft' parent key. For example, ourMessage
class can be a child of any QBO class, such asOrganization
,Contact
,Property
,Loan
,Policy
, etc.
api/{class}/{operation}/{id}?{parameters}
This will invoke a method signature equivalent to {class}/{operation}?ID={id}&{parameters}
api/{class}/{id:int}?{parameters}
Depending on the HTTP Method, this will:
- GET: invoke {class}/Summary?ID={id}&{parameters}
- DELETE: invoke {class}/Delete?ID={id}&{parameters}
- PATCH: invoke {class}/Save?ID={id}&{parameters}
- POST: invoke {class}/Save?ID={id}&{parameters}
- PUT: invoke {class}/Save?ID={id}&{parameters}
api/{class}/{operation}
This is a hook to invoke any QBO operation via a RESTful syntax where all parameters are part of the query string or form post.
Aliases of QBO classes can be implement with custom routes, and include standard parameters. For example, the Process
class might be used to manage Projects, using a custom route:
<CustomRoute Name="Projects" Url="projects/{operation}" MethodSignature="Process/{operation}"/>
Most examples we provide demonstrate passing parameters via the query string. QBO supports parameters via GET
(on the query string) or POST
(as form URL encoded, XML, or JSON).
Contact/Save?FirstName=John&LastName=Doe&USSSN=123-45-6789
is an easy-to-read example, but bad practice, since sensitive data is being included in the URL, and will thus appear in logs and other inappropriate places. Instead:
POST https://qbo.acmefinancial.com/Contact/Save HTTP/1.1
Accept: application/javascript
Content-type: application/x-www-form-urlencoded; charset=UTF-8
Cookie: ...
FirstName=John&LastName=Doe&USSSN=123-45-6789
This will ensure that the name and USSSN are encrypted in transit, and not be subject to logging plain text.
POST https://qbo.acmefinancial.com/Contact/Save HTTP/1.1
Accept: application/javascript
Content-type: text/xml; charset=UTF-8
Cookie: ...
<ContactCollection>
<ContactItem>
<FirstName>John</FirstName>
<LastName>Doe</LastName>
<USSSN>123-45-6789</USSSN>
</ContactItem>
</ContactCollection>
The decision to make RESTful URLs from nouns that are singular or plural is a bit of a religious war. The W3C REST protocol does not dictate best practice either way.
QBO was created in part with code generators, for which abstracting the correct English plural of a given class was annoying at best. Thus, we chose to make our SQL tables, their identity and label columns, matching class names and web endpoint singular.
If you prefer plural, you can easily use custom routes to enable them. For example, if you wish to access the Attachment
module via a documents
endpoint:
<CustomRoute Name="Documents" Url="documents/{operation}" MethodSignature="Attachment/{operation}"/>
POST
is supposed to create a new record. QBO deviated from this; if you POST
with a valid identifier, this acts as a PUT
(database UPDATE
statement).
The standard POST
operation (/{class}/{id}
) will invoke QBO's Save
method, which is equivalent of a RESTful PATCH
instead of POST
. This means that if a client POST omits a property from the target class, the property will remain unchanged, instead of NULLed. If the client intends to NULL a property, they should POST the property with no value. For example:
// This will update a Contact's FirstName and LastName, but leave the MiddleName alone
/contact/1?FirstName=Jane&LastName=Doe
// This will update a Contact's FirstName, LastName, and NULL out the MiddleName
/contact/1?FirstName=Jane&MiddleName=&LastName=Doe
IIS default settings do not provide for PUT and DELETE operations.
The following items must be set to enable:
- Website Application Pool Managed Pipeline Mode must be set to Integrated
- HTTP Verbs must be supported in IIS
Set Application Pool Pipeline Mode
- Go to IIS Manager > Application Pools
- Locate Application Pool for target website > Edit (double-click) > Managed pipeline mode: Integrated
HTTP Verb Update
The following snippet should be added to web.config
to enable all verbs, and to ensure that WebDAV
does not intercept and reject PUT
and DELETE
. If the default WebDAV
is configured, WebDAV
will intercept PUT
and DELETE
verbs, returning 405 errors (method not allowed).
<system.webServer>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="WebDAV" />
<add name="dotless" path="*.less" verb="GET" type="dotless.Core.LessCssHttpHandler,dotless.AspNet" resourceType="File" preCondition="" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv4.0" responseBufferLimit="0" />
</handlers>
<modules>
<remove name="WebDAVModule" />
</modules>
</system.webServer>
If you prefer a GUI to add verbs, go to IIS Manager > Handler Mappings. Find ExtensionlessUrlHandler-Integrated-4.0
, double click it. Click Request Restrictions...
button and on Verbs
tab, add both DELETE
and PUT
.