WURFL Microservice Client API for Microsoft .NET

Note: This guide assumes that you have access to a running instance of the WURFL Microservice HTTP Server, either through ScientiaMobile's Docker Registry, the AWS Marketplace, the Azure Marketplace, or other distribution channels. The guide also assumes familiarity with Device Detection (WURFL) and WURFL Capabilities.


WURFL Capabilities

WURFL Capabilities: WURFL capabilities are device properties represented in WURFL. While users of the WURFL Microservice for Docker have access to the list of licensed capabilities, customers who obtained the product through one of the Marketplace will have access to the WURFL capability list, predefined for the product they have licensed. Please note that there are two types of capabilities supported in WURFL: Static and Virtual.

While the difference between the two is mostly immaterial for you as a user as far as their practical usage goes, you still need to use two separate methods to use one or the other.

WURFL Microservice Client API (wmclient): Given an HTTP Request and a capability, the WURFL Client will return the property value.

Note: While the wmclient is an API, it requires interaction with the WURFL Microservice server to work. This introduces some latency (hugely mitigated by a built-in caching layer). For this reason, ScientiaMobile does not refer to the WURFL Microservice Client (wmclient) as a "WURFL API". That name is reserved for the WURFL OnSite APIs.


Obtaining, Installing and Running the wmclient


In order to use the wmclient please follow these instructions:

  1. Obtain the package with the wmclient library from WURFL repository. The file wmclient.dll is located on the dist directory.

  2. Move the WURFL Microservice wmclient.dll file into the Example solution folder.

  3. Open the Example solution in Visual Studio 2015 or above, and make the project inside the Example solution reference wmclient.dll (the 'Add Reference' menu in Visual Studio).

  4. Edit WmClientExample.cs, modifying the Create() call to include the public IP address of your AMI instance or Docker container service (i.g. replacing localhost with the IP address of your running AMI in AWS Marketplace, or the address of your Docker container service).

Note: There are multiple ways in which a service deployed through Docker can be exposed to the world around it. Please refer to your DevOps if in doubt of the actual address at which the service is running.*

  1. Build the Example solution and run it with Visual Studio (or just launch the .exe file created by the build).

  2. Code sample looks like the following:

using System;
using System.Collections.Generic;
using Wmclient;

