-----------------------------------

Acquista i software ArcGIS tramite Studio A&T srl, rivenditore autorizzato dei prodotti Esri.

I migliori software GIS, il miglior supporto tecnico!

I migliori software GIS, il miglior supporto tecnico!
Azienda operante nel settore GIS dal 2001, specializzata nell’utilizzo della tecnologia ArcGIS e aderente ai programmi Esri Italia Business Network ed Esri Partner Network

-----------------------------------



domenica 31 marzo 2013

Tutto il resto è Layar ... e non ho detto layer ma Layar Layar Layar ...

Nel 2010 avevo fatto riferimento alla realtà aumentata accennando a Layar. In questo post vediamo come esporre i nostri servizi ArcGIS Server a Layar e visualizzarli geolocalizzati in prossimità del luogo in cui si trova l'utente. I dati sono forniti sotto forma di livelli chiamati layer, che non sono altro che servizi web di tipo REST. I layer sono mantenuti dai pubblicatori mentre Layar è responsabile della loro convalida nel processo di pubblicazione che normalmente avviene entro 5 giorni dalla richiesta di pubblicazione. Poiché stiamo parlando di realtà aumentata su dispositivi mobile (abbiamo a disposizione sia l'app per iOS che per Android) per poter funzionare Layar necessità di GPS, magnetometro (bussola), fotocamera e accelerometro, nonché connessione a internet per ricevere i dati dei servizi online. Il nostro dispositivo inquadra in tempo reale l'ambiente circostante e al mondo reale vengono sovrapposti i livelli di contenuto (layer).

Vediamo come funziona:
  1. un utente lancia il Layer browser sul suo dispositivo mobile;
  2. il Layar Client invia la richiesta al Layar server;
  3. in base alla richiesta il Layar Server restituisce la definizione dei layer pubblicati;
  4. una lista dei layer viene inviata dal Layer Server e visualizzata sul Layar Client;
  5. un utente seleziona un layer dalla lista;
  6. una richiesta getPOIs è inviata al Layar Server;
  7. il Layar Server trasferisce la richiesta al Layer Service Provider di quel layer;
  8. il Layer Service Provider restituisce il contenuto basandosi sulle specifiche delle Developer API (response getPOIs) al Layar Server;
  9. il Layar Server convalida la response getPOIs e la invia al Layar Client
  10. il Layar Client visualizza i POI. 




Nella documentazione per lo sviluppatore potete trovare tutti i dettagli sull'architettura della piattaforma Layar.

Ora vediamo come creare un semplice Layer Service Providers per esporre i nostri POI direttamente da ArcGIS Server.
Come possiamo vedere dalla documentazione, tutte le chiamate sono RESTful  cosicché aderiscono ad una architettura REST. Correntemente solo una chiamata HTTP GET è stata definita per recuperare le risorse ed è GetPOIs.

In ArcGIS Server possiamo crearci una SOE REST che restituisce un JSON che aderisce a GetPOIs response.
In questo esempio i dati di input (request: layerName, lon, lat) e di output (response:layer, hotspots(id, anchor, text(title, description, footnote)), title, errorCode, errorString) sono solo quelli obbligatori ma nella documentazione potete trovarne molti altri opzionali che possono arricchire le funzionalità dei vostri POI.
Per semplificarci ulteriormente la vita, il raggio di ricerca viene impostato a livello di proprietà SOE ma anche il numero di POI restituiti è impostato a livello di proprietà SOE e restituiamo i più vicini ma le API ci permettono di gestire il raggio, il paging ecc.

//-----------------------------------------------------------------------
// <copyright file="LayarSOE.cs" company="Studio A&T s.r.l.">
//     Copyright (c) Studio A&T s.r.l. All rights reserved.
// </copyright>
// <author>Nicogis</author>
//-----------------------------------------------------------------------
namespace Studioat.ArcGis.Soe.Rest
{
    using System;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using System.Text;
    using ESRI.ArcGIS.Carto;
    using ESRI.ArcGIS.esriSystem;
    using ESRI.ArcGIS.Geodatabase;
    using ESRI.ArcGIS.Geometry;
    using ESRI.ArcGIS.Server;
    using ESRI.ArcGIS.SOESupport;
 
