Class wwJsonServiceClient

This class makes it easy to call JSON REST services with Visual FoxPro. The class manages JSON serialization and HTTP calls in a simple single method interface.

You can use the class directly using CallMethod() method to call a service directly, or - better - create a subclass and build a dedicated service class for multiple calls.

For a more detailed look please check out the following blog post:

You can use this class to:

  • Directly call JSON REST services
  • Subclass it and implement service methods that map a service

Directly call a JSON REST Service

The direct approach just uses the CallService() method directly.

A simple HTTP GET data retrieval could look like this:

loProxy = CREATEOBJECT("wwJsonServiceClient")

lcUrl = "https://albumviewer.west-wind.com/api/artists"
loArtists = loProxy.CallService(lcUrl)

IF (loProxy.lError OR ISNULL(loArtists))
   ? loProxy.cErrorMsg
   RETURN
ENDIF   

*** Service returns an array which is turned into a FoxPro collection
? loArtists.Count  && Collection 
FOREACH(loArtist in loArtists)
   ? loArtist.ArtistName
   ? loArtist.Description
   ? loArtist.ImageUrl
   ? "---"
ENDFOR

Sending Data to the Server

You can also pass data to the service. JSON REST endpoints that expect data typically expect a single JSON parameter - which can be an object with many sub properties - to represent input values. To call the service with an object parameter for example, simply provide a FoxPro object that maps the structure of the JSON expected by the service. wwJsonServiceClient automatically serializes the FoxPro object/value into JSON for you.

To send data to the server, simply pass the lvData parameter in CallService() with a FoxPro value or object and specify the HTTP verb to be used (typically POST or PUT):

loProxy = CREATEOBJECT("wwJsonServiceClient")

lcUrl = "http://albumviewer.west-wind.com/api/album"
lvData = loAlbum && FoxPro object with Album Data
lcVerb = "PUT"   && HTTP Verb

*** Update the Album with the loAlbum data we're sending
loAlbum = loProxy.CallService(lcUrl,lvData,lcVerb)

IF (loProxy.lError)
   ? loProxy.cErrorMsg
   RETURN
ENDIF   

*** Use the updated result album
? loAlbum.Title
? loAlbum.Description

Creating a Service Proxy Class

If you are calling a service that has many methods it's recommended that you create a separate class for all the service calls, similar to a service 'business object' that isolates all logic related to the service in a class. This does several things:

  • Single logical location for all Service related code
  • Consistent interface for calling service methods
  • Easy reuse for service calls throughout application

To use, subclass wwJsonServiceClient and implement your service methods that then using CallService() to call JSON service endpoints. The idea is that each service method you implement completely abstracts the service logic so the calling code simply calls a method with a parameter and a result value - all the service logic is handled internally to the service wrapper class.

Here's what a class looks like calling a couple of service methods:

DEFINE CLASS CustomerServiceClient as wwJsonServiceClient

cServiceBaseUrl = "http://localhost:23332/localizationService.ashx?method="

FUNCTION GetResourceSets()
    LOCAL loResult
    loResult = this.CallService(this.cServiceBaseUrl + "GetResourceSets")

    IF (this.lError)
       RETURN .NULL.
    ENDIF
    
    RETURN loResult
ENDFUNC

FUNCTION UpdateResource(loResource)
    LOCAL loResult
    
    *** Service returns update resource
    loResult = this.CallService(this.cServiceBaseUrl + "UpdateResource",loResource,"PUT")
    IF (this.lError)
       RETURN NULL
    ENDIF
    
    RETURN loResult
ENDFUNC

ENDDEFINE

To use this you would then just use:

loProxy = CREATEOBJECT("CustomerServiceClient")

loResources = loProxy.GetResources()
if ISNULL(loResources)
   ? loProxy.cErrorMsg
   RETURN
ENDIF   

? loResources.Count
? loResources.Item(0).Locale
? loResources.Item(0).Text


loResource = GetMyResourceToUpdateFromSomewhere()
loResource = loProxy.UpdateResource(loResource)

if !llResult
   ? loProxy.cErrorMsg
   RETURN
ENDIF   

? loResource.ResourceId
? loResource.Locale
? loResource.Text

This latter approach is preferrable as it treats service access like a business object where all logic created around the service is created in one place. It allows you to reuse the service calls and isolate your error handling and future maintenance all in one place.

Custom
  wwJsonServiceClient

Class Members

MemberDescription

CallService

o.CallService(lcUrl,lvData,lcVerb)

CreateSerializer

o.CreateSerializer(loSerializer)

CreatewwHttp

o.CreatewwHttp(loHttp)

cErrorMsg

An error message if an error occurs and .lError is .T.

cServiceUrl

lError

Flag that is set if either the HTTP request or JSON Serialization failed. If .T. the .cErrorMsg is also set.

lNoUtf8Decoding

By default all JSON responses are decoded from UTF-8. If the response content is not UTF-8 encoded set this value to .T.

JSON output is almost always returned in UTF-8 format, so you should never have to set this flag. It's here for those rare exceptions.

Example


************************************************************************
DEFINE CLASS CustomerServiceClient as wwJsonServiceClient
*********************************************************

************************************************************************
*  GetCustomers
***************
FUNCTION Customers(loParms)
LOCAL loCustomers

loCustomers = THIS.CallService(this.cServiceBaseUrl + "customers.csvc", loParms)

IF THIS.lError
   RETURN null
ENDIF   

RETURN loCustomers
ENDFUNC
*   GetCustomers

************************************************************************
*  UpdateCustomer
*****************
FUNCTION UpdateCustomer(loCustomer)

loCustomer = THIS.CallService(this.cServiceBaseUrl + "customer.csvc",loCustomer,"PUT")

IF THIS.lError
   RETURN null
ENDIF 

RETURN loCustomer
ENDFUNC
*   UpdateCustomer

ENDDEFINE

Requirements

Assembly: wwJsonSerializer.prg • wwHttp.prg (dependency)

See also:

Class wwJsonServiceClient

© West Wind Technologies, 1996-2020 • Updated: 09/28/20
Comment or report problem with topic