namespace Example
{
    public class WmClientExample
    {
        static void Main(string[] args)
        {
            try
            {
                // First we need to create a WM client instance, to connect to our WM server API at the specified host and port.
                // Last parameter is a prefix for path, most of the time we won't need it
                WmClient client = WmClient.Create("http" , "localhost", "8080", "");

                if (client != null)
                {
                    // enable cache: by setting the cache size we are also activating the caching option in WM client. 
                    // In order to not use cache, you just to need to omit setCacheSize call
                    client.SetCacheSize(100000);

                    // We ask Wm server API for some Wm server info such as server API version and info about WURFL API and file used by WM server.
                    JSONInfoData info = client.GetInfo();
                    Console.WriteLine("Server info: \n");
                    Console.WriteLine("WURFL API version: " + info.Wurfl_api_version);
                    Console.WriteLine("WM server version: " + info.Wm_version);
                    Console.WriteLine("WURFL file info:" + info.Wurfl_info + '\n');

                    var requestedStaticCaps = new string[]{
                        "brand_name",
                        "model_name"
                    };
                    var requestedVirtualCapabilities = new string[]{
                        "is_smartphone",
                        "form_factor"
                    };
                    // set the capabilities we want to receive from WM server
                    client.SetRequestedStaticCapabilities(requestedStaticCaps);
                    client.SetRequestedVirtualCapabilities(requestedVirtualCapabilities);

                    //var ua = "UCWEB/2.0 (Java; U; MIDP-2.0; Nokia203/20.37) U2/1.0.0 UCBrowser/8.7.0.218 U2/1.0.0 Mobile";
                    //Console.WriteLine("Device lookup for user-agent: " + ua);
                    // Perform a device detection calling WM server API passing the user-agent
                    // JSONDeviceData device = client.LookupUserAgent(ua);

                    var headers = new Dictionary<String, String>();
                    headers.Add("Content-Type", "application/json");
                    headers.Add("Accept-Encoding", "gzip, deflate");
                    headers.Add("Accept-Language", "en");
                    headers.Add("Referer", "https://www.cram.com/flashcards/labor-and-delivery-questions-889210");
                    headers.Add("User-Agent", "Opera/9.80 (Android; Opera Mini/51.0.2254/184.121; U; en) Presto/2.12.423 Version/12.16");
                    headers.Add("X-Clacks-Overhead", "GNU ph");
                    headers.Add("X-Forwarded-For", "110.54.224.195, 82.145.210.235");
                    headers.Add("X-Operamini-Features", "advanced, camera, download, file_system, folding, httpping, pingback, routing, touch, viewport");
                    headers.Add("X-Operamini-Phone", "Android #");
                    headers.Add("X-Operamini-Phone-Ua", "Mozilla/5.0 (Linux; Android 8.1.0; SM-J610G Build/M1AJQ; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 Mobile Safari/537.36");
                    headers.Add("Accept", "text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1");
                    headers.Add("Device-Stock-Ua", "Mozilla/5.0 (Linux; Android 8.1.0; SM-J610G Build/M1AJQ; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 Mobile Safari/537.36");
                    headers.Add("Forwarded", "for=\"110.54.224.195:36350\"");

                    // Perform a device detection calling WM server API passing the whole request headers
                    JSONDeviceData device = client.LookupHeaders(headers);

                    // Let's get the device capabilities and print some of them
                    Console.WriteLine("Detected device WURFL ID: " + device.Capabilities["wurfl_id"]);
                    Console.WriteLine("Detected device brand & model: " + device.Capabilities["brand_name"]
                        + " " + device.Capabilities["model_name"]);
                    Console.WriteLine("Detected device form factor: " + device.Capabilities["form_factor"]);
                    if (device.Capabilities["is_smartphone"].Equals("true"))
                    {
                        Console.WriteLine("This is a smartphone");
                    }

                    // Now let's print all the device capabilities
                    DumpDevice(device);

                    // Get all the device manufacturers, and print the first twenty
                    int limit = 20;
                    String[] deviceMakes = client.GetAllDeviceMakes();
                    Console.WriteLine(String.Format("Print the first {0} Brand of {1}\n", limit, deviceMakes.Length));

                    // Sort the device manufacturer names
                    Array.Sort(deviceMakes);
                    for (int i = 0; i < limit; i++)
                    {
                        Console.WriteLine(String.Format(" - {0}\n", deviceMakes[i]));
                    }

                    // Now call the WM server to get all device model and marketing names produced by Apple
                    Console.WriteLine("Print all Model for the Apple Brand");
                    JSONModelMktName[] devNames = client.GetAllDevicesForMake("Apple");

                    // Sort ModelMktName objects by their model name (a specific comparer is used)
                    Array.Sort(devNames, new ByModelNameComparer());
                    foreach (JSONModelMktName modelMktName in devNames)
                    {
                        Console.WriteLine(" - {0} {1}\n", modelMktName.Model_Name, modelMktName.Marketing_Name);
                    }

                    // Now call the WM server to get all operative system names
                    Console.WriteLine("Print the list of OSes");
                    String[] oses = client.GetAllOSes();
                    // Sort and print all OS names
                    Array.Sort(oses);
                    foreach (String os in oses)
                    {
                        Console.WriteLine(String.Format(" - {0}\n", os));
                    }
                    // Let's call the WM server to get all version of the Android OS
                    Console.WriteLine("Print all versions for the Android OS");
                    String[] osVersions = client.GetAllVersionsForOS("Android");
                    // Sort all Android version numbers and print them.
                    Array.Sort(osVersions);
                    foreach (String ver in osVersions)
                    {
                        Console.WriteLine(" - {0}\n", ver);
                    }
                }
                else
                {
                    Console.WriteLine("WmClient instance is null");
                }
                // Deallocate all client resources. Any call on client method after this one will throw a WmException
                client.DestroyConnection();
            }
            catch (WmException e)
            {
                // problems such as network errors  or internal server problems
                Console.WriteLine(e.Message);
            }

            finally
            {

                Console.Write("Press a key...");
                Console.ReadKey();
            }
        }

        // Prints all the device capabilities
        private static void DumpDevice(JSONDeviceData device)
        {
            if(device.Capabilities.Count > 0)
            {
                Console.WriteLine("Requested device capabilities");
                Console.WriteLine();
                var capKeys = device.Capabilities.Keys;

                foreach(string key in capKeys)
                {
                    Console.WriteLine("Capability " + key + ": " + device.Capabilities[key]);
                }
            }
        }
    }

    // Comparer implementation used to sort JSONModelMktName objects according to their model name property, 
    // for which is used the String natural ordering.
    internal class ByModelNameComparer : IComparer<JSONModelMktName>
    {
        public int Compare(JSONModelMktName o1, JSONModelMktName o2)
        {
            if (o1 == null && o2 == null) { return 0; }
            if (o1 == null && o2 != null)
            {
                return 1;
            }

            if (o1 != null && o2 == null)
            {
                return -1;
            }

            return o1.Model_Name.CompareTo(o2.Model_Name);
        }
    }
}

If you are using device detection from code running as part of an HTTP server, it is highly recommended that you pass the complete HttpRequest object to the LookupRequest method as follows:

JSONDeviceData device = client.LookupRequest(request);

This will provide optimal detection accuracy (see examples/web.php). For a complete reference of all static and virtual capabilities, you can refer to this document. Please note that WURFL Microservice for the AWS Marketplace comes with a predefined set of capabilities.

Complete reference of the WmClient API


