The logic for handling HTTP requests.
The service must operate on the Task
thread. Each request should operate on new instance of HttpService
.
HttpService
takes two arguments:
port
- required - instance ofroMessagePort
interceptors
- optional - an array of interceptors
port = CreateObject("roMessagePort")
httpService = HttpService(port)
It has only one method - fetch
- by calling it the HttpService
will create a request (HttpRequest
) according to options passed to it and fulfill the response with HttpResponse
.
Request options:
url
- string - request URLheaders
- associative array - headers fieldsmethod
- string - HTTP methodbody
- associative array - request bodyqueryParams
- associative array - with query paramscompression
- (defalt: true) boolean - indicating if the request should be compressedtimeout
- integer - time after which request should be aborted
The HttpResponse
is a node with fields:
id
- stringhttpStatusCode
- integerheaders
- associative arrayrequestOptions
- associative arrayrawData
- dynamicfailureReason
- string
To create your own request create a new component that extends Request
(/component/http/request/Request
).
You can always create a component that will aggregate the common logic of your requests and extend that component (MyRequest
extends MyCommonRequest
extends Request
).
Request
has the following interface that should be extended in your Request
derived component:
initRequest
- that will be run when the request is initialized. It is recommended to get all data from other nodes here, as exchanging the data between nodes will result in rendezvous in further process. The method is executed onrender
thread.runRequest
- If you are usingHttpService
, you will invokehttpService.fetch
here.getRequestOptions
- this needs to return response options like URL, headers, method, body, timeout.parseResponseData
- this method will parse response data, you can for example create a new node, add data there and return it. The method is executed on theTask
thread.generateErrorData
- here you can generate your custom error data that should be thrown on request failure. The method is executed on theTask
thread.
It is up to you how you handle your data. parseResponseData
and generateErrorData
should contain the appriopriate handlers.
Let's create a minimal working example:
<?xml version="1.0" encoding="utf-8" ?>
<component name="UpdateUserRequest" extends="Request">
<script type="text/brightscript" uri="GetUserRequest.brs" />
</component>
sub init()
m.top.observeFieldScoped("state", "_onStateChange")
end sub
' This is Request interface that child should implement
' This function runs on Task thread
sub runRequest()
response = m._httpService.fetch(m._requestOptions)
' Be aware that HttpService will parse response body to JSON when detected
if (response.isSuccess)
response.data = parseResponseData(response.rawData)
else
response.data = generateErrorData(response)
end if
' This causes rendezvous so ideally you should call it once
m.top.response = response
end sub
' This is Request interface that child should implement
' You can return the options or you can do any transformations on it
function getRequestOptions(options as Object) as Object
return {
url: "https://my-user.com/endpoint",
method: "POST",
headers: {
header1: "header-value",
},
body: {
userID: options.userID,
username: options.username,
},
}
end function
' This is Request interface that child should implement
function parseResponseData(data as Object) as Object
' Do any transformations here
return data
end function
' This is Request interface that child should implement
function generateErrorData(response as Object) as Object
' Do any transformations here
return { error: response.failureReason }
end function
sub _onStateChange(event as Object)
' This allows to rerun the same instance of a task
if (LCase(event.getData()) = "run")
m._port = CreateObject("roMessagePort")
m._httpService = HttpService(m._port)
else
m._port = Invalid
m._httpService = Invalid
end if
end sub
To send defined requests you need to use createRequest
function.
As a first argument, it takes the Request
component name defined by you in the application.
Let's use an example from previous section:
data = {
userID: "UserID123",
username: "Joe Doe",
}
promiseChain = createRequest("UpdateUserRequest", data)
promiseChain.then(sub (data as Object): ?data end sub, sub (error as Object): ?error end sub)
The data
is passed as an argument to getRequestOptions
method in UpdateUserRequest
component.
It will return a Promise
.
If you know that request is not needed anymore you can easily abort it by adding the AbortController
signal to your request and by calling abort
method on that instance of AbortController
.
To do that you can pass your instance of abort controller in the options of createRequest
.
myAbortController = AbortController()
createRequest("RequestName", {}, {
signal: myAbortController.signal,
})
myAbortController.abort()
AbortController
contains also isAborted
method which will return a boolean indicating if abort was called on it.
The catch/rejected handler of the promise will be invoked when request is aborted.
It is possible to intercept an HTTP request made by HttpService
.
Sometimes you need it for example for reporting reasons.
To do it, you need to create an interceptor (you can extend our HttpInterceptor
).
It contains two methods:
interceptRequest
which will be called when the request is sentinterceptResponse
which will be called when the response is sent
port = CreateObject("roMessagePort")
interceptor = HttpInterceptor()
interceptor.interceptRequest = sub (requestOptions as Object, urlTransfer as Object)
?requestOptions
?urlTransfer
end sub
interceptor.interceptResponse = sub (requestOptions as Object, urlEvent as Object)
?requestOptions
?urlEvent
end sub
httpService = HttpService(port, [interceptor])
You can intercept requests by adding your custom interceptors to the HttpService
. Each time the request is made, the interceptor will be invoked with arguments: requestOptions
and urlTransfer
.
interceptRequest
takes two arguments:
requestOptions
- these are options with which request was senturlTransfer
- an instance ofroUrlTransfer
that is handling this request
You can intercept response by adding your custom interceptors to the HttpService
. Each time the request is made, the interceptor will be invoked with arguments: requestOptions
and urlTransfer
.
interceptResponse
takes two arguments:
requestOptions
- these are options with which request was senturlEvent
- an instance ofroUrlEvent
that is returned on request fulfill