xWeb: Train
What is xWeb?
netFORUM adheres to three tier design architecture. This means that the software is divided into layers:
- one for presentation
- one for business logic
- one for data access.
In netFORUM, examples of presentation layers are eWeb, or iWeb -- both are different ways of viewing information in netFORUM. One creates new business logic when they make components and workflows. Finally, all interaction is passed through the netFORUM data classes which manage the persistence of objects in a SQL database.
Where does xWeb fit into this model? xWeb is a way to interact directly with the business logic -- the components of netFORUM. Because there are no restrictions imposed by the presentation layer, it's a much more powerful interaction and must be used with great care.
But how do you interact with netFORUM if there are no web pages to do so?
xWeb is a Web Services API. It uses the universal protocol SOAP to send and receive messages from your application. These interactions are defined by a Web Service Definition Language (WSDL).
Exercise
Examine the structure of a WSDL.
Dicussion
- What are Complex types?
- What are Messages?
- What is the significance of the tns: prefix?
Authentication Overview
Before we turn on our services for the first time, given the sensitivity of the data we can retrieve using xWeb, the first step is making sure that our direct interactions with the business layer will be secured from outside intrusions.
By now you're familiar with the authentication systems that netFORUM uses.
Logging into an iWeb account validates using Windows Authentication. This is a domain account. When you attempt to access a restricted path secured using Windows Authentication, a popup will request your username and password. This would work for web services, but an iWeb log in requires a great deal of dependencies and an active session.
Logging into eWeb is done through Forms Authentication and checking a hashed password on the customer table. Most websites that you frequent on the web will use a variation of forms authentication. Forms authentication will display a log in and password box on the web page itself, and POST the information to the server to verify. While this model works well for presentation driven sites, like eWeb, we can't apply this model effectively to xWeb.
So where does xWeb fit in? xWeb uses a completely from-ground-up approach to authentication, called authentication tokens. When a server requests access, they first ask for one of these tokens to authenticate the machine account. Using an authentication token allows netFORUM to cache credentials, and granularly log communication to and from servers.
The usage of an authentication token is simple. Each request to the server, must be accompanied by the sender's token in the header. Remember, we define sender as the server from which the request came.
Learn more
- Users vs customers set of permissions
- Authentication model
Setup
Because the log in process for an xWeb user is fundamentally different than an iWeb or eWeb user, they must be configured separately. Before we can execute our first query, we need to set up an xWeb user.
Setting up an xWebUser
- navigate to the Admin module
- find an existing user to modify, or create a new user
- edit the user
- set up xWeb password
- set row limit
I want more rows than that!
A row limit in xWeb is the maximum number of rows that can be returned from a GetQuery statement. Abila recommends that you set this setting even if you on occasion want more than that number of rows. You'll be able to override the row limit in a query statement for when it doesn't apply. Row limit is a safety net that prevents misuse of the GetQuery statement.
Object security
An xWeb user can be limit to perform actions on specific objects in netFORUM. This a particularly powerful integration feature as it allows you to limit the access that vendors and partners have to your secure data.
Utilities (SOAPUI, STORM)
Service architectures speak their own language, SOAP messages.
These SOAP messages, or envelopes, cannot be constructed using your web browser, so to be able to test out services before bringing them into your development platform, you'll need a third-party utility.
Abila recommends the utility SOAPUI. SOAPUI will allow you to connect to an existing endpoint and generates a proxy class in the background, which means that you can communicate with xWeb just like your future applications will.
Let's configure SOAPUI to point to our WSDL now.
- Navigate to File, New Project
- Paste your WSDL url into 'Initial WSDL'
- Uncheck 'Create sample requests for all operations'
- Click OK
xWeb Essentials
The core utilities xWeb are those that allow to
- Authenticate
- Request data
- Unify customer login (SSO)
Authenticate
- Create new authentication request.
- Replace '?' with the username and password fields
- Execute the request and verify that you now have an authentication token
- Finally, your instructor will verify that you now have an authentication token in the ws_security table
And what is Absolute?
First it's important to note that there are two authentication models, Absolute and Sliding. An absolute token has a fixed window from which it can be used, usually 20 minutes. The token can be reused any amount of times within that 20 minutes, but once the token has expired, it cannot be used again.
A sliding token is perhaps a misnomer. The assumption with a sliding token might be that when a token is used, it's expiration slides to the next window.
However, instead, a sliding token is replaced each time it used. The new token is passed back to the caller in the SOAP header for the next call.
If an error occurs, a token is invalidated. In web services, we will refer to these as faults. You can have a fault occur via bad SQL, query tampering, or even a failed customer login.
If a token is invalidated, it cannot be used again.
It's important to recognize how both of these patterns to be able to make effective, safe web services.
Discussion
- What scenarios might I opt for a sliding vs absolute?
- What effects does sliding have on parallelism that might make Absolute be a better choice in high-volume traffic?
Final notes on Authentication
Most settings that we've discussed in authentication can be changed, and some authentication features can be disabled entirely, using the value -1.
Here are a few of the configuration options available at your disposal.
<!--
Expiration defines the type of expiration, valid values are Sliding|Absolute.
Timeout sets the time interval that the token will remain valid.
-->
<xWebConfig Expiration="Sliding" Timeout="20" LockOutHours="6" FailCount="10" MethodsFaultLimitPerDay="100"/>
<appSettings>...
Timeout
Sets the time interval (in minutes) that the token will remain valid. The default value is 20 minutes.
FailCount
The number of authentication attempts per distinct user account + IP Address combination that yield a credential verification error. If the requesting party attempts to authenticate more than the FailCount value, then the requesting party will receive a LockOut soap:fault.
The value of this attribute and the LockOutHours support each other. The default value is 25. A value of -1 will disable this check. If this number is exceeded you will receive the error message "Credentials Locked Out" when calling Authenticate.
LockOutHours
Once a requesting party receives a LockOut soap:fault, the value of this attribute governs the number of hours that a requesting party must wait until their account is reset and available for them to commence a message exchange (or netFORUM Admin can reset). The default value is 6 hours.
MethodsFaultLimitPerDay
The number of non-authenticating soap:faults allowed per day per distinct user account + IP Address combination. If the requesting party exceeds this number, then they will have to wait until the next day to re-commence message exchange (or netFORUM Admin can reset). The default value is 100. A value of -1 will disable this check.
Retrieve Data
By now you're probably familiar with the functionality of the Query_Tool_3.0 in iWeb.
Like a search, a query allows you to work directly with the list tables to retrieve data faster than when dealing with standalone objects, but safer than when dealing with raw SQLl. Remember that not all object columns are available on a list table.
Running these queries is exposed in xWeb as GetQuery. It's the second-most dynamic method in the feature set of xWeb.
- Create a new GetQuery request.
- Run Authentication to retrieve a new token
- Populate the authentication token in the request.
Now, we will populate our query. Sending a query to xWeb incorporates four parts.
- ObjectName (instead of table name)
- ColumnList
- WhereClause
- OrderBy
Keep in mind that although the parameters look like sql values, that no table names should be used. Instead, a query is leveraged against an Object. For instance, queries should NOT be made against co_individual, but rather, individual.
Imagine cutting up a SQL expression.
SELECT ind_first_name, ind_last_name FROM individual WHERE ind_first_name='Aaron' ORDER BY ind_last_name
- objectName: Individual
- ColumnList: ind_first_name,ind_last_name
- whereClause: ind_first_name='Aaron'
- OrderBy: ind_first_name
The exceptions to this rule are tags that define query engine behavior. These tags are placed on the objectName. For instance, to indicate that only 1 row should be returned, the tag @TOP 1 would be added to the objectName.
Exercises
- retrieve the individuals with a first name starting with 'a'
- retrieve only one individual by with an empty GUID.
Going further
When forming a query, you can select only the columns that are available on the list table for the object.
What if you can't get at the data you need?
Consider Execute method. Execute method will allow you use the functionality of any stored procedure.
Alternatively, if you need another table join, it can be added in the list table setup.
Single Sign-On
Previously we saw how to authenticate a server for calls against xWeb.
Often you want to authenticate a customer. This process is called single sign-on, and the principle method provided for this is WebLogin.
WebLogin takes a parameter of username and password, and returns a special value, called a WebToken.
This WebToken, like an Authentication Token, has a limited window of use. Think of a WebToken as uniquely identifying a user session, while an Authentication Token identifies a server session.
Let's log in some customers.
- Create or find a customer from the CRM module and change their password.
- Run your Authenticate request
- Create new WebLogin request.
- Copy the authentication token result from the WebLogin response to the Weblogin request.
- Fill in username and password for WebLogin
- remove the keyOverride element.
- Execute
What can I do with a Web Token
Having a WebToken allows SSO through eWeb.
Knowing that, like authentication tokens, you have a small window in which you can initiate a session on eWeb, ideally, a session should be initiated immediately. To do, you need a way to request a resource from the eWeb site on the page dynamicpage.aspx
In the following case study, at the point of login the WebToken was placed into a session variable, and then retrieved to set the url on an embedded iframe. The iframe was embedded in a masterpage so that no matter what page the end-user was redirected to, their session would be initiated.
After the session is initiated, it must be kept alive. Even though we've exhausted the usefulness of the token, this iframe will continue to ping the eWeb session to keep it active. In using this method, we enable completely transparent cross-domain single sign-on.
However, given that a webtoken does not have a direct relationship to the primary key of the individual (the CutomerKey), but rather to the concept of a session, how can we retrieve data about the logged in user?
The answer lies in the WebValidate, a method that is used solely to retrieve the CustomerKey associated with their webtoken.
I don't need single sign-on. Isn't there some way that I can just get the key of the customer instead?
Recall the keyOverride parameter from WebLogin. When this element is present in the WebLogin request, the CustomerKey will be returned in lieu of a WebToken.
xWeb, bullet-proof
Ideally, authentication tokens should be shared. This presents some practical problems. If an error occurs, for instance, a failure for an end user logging in, the token is invalidated and a new one must be retrieved.
However, during the period of time, the token might be reused to retrieve a list of merchandise available to the end-user. In that scenario, the call will fail.
What should occur in this case?
Ideally, in the case of failure, the corrective action is to retrieve a new authentication token, and retry the call. This avoids 'tossing up' the error to the end user.
Together, examine and evaluate the structure developed in the xWeb eCommerce SDK:
This function serves as a wrapper for an xWeb call. Let's examine what an xWeb wrapper looks like:
First, the CheckAuthenticate checks to see if a new authentication token is required.
The conditions are as follows:
- The service had a known previous failure (a fault).
- The service has never been called.
- We've exceeded the token's expiration window.
If a new authentication token is required, it will call the Authenticate method for us.
Next, the call is attempted. If the call fails, we check to see if the issue was for a bad token. If it was, we'll retry the call once more.
private bool CheckAuthenticate()
{
bool bNeedToAuthenticate = false;
if (bFailedCall)
bNeedToAuthenticate = true;
else if (oWS.AuthorizationTokenValue == null)
bNeedToAuthenticate = true;
else if (String.IsNullOrEmpty(oWS.AuthorizationTokenValue.Token))
bNeedToAuthenticate = true;
else if (dLastCall.AddMinutes(20) < DateTime.Now)
bNeedToAuthenticate = true;
bool bAuthenticateSucceeded = false;
if (bNeedToAuthenticate)
{
bAuthenticateSucceeded = Authenticate();
}
else
bAuthenticateSucceeded = true;
return bAuthenticateSucceeded;
}
public CustomerAddressType WEBAddressInsert(CustomerAddressType oAddress)
{
CustomerAddressType oReturnAddress = null;
if (CheckAuthenticate())
{
try
{
oReturnAddress = oWS.WEBAddressInsert(oAddress);
dLastCall = DateTime.Now;
}
catch (SoapException se)
{
SystemExceptionInfo oException = ReadException(se);
if (oException is InvalidTokenExceptionInfo)
{
bFailedCall = true;
oReturnAddress = oWS.WEBAddressInsert(oAddress);
}
else
throw oException;
}
}
return oReturnAddress;
}
Alternatives
One of the largest weaknesses of this model is that every call requires its own wrapper function. The wrapper classes will continue to grow and rely on copy-and-paste. In order to reduce both the possibility of human error, and the reliance on copy-and-paste, let's consider two alternatives:
T4 Templating
T4 Templating is an extremely flexible templating utility that will allow you to auto-generate the wrappers for xWeb. It uses a syntax similar to ASP.
public class <#= this.ClassName #>
{
public static void HelloWorld()
{
Console.WriteLine("Hello World");
}
}
<#+
string ClassName = "MyClass";
#>
would create the following:
public class MyClass
{
public static void HelloWorld()
{
Console.WriteLine("Hello World");
}
}
When new features, classes, and methods are added to netFORUM, all you would need to do to take advantage would be to re-run the template.
Surrogates
Creating the xWeb runner:
Once you create a generic surrogate, it can be used in the following manner.
CustomerEmailType EmailType = new xWebRunner<CustomerEmailType>().Execute(xWebAuthenticationProvider, xWebWS =>
{
return xWebWS.oWS.WEBEmailInsert(oFacade Object);
});
Code that is placed with the confines of our xWeb runner (the inner delegate) will now follow the behaviors we've outlined for best token usage, and will be wrapped around the safety net of token validation. Let's break down the code and examine how it works.
- The <T> defines the return type
- xWebAuthenticationProvider is the xWebService class, and is compatible with the base class in the eCommerce SDK.
- xWebWS is the name of the instance variable that we use to interact with the web service while in the inline delegate
Defining the surrogate:
/// <summary>
/// xWebRunner executes a delegate in the context of the token authentication protocol.
/// Wrapping a command in an xWebRunner ensures token timeout, token caching, authentication retry after fault, etc
/// </summary>
public class xWebRunner<T>
{
const int RETRYCOUNT = 1;
public delegate T xWebHandler<T>(xWebService xWeb);
public T Execute(xWebService xWeb, xWebHandler<T> Command)
{
int retryCount = 0;
bool retry = true;
T oReturnNode = default(T);
while (retry && retryCount <= RETRYCOUNT)
{
retry = false;
if (xWeb.CheckAuthenticate())
{
try
{
oReturnNode = Command.Invoke(xWeb);
xWeb.UpdateLastCall(true);
}
catch (SoapException se)
{
Exception oException = xWebServiceUtil.ReadException(se);
if (oException is InvalidTokenExceptionInfo)
{
xWeb.UpdateLastCall(false);
retry = true;
retryCount++;
}
else
throw oException;
}
}
}
return oReturnNode;
}
}
Making It Fast
There are two tenets to getting every ounce of speed out of xWeb. The first is in the request, the second is response.
Request
When you create a Query request, always ask yourself:
- do I need the data to be fresh. Where possible, cache as much as possible. Remember that web-services will inherently reduce performance.
- When you do need instant data, do you need the number of rows you requested back. When does @TOP not make sense?
After answering these questions, if you still can't get the performance you need, consider moving to ExecuteMethod and a custom stored procedure.
Response
However, consider your processing of the response as well!
The way that we turn the data in POCOs[2] can dramatically effect deserialization performance. We'll see an alternative means of deseralization in the shopping cart exercises.
Implementing xWeb
How do I get xWeb into my application?
- Visual Studio, new Project
- Create a new ASP.NET Web Application
- Add a web reference to the WSDL
Once you specify a web reference, the relevant classes and methods will be auto-created. The schema for the classes is derived from the web service definition, or WSDL. This process of exchanging metadata allows for incredibly short development cycles, but creates overhead.
Discussion:
- Is there any time when xWeb not the best approach to integration?
- What is it really good at? What is it not so good at?
- What alternatives are there?
An intro to eCommerce and xPath
Understanding the shopping cart (what does it mean to be stateless?)
What does it actually mean to say our shopping cart is stateless. Perhaps the easier to define is what it is not, stateful. To be stateful means to retain knowledge about the previous requests sent by the user.
An example of a stateful system would be the eWeb shopping cart, which remembers your shopping cart in session.
Therefore, to be stateless means to function in isolation, to function solely as an algorithm.
The best analogy for stateless services is calculators. If xWeb is given a shopping cart, and invoice detail, it returns the shopping cart + the new invoice detail.
This model is designed for maximum customization for partners and clients.
However, one should always be cognizant of the limitations of a stateless system, both in performance and in transaction queuing.
What happens if a user clicks an ajax-enabled 'add to cart' button twice? Think back to the example of the calculator. If a user has a preexisting value of 2 and adds 2 more, if the button press happens *before* the calculator renders four, the answer will be four, not six.
Creating the storefront
Let's create a storefront, a listing of current merchandise available. Unfortunately, when we open the method in c# we realize that we do not have an auto-generated class to handle the data structure! Instead, we receive an XmlNode. On occasion, you will find that rather than having a fully realized class 'at-the-ready' for you to implement, the web service instead returns Xml. Often this done because a query or SP is used to power the logic, rather than a facade object.
What do we do now?
Option 1: Working with XSDs
- Execute WEBCentralizedShoppingCartGetMerchandiseList
- Take the XML from the request
- Use XSD.exe to generate an XSD
- Use XSD.exe to generate a c# class
- Serialize the returned XmlNode to a MerchandiseListResult
Class generation
pros:
- allows you to use a familiar notation.
cons:
- slow deserialization & excess data you don't want/need
- brittle definitions (code may break on schema changes)
- generated code is not portable
How do we deal with XmlNodes in a better way? If you've worked with XSL you already know the answer. There is a language that is used solely for the purpose of digging through and XML called XPath. Consider the following XML snippet:
<WEBCentralizedShoppingCartGetMerchandiseListResponse xmlns="http://www.avectra.com/2005/">
<WEBCentralizedShoppingCartGetMerchandiseListResult>
<Results recordReturn="16">
<Result>
<ptp_key>801b44ee-56be-4b41-90e1-9fcdd12ad7bd</ptp_key>
<ptp_code>Equipment</ptp_code>
<ptp_thumbnail>img_bike1.gif</ptp_thumbnail>
<prd_key>3abb744b-4612-4660-9a56-fe5b77a42969</prd_key>
<prd_code>Avectra Shirt</prd_code>
<prd_name>Avectra T-Shirt</prd_name>
...
</Result>
What do we really want to extract from this data? Probably a list of the result nodes. To do this with xPath, we would say "//Result". The // indicates that all the result nodes in the document are to be included, regardless of their position or hierarchy.
Option 2: Working with XPath
- Execute WEBCentralizedShoppingCartGetMerchandiseList.
- Take the XML from the request.
- Open Cooktop.
- Execute an xPath select for //Result.
- use SelectSingle to retrieve the data you need.
XPath
pros:
- incredibly fast architecture
- extensible, and flexible to schema changes
- XPath is a universal standard, and works *everywhere*
- 'plugs-in' to XSLT
cons:
- there is a learning curve.
add to cart
WEBCentralizedShoppingCartAccreditationSetLineItems.htm
checkout
WEBCentralizedShoppingCartAddLineItem.htm
Helpful libraries
public static XmlNode StripNamespaces(XmlNode Node)
{
XmlDocument d = new XmlDocument();
using (XmlTextReader tr = new XmlTextReader(new StringReader(Node.OuterXml)))
{
tr.Namespaces = false;
d.Load(tr);
}
return d.SelectSingleNode("/");
}
product concepts, ideas, discussion
Reusing components
We've completed our first single-sign on implementation. Certainly we might at some point have a second website, how can we can we package these common components under a unified framework?
If you are creating a .NET solution, consider creating a Membership Provider
- demo a netFORUM membership provider.
What ramifications does having a stateless shopping cart have?
- I'm creating a point-of-sale application. A barcode scanner can read 5 barcodes every 10 seconds. Can I thread transactions to a stateless server? What would happen if I sent a second barcode to add to the cart while the first was still processing?