WmClient Class exposes the following members:

Properties

Name Description
Host The WM server host to connect to.
MakeModels
Port Port of the host to connect to.
StaticCaps
VirtualCaps

Top Methods

Name Description
Create Creates a WURFL private cloud client and tests if server denoted by the given host and port is available for connection. Throws WmClientException in case server is down or not available for connection.
DestroyConnection Diposes all internal resources used (ie: the instance of HttpClient). Calling API methods.
GetApiVersion Returns the current client version.
GetInfo Returns information about the running WM server and API. Throws WmClientException in case any client related problem occurs.
HasStaticCapability(string capName) Returns true if the given capName exist in this client' staticcapability set, false otherwise.
HasVirtualCapability(string capName) Returns,true if the given capName exist in this client' virtual capability set, false otherwise.
LookupDeviceID(string wurfl_id) Searches WURFL device data using the given WURFL device id for detection. Throws WmClientException in case any client related problem occurs.
LookupRequest(HttpRequest req) Searches WURFL device data using the given HttpRequest headers for detection. Throws WmClientException in case any client related problem occurs.
LookupUserAgent(string userAgent) Searches WURFL device data using the given user-agent for detection. Throws WmClientException in case any client related problem occurs.
SetCacheSize(int size) Set the client internal cache maximum size.
SetRequestedStaticCapabilities(string[] caps) Sets the list of static capabilities to be requested to WM server.
SetRequestedVirtualCapabilities(string[] vcaps) Sets the list of virtual capabilities to be requested to WM server.
GetAllDeviceMakes Returns a list of all device manufacturers. Throws WmClientException in case any client related problem occurs
GetAllDevicesForMake(string make) Returns a list of all devices for the given manufacturer. Throws WmClientException in case any client related problem occurs
GetAllOSes() Returns a list of all device OSes. Throws WmClientException in case any client related problem occurs.
GetAllVersionsForOS(string osName) Returns all OS versions for the given OS name. Throws WmClientException in case any client related problem occurs.

JSONDeviceData Class holds the detected device data received from WM server

JSONDeviceData type exposes the following members:

Constructors

Name Description
APIVersion Returns the API version.
Capabilities List of the capability values for the detected device.
Error Returns a message if any error occurred during device detection, or an empty string if detection task returned a correct result.
Ltime Time of the WURFL.xml last load on server
Mtime This objects's generation time

JSONInfoData Class holds informations about wurfl private cloud server and API.

JSONInfoData type exposes the following members:

Constructors

Name Description
Ltime Time of the WURFL.xml last load on server
Static_caps List of static capabilities supported by currently running server
Virtual_caps List of virtual capabilities supported by currently running server
Wm_version Version of the WM server
Wurfl_api_version Version of WURFL API used by the WM server
Wurfl_info Information about the WURFL file used by the WM server

JSONMakeModel Class models simple device "identity" data in JSON format.

JSONMakeModel type exposes the following members:

Constructors

Name Description
JSONMakeModel

Top Properties

Name Description
Brand_Name Value of the WURFL capability brand_name
Marketing_Name Value of the WURFL capability marketing_name
Model_Name Value of the WURFL capability model_name

WmException Class is a general purpose exception thrown whenever an unrecoverable error occurs during device detection (ie: no connection available to WM server, wrong url or port configurations, etc.

Constructors

Name Description
WmException(String) Creates a WmClientException with the given error message.
WmException(String, Exception) Creates a WmException with the given error message and the exception that caused the current one. (ie: if uri is invalid, innerException could be an UriSyntaxException).


Error in case of Missing Capabilities

As mentioned above, depending on the configuration of your WURFL Microservice, one or more WURFL capabilities may be unavailable. In this case, the JSONDeviceData.Capabilities dictionary will not contain an entry for those capabilities and the caller will get a KeyNotFoundException. To avoid KeyNotFoundException you must verify the presence of the capability before retrieving it.

Presence of a capability can be checked with:

if(JSONDeviceData.Capabilities.ContainsKey("has_cellular_radio") {
    value = JSONDeviceData.capabilities["has_cellular_radio"];
}

Another option is to use method:

string value;
if (JSONDeviceData.capabilities.TryGetValue("has_cellular_radio", out value))
{
// Do something with value value
}

Additional capabilities can always be obtained by upgrading to greater version of the AMIs in the AWS Marketplace or by contacting ScientiaMobile to license additional capabilities for the Docker Image or other products.




© 2024 ScientiaMobile Inc.
All Rights Reserved.

NOTICE: All information contained herein is, and remains the property of ScientiaMobile Incorporated and its suppliers, if any. The intellectual and technical concepts contained herein are proprietary to ScientiaMobile Incorporated and its suppliers and may be covered by U.S. and Foreign Patents, patents in process, and are protected by trade secret or copyright law. Dissemination of this information or reproduction of this material is strictly forbidden unless prior written permission is obtained from ScientiaMobile Incorporated.