    [SuppressMessage("StyleCop.CSharp.NamingRules""SA1300:ElementMustBeginWithUpperCaseLetter", Justification = "Viewed.")]
    [SuppressMessage("StyleCop.CSharp.NamingRules""SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Viewed.")]
    [SuppressMessage("StyleCop.CSharp.NamingRules""SA1306:FieldNamesMustBeginWithLowerCaseLetter", Justification = "Viewed.")]
    [SuppressMessage("StyleCop.CSharp.DocumentationRules""SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "viewed.")]
 
    /// <summary>
    /// class Layar SOE
    /// </summary>
    [ComVisible(true)]
    [Guid("ec27c9f5-a7c4-4be6-8543-d170762e8604")]
    [ClassInterface(ClassInterfaceType.None)]
    [ServerObjectExtension("MapServer",
        AllCapabilities = "",
        DefaultCapabilities = "",
        Description = "Layar SOE",
        DisplayName = "Layar SOE",
        Properties = "NearestPOIs = 10;Radius = 500",
        HasManagerPropertiesConfigurationPane = false,
        SupportsREST = true,
        SupportsSOAP = false)]
    public class LayarSOE : IServerObjectExtensionIObjectConstructIRESTRequestHandler
    {
        /// <summary>
        /// name of soe
        /// </summary>
        private string soeName;
 
        /// <summary>
        /// properties of soe
        /// </summary>
        private IPropertySet configProps;
 
        /// <summary>
        /// Helper ServerObject
        /// </summary>
        private IServerObjectHelper serverObjectHelper;
 
        /// <summary>
        /// logger of soe
        /// </summary>
        private ServerLogger logger;
 
        /// <summary>
        /// request handler
        /// </summary>
        private IRESTRequestHandler reqHandler;
 
        /// <summary>
        /// number of nearest POIs
        /// </summary>
        private int nearestPOIs;
 
        /// <summary>
        /// radius for POIs
        /// </summary>
        private int radius;
 
        /// <summary>
        /// List of POILayerInfo
        /// </summary>
        private List<POILayerInfo> poiLayerInfos;
 
        /// <summary>
        /// Initializes a new instance of the <see cref="LayarSOE"/> class
        /// </summary>
        public LayarSOE()
        {
            this.soeName = this.GetType().Name;
            this.logger = new ServerLogger();
            this.reqHandler = new SoeRestImpl(this.soeName, this.CreateRestSchema()) as IRESTRequestHandler;
        }
 
        #region IServerObjectExtension Members
 
        /// <summary>
        /// init event of soe
        /// </summary>
        /// <param name="pSOH">Helper ServerObject</param>
        public void Init(IServerObjectHelper pSOH)
        {
            this.serverObjectHelper = pSOH;
        }
 
        /// <summary>
        /// shutdown event of soe
        /// </summary>
        public void Shutdown()
        {
        }
 
        #endregion
 
        #region IObjectConstruct Members
 
        /// <summary>
        /// costruct event of soe
        /// </summary>
        /// <param name="props">properties of soe</param>
        public void Construct(IPropertySet props)
        {
            this.configProps = props;
            this.nearestPOIs = Convert.ToInt32(this.configProps.GetProperty("NearestPOIs"));
            this.radius = Convert.ToInt32(this.configProps.GetProperty("Radius"));
            this.poiLayerInfos = new List<POILayerInfo>();
            this.GetPOILayerInfos();
        }
 
        #endregion
 
        #region IRESTRequestHandler Members
 
        /// <summary>
        /// get schema of soe
        /// </summary>
        /// <returns>schema of soe</returns>
        public string GetSchema()
        {
            return this.reqHandler.GetSchema();
        }
 
        /// <summary>
        /// Handler of request rest
        /// </summary>
        /// <param name="Capabilities">capabilities of soe</param>
        /// <param name="resourceName">name of resource</param>
        /// <param name="operationName">name of operation</param>
        /// <param name="operationInput">input of operation</param>
        /// <param name="outputFormat">format of output</param>
        /// <param name="requestProperties">list of request properties</param>
        /// <param name="responseProperties">list of response properties </param>
        /// <returns>response in byte</returns>
        public byte[] HandleRESTRequest(string Capabilities, string resourceName, string operationName, string operationInput, string outputFormat, string requestProperties, out string responseProperties)
        {
            return this.reqHandler.HandleRESTRequest(Capabilities, resourceName, operationName, operationInput, outputFormat, requestProperties, out responseProperties);
        }
 
        #endregion
 
        /// <summary>
        /// create schema of soe
        /// </summary>
        /// <returns>resource of soe</returns>
        private RestResource CreateRestSchema()
        {
            RestResource rootRes = new RestResource(this.soeName, falsethis.RootResHandler);
 
            RestResource item = new RestResource("POILayers"truenew ResourceHandler(this.POILayer));
            rootRes.resources.Add(item);
 
            RestOperation getPOIsOperation = new RestOperation("getPOIs"new string[] { "lon""lat""layerName" }, new string[] { "json" }, this.getPOIsOperationHandler);
 
            item.operations.Add(getPOIsOperation);
 
            return rootRes;
        }
 
        /// <summary>
        /// handler of resource root
        /// </summary>
        /// <param name="boundVariables">list of variables bound</param>
        /// <param name="outputFormat">format of output</param>
        /// <param name="requestProperties">list of request properties</param>
        /// <param name="responseProperties">list of response properties </param>
        /// <returns>root resource in format output in byte</returns>
        private byte[] RootResHandler(NameValueCollection boundVariables, string outputFormat, string requestProperties, out string responseProperties)
        {
            responseProperties = "{\"Content-Type\" : \"application/json\"}";
 
            JsonObject result = new JsonObject();
            AddInPackageAttribute addInPackage = (AddInPackageAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AddInPackageAttribute), false)[0];
            result.AddString("agsVersion", addInPackage.TargetVersion);
            result.AddString("soeVersion", addInPackage.Version);
            result.AddString("author", addInPackage.Author);
            result.AddString("company", addInPackage.Company);
 
            return result.JsonByte();
        }
 
        /// <summary>
        /// Handler operation Get POIs
        /// </summary>
        /// <param name="boundVariables">list of variables bound</param>
        /// <param name="operationInput">input of operation</param>
        /// <param name="outputFormat">format of output</param>
        /// <param name="requestProperties">list of request properties</param>
        /// <param name="responseProperties">list of response properties </param>
        /// <returns>response in byte</returns>
        private byte[] getPOIsOperationHandler(NameValueCollection boundVariables, JsonObject operationInput, string outputFormat, string requestProperties, out string responseProperties)
        {
            responseProperties = "{\"Content-Type\" : \"application/json\"}";
            string layerName = null;
            try
            {
                bool found = operationInput.TryGetString("layerName"out layerName);
                if (!found)
                {
                    throw new ArgumentNullException("layerName");
                }
 
                int poiLayerID = Convert.ToInt32(boundVariables["POILayersID"], CultureInfo.InvariantCulture);
 
                POILayerInfo poiLayerInfo = this.GetPOILayerInfo(poiLayerID);
 
                double? lon;
                found = operationInput.TryGetAsDouble("lon"out lon);
                if ((!found) || (!lon.HasValue))
                {
                    throw new ArgumentNullException("lon");
                }
 
                double? lat;
                found = operationInput.TryGetAsDouble("lat"out lat);
                if ((!found) || (!lat.HasValue))
                {
                    throw new ArgumentNullException("lat");
                }
 
                IFeatureClass featureClass = this.GetPOIFeatureClass(poiLayerInfo.Id);
 
                IPoint point = new PointClass();
                point.X = lon.Value;
                point.Y = lat.Value;
                point.SpatialReference = Helper.GCS1984;
 
                point.Project(poiLayerInfo.Extent.SpatialReference);
 
                ITopologicalOperator topologicalOperator = point as ITopologicalOperator;
                ISpatialFilter spatialFilter = new SpatialFilterClass();
                spatialFilter.Geometry = topologicalOperator.Buffer(this.radius);
                spatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;
                spatialFilter.GeometryField = featureClass.ShapeFieldName;
                IFeatureCursor featureCursor = null;
 
                IDictionary<intdouble> dictionaryDistances = new Dictionary<intdouble>();
                try
                {
                    featureCursor = featureClass.Search(spatialFilter, true);
 
                    IFeature feature = featureCursor.NextFeature();
                    while (feature != null)
                    {
                        IGeometry geometry = feature.ShapeCopy as IGeometry;
                        if (geometry == null || geometry.IsEmpty)
                        {
                            continue;
                        }
 
                        IProximityOperator proximityOperator = feature.ShapeCopy as IProximityOperator;
                        double distance = proximityOperator.ReturnDistance(point);
 
                        dictionaryDistances.Add(feature.OID, distance);
                        feature = featureCursor.NextFeature();
                    }
                }
                catch
                {
                    throw;
                }
                finally
                {
                    Marshal.FinalReleaseComObject(featureCursor);
                }
 
                JsonObject result = new JsonObject();
                result.AddString("layer", layerName);
               
                if (dictionaryDistances.Count == 0)
                {
                    result.AddLong("errorCode", 21);
                    result.AddString("errorString""POIs not found!");
                    return Encoding.UTF8.GetBytes(result.ToJson());
                }
 
                var listPOIs = from item in dictionaryDistances orderby item.Value ascending select item.Key;
                if (dictionaryDistances.Count > this.nearestPOIs)
                {
                    listPOIs = listPOIs.Take(this.nearestPOIs);
                }
 
                int[] listPOIOIDs = listPOIs.ToArray();
 
                List<JsonObject> hotspots = new List<JsonObject>();
                try
                {
                    featureCursor = featureClass.GetFeatures(listPOIOIDs, true);
                    int idxField = featureClass.FindField("MyField");
                    IFeature feature = featureCursor.NextFeature();
                    while (feature != null)
                    {
                        string valueField = Convert.ToString(feature.get_Value(idxField));
                        JsonObject hotspot = new JsonObject();
                        hotspot.AddString("id", valueField);
                        IPoint poi = feature.ShapeCopy as IPoint;
                        poi.Project(Helper.GCS1984);
                        hotspot.AddString("anchor"string.Format("geo:{0},{1}", poi.Y.ToString(Helper.CultureUS), poi.X.ToString(Helper.CultureUS)));
                        JsonObject text = new JsonObject();
                        text.AddString("title"string.Format("Id:{0}", valueField));
                        text.AddString("description"string.Empty);
                        text.AddString("footnote""Power by MyLayer");
                        hotspot.AddJsonObject("text", text);
                        hotspot.AddString("title", valueField);
                        ////hotspot.AddString("imageURL", ""); <100Kb 100x75
                        hotspots.Add(hotspot);
                        feature = featureCursor.NextFeature();
                    }
                }
                catch
                {
                    throw;
                }
                finally 
                {
                    Marshal.FinalReleaseComObject(featureCursor);
                }
                
                result.AddArray("hotspots", hotspots.ToArray());
                result.AddLong("errorCode", 0);
                result.AddString("errorString"string.Empty);
 
                return Encoding.UTF8.GetBytes(result.ToJson());
            }
            catch
            {
                JsonObject result = new JsonObject();
                result.AddString("layer", layerName);
                result.AddLong("errorCode", 20);
                result.AddString("errorString""Error return data");
                return Encoding.UTF8.GetBytes(result.ToJson());
            }
        }
 
        /// <summary>
        /// resource POILayer
        /// </summary>
        /// <param name="boundVariables">list of variables bound</param>
        /// <param name="outputFormat">format of output</param>
        /// <param name="requestProperties">list of request properties</param>
        /// <param name="responseProperties">list of response properties </param>
        /// <returns>resource in byte</returns>
        private byte[] POILayer(NameValueCollection boundVariables, string outputFormat, string requestProperties, out string responseProperties)
        {
            responseProperties = "{\"Content-Type\" : \"application/json\"}";
            if (boundVariables["POILayersID"] == null)
            {
                JsonObject[] objectArray = System.Array.ConvertAll(this.poiLayerInfos.ToArray(), i => i.ToJsonObject());
                JsonObject jsonObject = new JsonObject();
                jsonObject.AddArray("poiLayers", objectArray);
                return jsonObject.JsonByte();
            }
            else
            {
                int layerID = Convert.ToInt32(boundVariables["POILayersID"], CultureInfo.InvariantCulture);
                return this.GetPOILayerInfo(layerID).ToJsonObject().JsonByte();
            }
        }
 
        /// <summary>
        /// get POILayerInfo from Id
        /// </summary>
        /// <param name="poiLayerID">value of poiLayerID</param>
        /// <returns>object POILayerInfo</returns>
        private POILayerInfo GetPOILayerInfo(int poiLayerID)
        {
            if (poiLayerID < 0)
            {
                throw new ArgumentOutOfRangeException("poiLayerID");
            }
 
            IMapServer3 serverObject = this.GetMapServer();
            IMapLayerInfos mapLayerInfos = serverObject.GetServerInfo(serverObject.DefaultMapName).MapLayerInfos;
            long count = mapLayerInfos.Count;
            for (int i = 0; i < count; i++)
            {
                IMapLayerInfo mapLayerInfo = mapLayerInfos.get_Element(i);
                if (mapLayerInfo.ID == poiLayerID)
                {
                    return new POILayerInfo(mapLayerInfo);
                }
            }
 
            throw new ArgumentOutOfRangeException("poiLayerID");
        }
 
        /// <summary>
        /// Feature Class from id of layer
        /// </summary>
        /// <param name="poiLayerID">id poi layer</param>
        /// <returns>feature class</returns>
        private IFeatureClass GetPOIFeatureClass(int poiLayerID)
        {
            IMapServer3 mapServer = this.GetMapServer();
            IMapServerDataAccess dataAccess = (IMapServerDataAccess)mapServer;
            return (IFeatureClass)dataAccess.GetDataSource(mapServer.DefaultMapName, poiLayerID);
        }
 
        /// <summary>
        /// Get object MapServer of ServerObject 
        /// </summary>
        /// <returns>object MapServer</returns>
        private IMapServer3 GetMapServer()
        {
            IMapServer3 mapServer = this.serverObjectHelper.ServerObject as IMapServer3;
            if (mapServer == null)
            {
                throw new LayarSOEException("Unable to access the map server.");
            }
 
            return mapServer;
        }
 
        /// <summary>
        /// From service fills list of layer point
        /// </summary>
        private void GetPOILayerInfos()
        {
            IMapServer3 serverObject = this.GetMapServer();
            IMapLayerInfos mapLayerInfos = serverObject.GetServerInfo(serverObject.DefaultMapName).MapLayerInfos;
 
            this.poiLayerInfos = new List<POILayerInfo>();
            for (int i = 0; i < mapLayerInfos.Count; i++)
            {
                IMapLayerInfo mapLayerInfo = mapLayerInfos.get_Element(i);
                if (mapLayerInfo.IsFeatureLayer)
                {
                    IFields fields = mapLayerInfo.Fields;
                    for (int j = 0; j < fields.FieldCount; j++)
                    {
                        IField field = fields.get_Field(j);
                        if (field.Type == esriFieldType.esriFieldTypeGeometry)
                        {
                            IGeometryDef geometryDef = field.GeometryDef;
                            if (geometryDef.GeometryType == ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPoint)
                            {
                                this.poiLayerInfos.Add(new POILayerInfo(mapLayerInfo));
                            }
 
                            break;
                        }
                    }
                }
            }
        }
    }
}


Possiamo proteggere il nostro servizio ArcGIS Server esponendo un proxy per ricevere e trasferire le chiamate dal Layar Platform. Il proxy genera un token ArcGIS Server dinamicamente per poter utilizzare il servizio al quale abbiamo abilitato la SOE o possiamo crearne uno statico che memorizziamo nel file di configurazione. Inoltre nel proxy possiamo gestire, ad esempio, le richieste firmate così da verificare se provengono da Layar e quindi farle passare o meno. La firma è conforme alla firma OAuth. Quando si pubblica il layer su Layar Publishing Website, se si desidera avere delle richieste firmate, si possono impostare i parametri per l'autenticazione OAuth (OAuth signing required,  OAuth consumer key  e OAuth consumer secret).
Il solo metodo di firma supportato è HMAC-SHA1 e la Signature Base String è generata utilizzando:
  1. metodo http: GET;
  2. l'url della richiesta: nel nostro caso l'url del nostro proxy;
  3. i parametri di richiesta normalizzati inclusi quelli di oauth ad esclusione di oauth_signature. I parametri di richiesta oauth usati da Layar Developer API sono: oauth_consumer_key, oauth_signature_method, oauth_timestamp, oauth_nonce, oauth_body_hash e oauth_version.

L'OAuth utilizzata è il 2-Legged che in sostanza è una OAuth senza utente. In pratica è un modo nel quale il consumer (Layar Platform) può fare una richiesta firmata ad un provider (nostro proxy) facendo leva sull'algoritmo di firma OAuth. Questo significa che il provider ha un livello di fiducia ulteriore con il consumer ed inoltre fornisce i dati al consumer senza la necessità di ottenere l'autorizzazione (token) dall'utente.


Ed ecco il nostro proxy. Per la verifica della firma della richiesta potete scaricare le due dll (OAuth.Net.Common e OAuth.Net.Components) da http://code.google.com/p/oauth-dot-net/

<%@ WebHandler Language="C#" Class="proxy" %>
/*
  This proxy page does not have any security checks. It is highly recommended
  that a user deploying this proxy page on their web server, add appropriate
  security checks, for example checking request path, username/password, target
  url, etc.
*/
using System;
using System.Drawing;
using System.IO;
using System.Web;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.Web.Caching;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
using OAuth.Net.Common;
using OAuth.Net.Components;
 
/// <summary>
/// Forwards requests to an ArcGIS Server REST resource.
/// </summary>
public class proxy : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        System.Collections.Specialized.NameValueCollection queryString = context.Request.QueryString;
        string layerName = queryString["layerName"];
        Dictionary<stringstring> dataConfig = this.getDataFromConfigFile(layerName);
        
