Access HTTP content over the Web
The Client Tools make it easy to access HTTP content using the wwHTTP class. It provides simple functions to retrieve and post content to and from a Web Server as well as full access to advanced HTTP features.
Web content includes anything that is accessible via the HTTP protocol. This can be HTML text, plain text, JSON or XML data, binary content like Zip files, images or PDFs. You can easily use any HTTP Verb like
DELETE etc., access secure content, set and access HTTP headers and automatically handle authentication, redirect and GZip compression/decompression.
The simplest requests are
GET operations to retrieve content only. The following example captures some HTML text from a URL:
DO wwHttp && load dependencies loHttp=CREATEOBJECT("wwHTTP") *** Issue an HTTP GET operation lcHTML = loHttp.Get("https://west-wind.com") ? lcHtml && Raw HTML
The raw result from the request is returned as a string in the return value (or a result file via the
llDownloadFile parameter ) and in most cases that may be all you need to know.
For proper content and result identification - especially for API calls or binary downloads - you also should check for errors and frequently look at the HTTP headers to identify the content type, and the response code of whether a request worked or not and what type of content it returned. You can use the HTTP Headers to do this.
lcHTML = loHttp.Get("https://markdownmonster.west-wind.com") *** Check for errors IF loHttp.nError != 0 ? loHttp.cErrorMsg RETURN ENDIF *** Get HTTP Response Code ? loHttp.cResultCode && 200, 500, 401, 404, 401.1 etc. *** Get full Http Response Header (multiple lines) ? loHttp.cHttpHeaders *** Retrieve any HTTP header from the result ? loHttp.GetHttpHeader("Content-Type")
HTTP headers for the request above look like this:
HTTP/1.1 200 OK Cache-Control: private Content-Type: text/html; charset=utf-8 Server: Microsoft-IIS/10.0 Date: Tue, 23 Feb 2021 21:13:55 GMT Content-Length: 39718
When making HTTP calls you should always check for errors after the call to ensure you got data. At minimum check for empty results, but you can use the
cErrorMsgproperties to get detailed error information.
Http headers includes information about a request - like the Content Type, Encoding, Content Length, Authentication Information as well as any custom headers the server is sending. Not every request needs to look at this, but it's important for generic applications that process HTTP content, or even for binary downloads that need to determine what type of content you are dealing with (for example downloading an image or file and saving it to disk).
To post data to a server you generally use either
.Put(). Semantically POST is used for adding, PUT for updating, but for most Web backends this distinction is not significant and you can use the two interchangeably. POST tends to be more common.
To post data to a server using HTML Form style form submissions with urlencoded Key/Value pairs (
application/x-www-form-urlencoded content), you can use the
.AddPostKey() method. Use that method to add key and value pairs to POST to the server. You can also add HttpHeaders by using
.AppendHeader() to add any standard or custom HTTP headers to your request.
You then call
.Put()) to make the request:
loHttp=CREATEOBJECT("wwHTTP") *** Add POST form variables (url encoded by default) loHttp.AddPostKey("FirstName","Rick") loHttp.AddPostKey("LastName","Strahl") loHttp.AddPostKey("Company","West Wind Technologies") *** Optionally add headers loHttp.AppendHeader("Authorization","Bearer c42ffadef3f3aa3d5c97da77e") loHttp.AppendHeader("Custom-Header","Custom value") lcHTML = loHttp.Post("https://west-wind.com/wconnect/TestPage.wwd") ShowHTML( lcHTML )
This formats the HTTP POST buffer for sending the variables to the Web server which simulates an HTML Web form submission which is quite common.
The above example posts using Html Form type POST data that is provided in key value pairs. wwHttp supports 2 form data post modes via the
- 1 -
application/x-www-form-urlencoded- html form based encoding
- 2 -
multi-part/form-data- used for file uploads and forms with binary data
For raw data posts that provide a full buffer of data as-is rather than the key/value pairs shown above, you can can use the
cContentType property to specify the content type, then POST the raw data as a single string.
The following is sufficient for sending JSON content to the server:
loHttp.cContentType = "application/json" loHttp.Post(lcUrl, lcJson)
cContentType- but don't use both! Use
nHttpPostModefor form submissions with
cContentTypeto send raw buffers with
AddPostKey(lcData)or directly in
A common scenario is to post JSON or XML to a server for some sort of Service access. In this case you'll need to use the
PUT HTTP operation and set the
cContentType to the appropriate content type like
text/xml for example.
To send XML data to a server you can use the following code:
loHttp = CREATEOBJECT("wwHttp") *** Specify that we want to post raw data and a custom content type loHttp.cContentType = "text/xml" && Content type of the data posted *** Explicitly specify HTTP verb optionall * loHttp.cHttpVerb = "POST" && (PUT/DELETE/HEAD/OPTIONS) *** Load up the XML data any way you need lcXML = FILETOSTR("XmlData.xml") lcXmlResult = loHttp.Post("http://www.west-wind.com/SomeXmlService.xsvc", lcXml) IF (loHttp.nError # 0) RETURN ENDIF ShowXml(lcXmlResult)
If you've been using
wwHttp for a while you might note that this syntax of using
.Post() is new as is the
lcData parameter. The old syntax that uses
.AddPostKey(lcData) also still works:
with the same behavior as the example above. The new
.Post() syntax is just simpler and more common for HTTP requests.
REST services are the latest rage, and when using REST you typically deal with JSON data instead of XML. The client tools include a wwJsonSerializer class that can handle serialization and deserialization for you.
DO wwHttp DO wwJsonSerializer loHttp = CREATEOBJECT("wwHttp") loHttp.cContentType = "application/json; charset=utf-8" lcJsonResult = loHttp.Post(lcUrl, STRCONV(lcJsonIn,9)) loResultObject = loSer.DeserializeJson(lcJsonResult) *** Do something with the result object ? loResultObject.status ? loResultObject.Data.SummaryValue
To make things even easier with JSON REST Services take a look at the JsonServiceClient class which handles all the HTTP calls and serialization and error handling all via single
The following sends JSON data to a service and receives a JSON result back as a FoxPro object:
loProxy = CREATEOBJECT("wwJsonServiceClient") lcUrl = "http://albumviewer.west-wind.com/api/album" lvData = loAlbum && FoxPro object lcVerb = "PUT" && HTTP Verb *** Make the service call and returns an Album object loAlbum2 = loProxy.CallService(lcUrl, lvData, lcVerb) IF (loProxy.lError) ? loProxy.cErrorMsg RETURN ENDIF ? loAlbum2.Title ? loAlbum2.Artist.ArtistName
You can also send binary data, which in FoxPro is just represented as a string. Make sure you set the
.cContentType property to specify what you are sending to the server:
loHttp = CREATEOBJECT("wwHttp") *** Specify that we want to post raw data with a custom content type loHttp.cContentType = "application/pdf" lcPdf = FILETOSTR("Invoice.pdf") loHttp.AddPostKey(lcPdf) && Add as raw string *** Send to server and retrieve result lcHtml = loHttp.Post("http://www.west-wind.com/SomeUril.xsvc")
Note that you can use the cContentType property to specify the content type of the data POSTed to the server.
Many APIs and applications may also require form based file uploads which uses a special HTTP format (known as multi-part encoding) to upload files via HTTP. This format requires that you use
nHttpPostMode=2 and you can then use
AddPostKey() to add form variables to the POST operation:
loHTTP = CREATEOBJECT("wwHTTP") *** Posting a multi-part form with a File Upload loHTTP.nHttpPostMode = 2 && Multipart form encoding *** Post a file loHttp.AddPostFile("File","d:\temp\wws_invoice.pdf",.T.) *** Post normal text (part of the same form) loHttp.AddPostKey("txtFileNotes","test note") lcHTML = loHTTP.Post("http://localhost/wconnect/FileUpload.wwd")
You can also stream the content from a URL directly to a file which is useful to avoid FoxPro's 16mb string limit and also because it's faster and less resource intensive.
To do this you can use additional parameters on the
.Delete() methods which take an
lcOutputFilename parameter to allow streaming the HTTP data into. This allows for large files to be downloaded without tying up memory as they normally would.
loHttp = CREATEOBJECT('wwhttp') loHttp.Get("http://www.west-wind.com/","c:\test.htm") IF loHttp.nError # 0 wait window loHttp.cErrorMsg return ENDIF *** Browse the file from local disk GoUrl("c:\test.htm")
Streaming to file allows you to bypass large memory usage as
wwHTTP directly streams into the file with small chunks read from the server. This allows files much larger than 16mb to be downloaded.
wwHttp also includes an OnHttpBufferUpdate event wich provides you download progress information via an event handler on the wwHttp class. You can find out more on how this works and an example here:
Comment or report problem with topic