amr-it
asked on
Best Practice for integration against a WebService
Hello there experts!
To start off with, I am not very experienced with professional development, but I guess we've all been there.
I am trying to develop and integrate to a fairly large system, offering more than 10 Webservice "Managers" with an average of 50 methods per manager. Just to skip the details about the system, let's call it a device communication / management system.
The system, as mentioned, offers more than 10 webservices (managers) containing lots of different methods. Each method takes different parameters, specific to the method, and aslo a webservice security key which can be retrieved from the LoginManager.Login method.
The parameters are specified in a string formatted XML.
Example:
I do know I have a lot of work to cover all the methods and to be able to parse all the returned data. But what I do want help with, is what is the best practice working with something like this? The process from building the parameters in a smart way, passing them to the method, validating the received data and serialize it to objects.
The system provider, do not offer any XSDs, but examples of the return data.
I have been playing around with VS 2010 xsd.exe to create classes and schemas, but this is very time consuming.
Should I typically create a base class, with subclasses for each manager and their methods? Along with a class for the method parameters?
To illustrate an example, the following is from the actual documentation I have.
Due to privacy and confidentially I have shortened the parameters, but you will get the idea.
My apologies if the XML broke while editing.
RetrieveList
public string RetrieveList(string sKey, string sXmlParameters)
Retrieves a list of devices based on a set of restriction criteria. This API also supports
paging through items that match the restriction criteria.
Parameters:
Name: sKey Type: string
Description: The API security key. A valid API security key is required for every API call.
Name: sXmlParameters Type: string
Description: Device fields and their values used to restrict the retrieval. See the Additional
Information section for XML format.
Return Value:
Type: string
Description: Returns XML containing the status of the list retrieval and if the status is
ReturnCodes.SUCCEEDED, a list of devices matching the
search criteria and information used for paging through data. The devices are sorted by
device name in ascending order, then by device ID in ascending order. See the
Additional Information section for the return API payload format
sXmlParameters format:
Return API Payload format:
Please let me know if you want more details.
Thank you,
amr-it
To start off with, I am not very experienced with professional development, but I guess we've all been there.
I am trying to develop and integrate to a fairly large system, offering more than 10 Webservice "Managers" with an average of 50 methods per manager. Just to skip the details about the system, let's call it a device communication / management system.
The system, as mentioned, offers more than 10 webservices (managers) containing lots of different methods. Each method takes different parameters, specific to the method, and aslo a webservice security key which can be retrieved from the LoginManager.Login method.
The parameters are specified in a string formatted XML.
Example:
<PARAMETERS><DEVICE><ID>123456</ID></DEVICE></PARAMETERS>
I do know I have a lot of work to cover all the methods and to be able to parse all the returned data. But what I do want help with, is what is the best practice working with something like this? The process from building the parameters in a smart way, passing them to the method, validating the received data and serialize it to objects.
The system provider, do not offer any XSDs, but examples of the return data.
I have been playing around with VS 2010 xsd.exe to create classes and schemas, but this is very time consuming.
Should I typically create a base class, with subclasses for each manager and their methods? Along with a class for the method parameters?
To illustrate an example, the following is from the actual documentation I have.
Due to privacy and confidentially I have shortened the parameters, but you will get the idea.
My apologies if the XML broke while editing.
RetrieveList
public string RetrieveList(string sKey, string sXmlParameters)
Retrieves a list of devices based on a set of restriction criteria. This API also supports
paging through items that match the restriction criteria.
Parameters:
Name: sKey Type: string
Description: The API security key. A valid API security key is required for every API call.
Name: sXmlParameters Type: string
Description: Device fields and their values used to restrict the retrieval. See the Additional
Information section for XML format.
Return Value:
Type: string
Description: Returns XML containing the status of the list retrieval and if the status is
ReturnCodes.SUCCEEDED, a list of devices matching the
search criteria and information used for paging through data. The devices are sorted by
device name in ascending order, then by device ID in ascending order. See the
Additional Information section for the return API payload format
sXmlParameters format:
<PARAMETERS>
<DATACONTENTRETURNTYPEID>
string from the DataContentReturnTypes class. Specifies
what data content (list of devices only, list of devices and
count, or count of devices only) should be returned in the return XML.
</DATACONTENTRETURNTYPEID> (optional - if not specified a value of Constants.DataContentReturnTypes.LIST_ONLY will be used)
<DEVICES>
<DEVICE>
<ID>
string - 32-character GUID.
</ID> (optional - if specified, the Name and Serial Number
nodes cannot be included. If not specified, either the
Name node or the Serial Number node must be specified.)
<NAME>
<VALUE>
string - up to 64 characters. The device name to use to restrict the retrieve.
If the value specified in the Match Type ID node is ValueMatchTypes.CONTAINS,
certain characters can be included and used as wildcard characters.
</VALUE>
<MATCHTYPEID>
string from ValueMatchTypes class.
Indicates whether the devices returned should have a name that
equals the value in the Value node or just contains the value in the Value node.
</MATCHTYPEID> (optional - if not specified, ValueMatchTypes.EQUALS is used)
</NAME> (optional - if specified, the ID and Serial Number nodes cannot be included. If not specified, either the
Serial Number node or the ID node must be specified.)
<SERIALNUMBER>
<VALUE>
blank or string - up to 64 characters.
The device serial number to use
to restrict the retrieve. If the value
specified in the Match Type ID node
ValueMatchTypes.CONTAINS,
certain characters can be
included and used as wildcard
characters.
</VALUE>
<MATCHTYPEID>
string from
ValueMatchTypes class.
</MATCHTYPEID> (optional - if not specified,
ValueMatchTypes.EQUALS is used)
</SERIALNUMBER> (optional - if specified, the ID and Name
nodes cannot be included. If not specified, either the Name
node or the ID node must be specified.)
</DEVICE>
...
</DEVICES> (optional, if included at least one Device node must be included)
<TYPEID>
string from DeviceTypes class. The device type ID to use to restrict the retrieve.
</TYPEID> (optional)
<STATUSTYPES>
<STATUSTYPE>
<ID>
string from DeviceStatus class. The device
status type ID to use to restrict the retrieve.
</ID>
</STATUSTYPE>
...
</STATUSTYPES> (optional)
<STARTINSTALLATIONDATETIME>
date time in UTC - greater than or equal to 1753-01-01 00:00:00.000
and less than or equal to the end date time, if included, otherwise
less than or equal to 9999-12-31 23:59:59.998. Devices returned
will have an installation date time that occurred on or after the
date time specified.
</STARTINSTALLATIONDATETIME> (optional)
<ENDINSTALLATIONDATETIME>
date time in UTC - greater than or equal to the start date time, if
included, otherwise greater than or equal to 1753-01-01 00:00:00.000
and less than or equal to 9999-12-31 23:59:59.998. Devices returned
will have an installation date time that occurred on or before the
date time specified.
</ENDINSTALLATIONDATETIME> (optional)
<MAXCOUNT>
integer - greater than 0 and less than or equal to 2,147,483,647. The
maximum number of devices to return.
</MAXCOUNT> (optional - if not specified, the value of the Retrieve
<SORTOPTIONS>
<SORTOPTION>
<SORTBYTYPEID>
string from DeviceRetrieveListSortByTypes class.
Indicates what the devices returned should be sorted by.
</SORTBYTYPEID>
<SORTORDERTYPEID>
string from SortOrderTypes class.
Indicates whether the sort by type specified is ordered ascending or descending.
</SORTORDERTYPEID>
</SORTOPTION>*
</SORTOPTIONS> (optional, if not included and the Data Content Return Type
ID node is not included or is included with the value of
DataContentReturnTypes.LIST_ONLY
or DataContentReturnCodes.LIST_AND_COUNT,
<DATASET>
<NEXT>
<DEVICEID>
string - up to 32 characters. The device ID used to retrieve the next
set of devices matching the restriction criteria in sXmlParameters.
</DEVICEID>
<!--If the Sort Options node is not included or if the sort by type ID is
specified as GatewayRetrieveListSortByTypes.DEVICE_NAME
the following node is required-->
<DEVICENAME>
string - up to 64 characters. The device name used to retrieve the
next set of devices matching the restriction criteria in sXmlParameters.
</DEVICENAME>
<!--If the Sort Options node is specified as
GatewayRetrieveListSortByTypes.GATEWAY_NAME the
following nodes are required-->
<DEVICENAME>
string - up to 64 characters. The device name used to retrieve the
next set of devices matching the restriction criteria in
sXmlParameters.
</DEVICENAME>
<DATETIME>
date time in UTC. The last Device Delta Load Profile result
received date time used to retrieve the next set of devices
matching the restriction criteria in sXmlParameters.
</DATETIME>
</LASTRECEIVEDRESULT>
<DEVICENAME>
string - up to 64 characters. The device name used to retrieve the
next set of devices matching the restriction criteria in sXmlParameters.
</DEVICENAME>
</NEXT>
</DATASET>(Optional)
</PARAMETERS>
Return API Payload format:
<COUNT>
integer - the total number of devices matching the search criteria
independent of the maximum numbers of devices to be
returned in the list. For example, if the maximum number of
devices to be returned is 50, but 200 match the search
criteria in the XML parameters, the value in this node will
be 200.
</COUNT> (returned only if the value specified in the Data Content Return
Type ID node in the XML parameters was
DataContentReturnTypes.COUNT_ONLY or
DataContentReturnTypes.LIST_AND_COUNT)
<DEVICES>
<DEVICE>
<ID>string - 32-character GUID. The device ID.</ID>
<NAME>string - up to 64 characters. The device name.</NAME>
<TYPEID>
string from DeviceTypes class. The device type.
</TYPEID>
<STATUSTYPEID>
string from DeviceStatus class. The status of the
device
</STATUSTYPEID>
<SERIALNUMBER>
string - up to 64 characters. The device serial number.
</SERIALNUMBER>
</DEVICE>
...
</DEVICES> (returned only if the value specified in the Data Content Return Type ID
node in the XML parameters was DataContentReturnTypes.LIST_ONLY or
DataContentReturnTypes.LIST_AND_COUNT or if the Data Content Return
Type ID node was not included in the XML parameters)
<DATASET>
<NEXT>
<ADDITIONAL>
string of StandardAPIOptions.YES or
StandardAPIOptions.NO. If the number of devices
meeting the restriction criteria specified in
sXmlParameters is less than or equal to the maximum count
of items to be retrieved, the value returned in this node
is StandardAPIOptions.NO. If the number of devices
meeting the restriction criteria specified in sXmlParameters
is more than the maximum count of items to be retrieved the
value returned in this node is
StandardAPIOptions.YES.
</ADDITIONAL>
<DEVICEID>
string - up to 32 characters. The device ID used to retrieve the next
set of devices matching the restriction criteria in sXmlParameters.
</DEVICEID>
<!--If the Sort Options node is not included or if the sort by type ID is
specified as GatewayRetrieveListSortByTypes.DEVICE_NAME
the following node is required-->
<DEVICENAME>
string - up to 64 characters. The device name used to retrieve the
next set of devices matching the restriction criteria in sXmlParameters.
</DEVICENAME>
</NEXT>
</DATASET>
Please let me know if you want more details.
Thank you,
amr-it
ASKER
Best practice may not have been the best explanation of my problem.
But to wrap it into an understandable question, I'd say what would be a good and maintainable way to handle a webservice offering so many possibilities, to gather them into methods that returns serialized objects rather than xml strings, that verifies the parameters, that verifies the response, all in one package where one easily could access the needed method in a quick way when needed.
I might update my question and change the "Best Practice" part. The goal here is to use and re-use the webservice in different applications, big and small without having to develop much more in the core methods.
A returned meter, would always be a meter with its properties according to the webservice/system.
I hope this is better formulated.
Thank you for your feedback.
Cheers
amr-it
But to wrap it into an understandable question, I'd say what would be a good and maintainable way to handle a webservice offering so many possibilities, to gather them into methods that returns serialized objects rather than xml strings, that verifies the parameters, that verifies the response, all in one package where one easily could access the needed method in a quick way when needed.
I might update my question and change the "Best Practice" part. The goal here is to use and re-use the webservice in different applications, big and small without having to develop much more in the core methods.
A returned meter, would always be a meter with its properties according to the webservice/system.
I hope this is better formulated.
Thank you for your feedback.
Cheers
amr-it
It sounds like you are not in control of the web services, you just need to manage how you connect to these web services. It sounds like, on the surface, that you might want to think about a dynamic library, that defines wrappers classes to provide a centralized interface to the web services that translates the calls into something more understandable. I would look into a plug-in interface, where you could easily extend the web service integration library.
(If I understand your requirements)...
(If I understand your requirements)...
ASKER
That sounds about right, I have been looking at different wrapper solutions for other purposes.
Where it gave me the idea to build a wrapper class for each manager, where each method would be included.
And for each manager create the classes needed for the parameters, for simpler re-use and to know what different parameters that can be used for the different methods and to create classes for the return XML to deserialize it to objects.
Some methods would be common for the different managers such as the serialization and deserialization, setting the webservice url and so on.
I would appreciate recommendations for the hierarchy of this wrapper.
Depending on the architechture, going with static classes, all methods could be accessed without creating an instance of the wrapper, but they can't be inherited and so on.
I believe that a strategy has to be made before starting to work with it, creating xsd schemas, going with a static wrapper and so on. Do you have any plug-in/hierarchy recommendations you can give? Some references where i can read more to support me in choosing the more correct approach for my needs?
Cheers,
amr-it
Where it gave me the idea to build a wrapper class for each manager, where each method would be included.
And for each manager create the classes needed for the parameters, for simpler re-use and to know what different parameters that can be used for the different methods and to create classes for the return XML to deserialize it to objects.
Some methods would be common for the different managers such as the serialization and deserialization, setting the webservice url and so on.
I would appreciate recommendations for the hierarchy of this wrapper.
Depending on the architechture, going with static classes, all methods could be accessed without creating an instance of the wrapper, but they can't be inherited and so on.
I believe that a strategy has to be made before starting to work with it, creating xsd schemas, going with a static wrapper and so on. Do you have any plug-in/hierarchy recommendations you can give? Some references where i can read more to support me in choosing the more correct approach for my needs?
Cheers,
amr-it
Can you give me some idea of what you are working with? Web methods, return values, expectations, ... The key to good design is to recognize a pattern, and apply the right tool for the right job.
ASKER
Hi,
I can give you an example of how I have managed it so far:
To use any web method, I have to make a call to the UserManager.Login web method to retrieve a valid APIkey.
To make the call, I have to pass 3 parameters to the web method (string): username, password, authType.
The API would return a XML string similar to:
The key must be retrieved and passed to all other calls. The key is valid for 1 hour.
Example of how I have managed it until now:
Within the Wrapper, I have a property for the APIKey (securityToken) which is assigned from the Login() method (which calls the UserManager.Login() and a property for the webservice url which must be set before any calls. (coreServicesURL).
As a second example, let's say I'd like to update a device using the API.
The DeviceManager.Update takes two parameters, the APIkey and a string XML containing multiple parameters.
This method only returns SUCCESS or a guid to indicate if the call was successful or not. No data is returned.
To make it easier, to know what parameters the API takes, I created a class for the method, to create a serializable object that I can set my parameters to.
Before using the UpdateDevice method, I'd serialize my UpdParameters object to XML and pass it as a string to the method.
Please let me know if you need more information.
The above example was a simpler one, retrieving lists of devices, would need a de-serialization of the result XML to return devices in those methods that would expect it.
My main goal is just to manage all web methods and the entities around these methods in a good and maintainable way. How to hierarchically build the class and wrapper methods.
Somehow, I want to fit everything under one umbrella, in a good design:
cheers
amr-it
I can give you an example of how I have managed it so far:
To use any web method, I have to make a call to the UserManager.Login web method to retrieve a valid APIkey.
To make the call, I have to pass 3 parameters to the web method (string): username, password, authType.
The API would return a XML string similar to:
<RETURN>
<STATUS>Successful</STATUS>
<APIPAYLOAD>
<APIKEY>validAPIkey</APIKEY>
</APIPAYLOAD>
</RETURN>
The key must be retrieved and passed to all other calls. The key is valid for 1 hour.
Example of how I have managed it until now:
Within the Wrapper, I have a property for the APIKey (securityToken) which is assigned from the Login() method (which calls the UserManager.Login() and a property for the webservice url which must be set before any calls. (coreServicesURL).
public void Login()
{
UserManager.UserManager userManager = new UserManager.UserManager();
userManager.Url = coreServicesURL + "/UserManager.asmx";
//get the return XML from the web call
string result = userManager.Login(username, password, UserAuthenticationTypes.DEFAULT);
XmlNode apiPayLoad = null;
//Pass the resul to GetResult helper.
if (GetResult(result, out apiPayLoad))
//If it was successful, get the APIKEY node inner text (key).
securityToken = apiPayLoad.SelectSingleNode("APIKEY").InnerText;
}
public bool GetResult(string result, out XmlNode apiPayLoad)
{
XmlDocument xmlDocument = new XmlDocument();
apiPayLoad = null;
xmlDocument.LoadXml(result);
//If the result indicates it was successful
if (xmlDocument.SelectSingleNode("RETURNS/STATUS").InnerText == ExternalServiceReturnCodes.SUCCEEDED)
{
//Return the APIPayLoad (Could be w/e)
apiPayLoad = xmlDocument.SelectSingleNode("RETURNS/APIPAYLOAD");
return true;
}
else
{
apiPayLoad = xmlDocument.SelectSingleNode("RETURNS/STATUS");
return false;
}
}
As a second example, let's say I'd like to update a device using the API.
The DeviceManager.Update takes two parameters, the APIkey and a string XML containing multiple parameters.
This method only returns SUCCESS or a guid to indicate if the call was successful or not. No data is returned.
To make it easier, to know what parameters the API takes, I created a class for the method, to create a serializable object that I can set my parameters to.
public class UpdateDeviceParameters
{
/// <summary>
/// Name of the device (optional)
/// </summary>
public string NAME { get; set; }
/// <summary>
/// Description of the Device (optional)
/// </summary>
public string DESCRIPTION { get; set; }
/// <summary>
/// Installation date time of the Device (optional)
/// Date time in UTC - Greater than or equal to 9999-12-31 23:59:59.998. The new installation date time for the device.
/// </summary>
public string INSTALLATIONDATETIME { get; set; }
/// <summary>
/// TimeZone of the Device (optional)
/// The new time zone ID for the device. Call the TimeZoneManager.RetrieveList API to get a list of valid time zone IDs.
/// </summary>
public string TIMEZONEID { get; set; }
/// <summary>
/// Hardware version for the device (optional)
/// </summary>
public string HARDWAREVERSION { get; set; }
}
Before using the UpdateDevice method, I'd serialize my UpdParameters object to XML and pass it as a string to the method.
public string UpdateDevice(string DeviceID, string UpdParameters)
{
XmlNode apiPayLoad = null;
DeviceManager devMgr = new DeviceManager();
devMgr.Url = getcoreServicesURL() + "/DeviceManager.asmx";
//getsecurityToken() gets the securitytoken received from the Login method.
string result = devMgr.Update(getsecurityToken(), DeviceID, UpdParameters);
if(GetResult(result, out apiPayLoad))
{
return "Success!";
}
else
{
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(result);
return xmlDocument.SelectSingleNode("RETURNS/STATUS").InnerText;
}
}
public bool GetResult(string result, out XmlNode apiPayLoad)
{
XmlDocument xmlDocument = new XmlDocument();
apiPayLoad = null;
xmlDocument.LoadXml(result);
if (xmlDocument.SelectSingleNode("RETURNS/STATUS").InnerText == ExternalServiceReturnCodes.SUCCEEDED)
{
apiPayLoad = xmlDocument.SelectSingleNode("RETURNS/APIPAYLOAD");
return true;
}
else
{
apiPayLoad = xmlDocument.SelectSingleNode("RETURNS/STATUS");
return false;
}
}
Please let me know if you need more information.
The above example was a simpler one, retrieving lists of devices, would need a de-serialization of the result XML to return devices in those methods that would expect it.
My main goal is just to manage all web methods and the entities around these methods in a good and maintainable way. How to hierarchically build the class and wrapper methods.
Somehow, I want to fit everything under one umbrella, in a good design:
Class Wrapper
Common Property
Common Property
Common Property
Class DeviceManagerWrapper : Wrapper
DevManager Property
DevManager Property
DevManager Property
Method Create()
Class CreateParameters
Method Update()
Class UpdateParameters
Method Delete()
Class DeleteParameters
Class UserManager : Wrapper
UserManager Property
UserManager Property
UserManager Property
Method Login()
Common Entities
Device
Etc..
cheers
amr-it
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
I've requested that this question be deleted for the following reason:
This question has been classified as abandoned and is closed as part of the Cleanup Program. See the recommendation for more details.
This question has been classified as abandoned and is closed as part of the Cleanup Program. See the recommendation for more details.
Ahem, I believe that I answered this question.
Answer = #35736899
https://www.experts-exchange.com/blogs/TheLearnedOne/B_4708-The-Best-We-Can-Do-Is-Better.html
The "best" I can do is to offer what I believe is the "better" choice given my understanding of your situation. I have to admit that I am a "bigger picture" kind of guy, so sometimes these questions where I get too much detail up front are confusing (I get stuck in all the words).
I would like to try from about 10000 meters, to get an understanding of your goal, given your admission that you are "not very experienced with professional development". That way, I can try to find one of a possible set of solutions.
Bob