        if (Convert.ToBoolean(dataConfig["oauth"]))
        {
            ISigningProvider SigningProvider = new HmacSha1SigningProvider();
            var param = new OAuthParameters()
            {
                ConsumerKey = queryString["oauth_consumer_key"],
                SignatureMethod = queryString["oauth_signature_method"],
                Version = queryString["oauth_version"],
                Nonce = queryString["oauth_nonce"],
                Timestamp = queryString["oauth_timestamp"]
            };
 
            param.AdditionalParameters.Add("oauth_body_hash"HttpUtility.HtmlDecode(queryString["oauth_body_hash"]));
 
            foreach (string s in queryString.Keys)
            {
                if (!s.StartsWith("oauth_"))
                {
                    param.AdditionalParameters.Add(s, queryString[s]);
                }
            }
 
            var signatureBase = SignatureBase.Create(WebRequestMethods.Http.Get, new Uri(context.Request.Url.GetLeftPart(UriPartial.Path)), param);
 
            if (!SigningProvider.CheckSignature(signatureBase, HttpUtility.HtmlDecode(queryString["oauth_signature"]), dataConfig["oauthConsumerSecret"], null))
            {
                throw new Exception("Unauthorized");
            }
             
        }
        
        HttpResponse response = context.Response;
 
        System.Collections.Specialized.NameValueCollection nvc = new System.Collections.Specialized.NameValueCollection();
        foreach (string s in queryString.Keys)
        {
            if (!s.StartsWith("oauth_"))
            {
                nvc.Add(s, queryString[s]);
            }
        }
        
