Making HTTP Rest Requests With Openedgenet
Making HTTP Rest Requests With Openedgenet
www.progress.com
OPENEDGE.NET.URI
The URI class encapsulates the target address, including the scheme (enumerated
in OpenEdge.Net.UriSchemeEnum), host, port, path, query and friends. There's a
great description of the pieces that comprise a URL at
http://www.skorks.com/2010/05/what-every-developer-should-know-about-urls/ .
Typically, a new URI object is created via constructor, passing in at least the scheme
and host. The other elements of the URI can be added via properties (most) or
methods (queries).
using OpenEdge.Net.URI.
A URI can additionally be derived from a string via the static Parse() method. An
encoded version of the URL is returned via the Encode() method.
using OpenEdge.Net.URI.
OPENEDGE.NET.HTTP.HTTPREQUEST
A request has 2 or 3 mandatory elements: a method ("verb" like GET; represented
by the OpenEdge.Net.HTTP.MethodEnum enum) and a URI (the address for the
request).
Some requests also require a payload, called the entity or body or message. To add
an entity to a request, set the Entity property to an object instance. All entity data
must be encapsulated in a Progress.Lang.Object (ie not a temp-table handle).
Headers can be added to the request via the AddHeader() method.
While the request type (OpenEdge.Net.Http.HttpRequest) can be manually
built, using the Request Builder helper class to construct a request is indented to be
simpler and more readable. Request objects are reusable across requests.
Building a request
The OpenEdge.Net.Http.RequestBuilder helper class provides a fluent (or
chained) API for creating HttpRequest objects.
The initial methods to call are one of the static 'verb' methods:
> RequestBuilder:Get(<uri>|<entity>)
> RequestBuilder:Delete(<uri>|<entity>)
> RequestBuilder:Head(<uri>)
> RequestBuilder:Options(<uri>|<entity>)
> RequestBuilder:Trace(<uri>)
Where <uri> is either an instance of a URI object, or a character string, and
<entity> is an instance of an object.
From this start, a number of additional methods can be chained
> HttpVersion: Change the HTTP version from its 1.1 default
using OpenEdge.Net.URI.
using OpenEdge.Net.UriSchemeEnum.
using OpenEdge.Net.HTTP.RequestBuilder.
using OpenEdge.Net.HTTP.HttpRequest.
oRequest = RequestBuilder:Get(oURI)
:Request.
R E Q U E S T B U I L D E R E X A MP L E 2 ( S I MP L E P U T )
using OpenEdge.Net.URI.
using OpenEdge.Net.UriSchemeEnum.
using OpenEdge.Net.HTTP.RequestBuilder.
using OpenEdge.Net.HTTP.HttpRequest.
using Progress.Json.ObjectModel.JsonObject.
R E Q U E S T B U I L D E R E X A MP L E 3 ( A D D I N G B A S I C A U T H )
oURI = URI:Parse('http://localhost:9090/oem/resources').
R E Q U E S T B U I L D E R E X A MP L E 4 ( A D D I N G G E N E R A L C R E D E N T I A L S )
oURI = URI:Parse('http://localhost:9090/oem/resources').
oRequest = RequestBuilder:Get('http://localhost:9090/oem/resources')
:AcceptJson()
:AddCallback(get-class(IAuthFilterEventHandler),
new AuthStatusListener())
:Request.
end method.
end class.
OPENEDGE.NET.HTTP.HTTPCLIENT
The client performs the request via its Execute() method, which simply takes an
HttpRequest and returns an HttpResponse. The response's StatusCode is
evaluated and the request may be modified and resubmitted.
EXECUTING A REQUEST
using OpenEdge.Net.HTTP.HttpRequest.
using OpenEdge.Net.HTTP.HttpResponse.
using OpenEdge.Net.HTTP.HttpClient.
oResponse = HttpClient:Instance():Execute(oRequest).
Cookies
The default HttpClient is a stateless client (ie no cookies).
OpenEdge.Net.Http.StatefulHttpClient extends the standard HttpClient
and writes cookies into the HttpRequest and reads them from the HttpResponse
after request execution. Cookies are stored in an instance of an
OpenEdge.Net.HTTP.ICookieJar class (the standard implementation is
OpenEdge.Net.HTTP.CookieJar which writes persistent cookies to disk in JSON
form.
The OpenEdge.Net.HTTP.Cookie type encapsulates a single cookie. There's
also a OpenEdge.Net.HTTP.CookieCollection class provided for working with
collections of Cookies.
HTTP Authentication
The HttpClient supports HTTP Basic and Digest authentication, either pre-emptively
(ie set by the developer in advance) or on request (via a 401/Unauthorized status
and the WWW-Authenticate header).
Options
The HttpClient has an Options property (of
OpenEdge.Net.HTTP.ClientOptions type), which allows the setting of options
for the HttpClient, such as timeouts and retry counts.
OPENEDGE.NET.HTTP.HTTPRESPONSE
Once a request successfully executes (note that success means a successful round-
trip, not necessarily that the request returns what was desired), an HttpResponse
object is returned to the caller. The two most important pieces of data this object
returns are the StatusCode and the Entity properties. StatusCode indicates
the result of the request. These values are enumerated by
OpenEdge.Net.HTTP.StatusCodeEnum, which is based on
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.1.1
STATUS CODES
using OpenEdge.Net.HTTP.HttpRequest.
using OpenEdge.Net.HTTP.HttpResponse.
using OpenEdge.Net.HTTP.HttpClient.
oResponse = HttpClient:Instance():Execute(oRequest).
message
oResponse:StatusCode:Value skip
oResponse:StatusCode:Name skip
view-as alert-box.
The example code below retrieves the Entity and writes it to disk in the correct
format based on the type of the returned Entity. We can also use the Response's
ContentType property to perform similar things.
EXTRACTING TYPED DATA FROM A RESPONSE
using Progress.Json.ObjectModel.JsonObject.
using Progress.Json.ObjectModel.ObjectModelParser.
using Progress.Lang.Object.
using OpenEdge.Core.WidgetHandle.
using OpenEdge.Core.String.
using OpenEdge.Net.HTTP.HttpRequest.
using OpenEdge.Net.HTTP.HttpResponse.
using OpenEdge.Net.HTTP.HttpClient.
oResponse = HttpClient:Instance():Execute(oRequest).
if type-of(oEntity, JsonObject) then
cast(oEntity, JsonObject):WriteFile('temp/entity.json', true).
else
if type-of(oEntity, WidgetHandle) then
do:
hXmlDoc = cast(oEntity, WidgetHandle):Value.
hXmlDoc:save('file', 'temp/entity.xml').
end.
else
do:
if type-of(oEntity, String) then
lcHTML = cast(oEntity, String):Value.
else
lcHTML = oEntity:ToString().
OPENEDGE.NET.HTTP.LIB
The OpenEdge.Net.Http types represent the public API and act as a wrapper
around an instance of the OpenEdge.Net.HTTP.IHttpClientLibrary interface.
This is an interface representing the library performing actual HTTP requests and
processing the responses. This library is not 'customer facing': the classes
implementing this interface will be highly-specific to the underlying
DLL/implementations.
The library currently shipped uses ABL sockets for its HTTP client. The socket library
implementation used is an OpenSource library available at
https://bitbucket.org/jmls/dotrsocket/ . Alternate implementations could include an
external DLL like libcurl or the .NET classes. The ABL sockets version was chosen
for its platform independence.
FILTERS
Much incremental functionality is added to the base library by means of filter classes.
These filters allow additional functionality to be added to the Http Client and Request
and/or Response objects without requiring changes to those types.
Filters are added during object initialisation, and are hard-coded (this is an area for
improvement/refactoring).
Filters are chained and by default are executed in order. Individual filters can prevent
execution of downstream/further filters. Filters are a specialisation of a singly-
LinkedList and each filter has its own list. These are in the
OpenEdge.Net.HTTP.Filter namespace, with further specialisation.
> Header: Handles headers and their values. For instance, the Set-
Cookie header is converted into a 'real' cookie. Used by both
requests and responses.
> Status: Performs actions based on a response's StatusCode
property. Used to deal with redirects or authorisation requests. Used
by responses.
> PutBytes(): Adds bytes from an existing MEMPTR into the bucket.
This is a deep copy, and the caller is (still) responsible for cleaning up
the incoming MEMPTR.