        // Get token for ags service, and url operation soe and append to the request
        if (!string.IsNullOrEmpty(dataConfig["token"]))
        {
            nvc.Add("token", dataConfig["token"]);
        }
 
        nvc.Add("f""json");
 
        System.Net.WebRequest req = System.Net.WebRequest.Create(new Uri(dataConfig["urlOperation"] + ToQueryString(nvc)));
        req.Method = context.Request.HttpMethod;
 
        // Set body of request for POST requests
        if (context.Request.InputStream.Length > 0)
        {
            byte[] bytes = new byte[context.Request.InputStream.Length];
            context.Request.InputStream.Read(bytes, 0, (int)context.Request.InputStream.Length);
            req.ContentLength = bytes.Length;
            req.ContentType = "application/x-www-form-urlencoded";
            using (Stream outputStream = req.GetRequestStream())
            {
                outputStream.Write(bytes, 0, bytes.Length);
            }
        }
 
        // Send the request to the server
        System.Net.WebResponse serverResponse = null;
        try
        {
            serverResponse = req.GetResponse();
        }
        catch (System.Net.WebException webExc)
        {
            response.StatusCode = 500;
            response.StatusDescription = webExc.Status.ToString();
            response.Write(webExc.Response);
            response.End();
            return;
        }
 
        // Set up the response to the client
        if (serverResponse != null)
        {
            response.ContentType = serverResponse.ContentType;
            using (Stream byteStream = serverResponse.GetResponseStream())
            {
 
                // Text response
                if (serverResponse.ContentType.Contains("text"))
                {
                    using (StreamReader sr = new StreamReader(byteStream))
                    {
                        string strResponse = sr.ReadToEnd();
                        response.Write(strResponse);
                    }
                }
                else
                {
                    // Binary response (image, lyr file, other binary file)
                    BinaryReader br = new BinaryReader(byteStream);
                    byte[] outb = br.ReadBytes((int)serverResponse.ContentLength);
                    br.Close();
 
                    // Tell client not to cache the image since it's dynamic
                    response.CacheControl = "no-cache";
 
                    // Send the image to the client
                    // (Note: if large images/files sent, could modify this to send in chunks)
                    response.OutputStream.Write(outb, 0, outb.Length);
                }
 
                serverResponse.Close();
            }
        }
        response.End();
    }
 
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
 
    private string ToQueryString(System.Collections.Specialized.NameValueCollection nvc)
    {
        return "?" + string.Join("&"Array.ConvertAll(nvc.AllKeys, key => string.Format("{0}={1}", key, nvc[key])));
    }
 
    // Gets the token and urlOperation for a server URL from a configuration file
    private Dictionary<string,string> getDataFromConfigFile(string layerName)
    {
        try
        {
            LayarConfig config = LayarConfig.GetCurrentConfig();
            if (config != null)
            {
                Dictionary<string,string> dct = new Dictionary<string,string>();
                dct.Add("token",config.GetToken(layerName));
                dct.Add("urlOperation",config.GetUrlOperation(layerName));
                dct.Add("oauth", config.GetOAuth(layerName).ToString());
                dct.Add("oauthConsumerSecret", config.GetOAuthConsumerSecret(layerName));
                return dct;
            }
            else
            {
                throw new ApplicationException("Layar.config file does not exist at application root, or is not readable.");
            }
        }
        catch (InvalidOperationException)
        {
            HttpResponse response = HttpContext.Current.Response;
            response.StatusCode = (int)System.Net.HttpStatusCode.Forbidden;
            response.End();
        }
        catch (Exception e)
        {
            if (e is ApplicationException)
            {
                throw e;
            }
        }
 
        throw new Exception();
    }
}
 
[XmlRoot("LayarConfig")]
public class LayarConfig
{
    #region Static Members
 
    private static object _lockobject = new object();
 
    public static LayarConfig LoadLayarConfig(string fileName)
    {
        LayarConfig config = null;
 
        lock (_lockobject)
        {
            if (System.IO.File.Exists(fileName))
            {
                XmlSerializer reader = new XmlSerializer(typeof(LayarConfig));
                using (System.IO.StreamReader file = new System.IO.StreamReader(fileName))
                {
                    config = (LayarConfig)reader.Deserialize(file);
                }
            }
        }
 
        return config;
    }
 
    public static LayarConfig GetCurrentConfig()
    {
        LayarConfig config = HttpRuntime.Cache["layarConfig"as LayarConfig;
        if (config == null)
        {
            string fileName = GetFilename(HttpContext.Current);
            config = LoadLayarConfig(fileName);
 
            if (config != null)
            {
                CacheDependency dep = new CacheDependency(fileName);
                HttpRuntime.Cache.Insert("layarConfig", config, dep);
            }
        }
 
        return config;
    }
 
    public static string GetFilename(HttpContext context)
    {
        return context.Server.MapPath("~/layar.config");
    }
    #endregion
 
    ServerUrl[] serverUrls;
 
    [XmlArray("serverUrls")]
    [XmlArrayItem("serverUrl")]
    public ServerUrl[] ServerUrls
    {
        get { return this.serverUrls; }
        set { this.serverUrls = value; }
    }
 
    public string GetUrlOperation(string layerName)
    {
        foreach (ServerUrl su in serverUrls)
        {
            if (string.Compare(layerName, su.Layername, true) == 1)
            {
                return su.UrlOperation;
            }
        }
 
        return string.Empty;
    }
    
    public string GetOAuthConsumerSecret(string layerName)
    {
        foreach (ServerUrl su in serverUrls)
        {
            if (string.Compare(layerName, su.Layername, true) == 1)
            {
                return su.OAuthConsumerSecret;
            }
        }
 
        return string.Empty;
    }
 
    public bool GetOAuth(string layerName)
    {
        foreach (ServerUrl su in serverUrls)
        {
            if (string.Compare(layerName, su.Layername, true) == 1)
            {
                return su.OAuth;
            }
        }
 
        return true;
    }
    
    
    
    public string GetToken(string layerName)
    {
        foreach (ServerUrl su in serverUrls)
        {
            if (string.Compare(layerName, su.Layername, true) == 1 && su.DynamicToken)
            {
                // Code to dynamically get the token
                string tokenService = string.Format("https://{0}/{1}/tokens?request=getToken&username={2}&password={3}&expiration=30", su.Host, su.Instance, su.UserName, su.Password);
                string token;
 
                // This script is added to force the application to certify the SSL script (if for example you have a self certificate on server)
                System.Net.ServicePointManager.ServerCertificateValidationCallback += delegate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
                {
                    return true;
                };
 
                System.Net.WebRequest tokenRequest = System.Net.WebRequest.Create(tokenService);
                System.Net.WebResponse tokenResponse = tokenRequest.GetResponse();
                System.IO.Stream responseStream = tokenResponse.GetResponseStream();
                System.IO.StreamReader readStream = new System.IO.StreamReader(responseStream);
                token = readStream.ReadToEnd();
 
                return token;
            }
            else if (string.Compare(layerName, su.Layername, true) == 1)
            {
                return su.Token;
            }
        }
 
        return string.Empty;
    }
}
 
public class ServerUrl
{
    string urlOperation;
    string token;
    bool dynamicToken;
    string userName;
    string host;
    string password;
    string instance;
    string layername;
    string oauthConsumerSecret;
    bool oauth;
 
    [XmlAttribute("layername")]
    public string Layername
    {
        get { return layername; }
        set { layername = value; }
    }
 
    [XmlAttribute("urlOperation")]
    public string UrlOperation
    {
        get { return urlOperation; }
        set { urlOperation = value; }
    }
 
    [XmlAttribute("token")]
    public string Token
    {
        get { return token; }
        set { token = value; }
    }
 
    [XmlAttribute("dynamicToken")]
    public bool DynamicToken
    {
        get { return dynamicToken; }
        set { dynamicToken = value; }
    }
 
    [XmlAttribute("host")]
    public string Host
    {
        get { return host; }
        set { host = value; }
    }
 
    [XmlAttribute("instance")]
    public string Instance
    {
        get { return instance; }
        set { instance = value; }
    }
 
    [XmlAttribute("userName")]
    public string UserName
    {
        get { return userName; }
        set { userName = value; }
    }
 
    [XmlAttribute("password")]
    public string Password
    {
        get { return password; }
        set { password = value; }
    }
 
    [XmlAttribute("oauthConsumerSecret")]
    public string OAuthConsumerSecret
    {
        get { return oauthConsumerSecret; }
        set { oauthConsumerSecret = value; }
    }
    [XmlAttribute("oauth")]
    public bool OAuth
    {
        get { return oauth; }
        set { oauth = value; }
    }
}
 
 
Ora non ci resta che registrarci sul sito di Layar e pubblicare il nostro url endpoint:


In Additional settings avete la possibilità gestire il flusso di autenticazione degli utenti al proprio layer.

Prima della pubbicazione abbiamo la possibilità di testarlo sia via web che su mobile loggandoci con il nostro utente nell'app.