图层访问
来源
图层访问
概述
// Copyright 2015 ESRI
//
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
//
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
//
// See the use restrictions at /DeveloperKit10.3/userestrictions.txt.
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Server;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.SOESupport;
using System.IO;
using System.Text.RegularExpressions;
using System.Web.Script.Serialization;
using NetLayerAccessSOI.SOISupport;
//TODO: sign the project (project properties > signing tab > sign the assembly)
// this is strongly suggested if the dll will be registered using regasm.exe .dll /codebase
namespace NetLayerAccessSOI
{
[ComVisible(true)]
[Guid("0b8694f2-1730-453d-b645-7022dfc24bef")]
[ClassInterface(ClassInterfaceType.None)]
[ServerObjectInterceptor("MapServer",
Description = "SOI to control access to different layers of a service.",
DisplayName = "DotNet Layer Access SOI Example",
Properties = "")]
public class NetLayerAccessSOI : IServerObjectExtension, IRESTRequestHandler, IWebRequestHandler, IRequestHandler2, IRequestHandler
{
private string _soiName;
private IServerObjectHelper _soHelper;
private ServerLogger _serverLog;
private IServerObject _serverObject;
RestServiceSOI _restServiceSOI;
SoapServiceSOI _soapServiceSOI;
private Dictionary _operationMap;
/*
* Map used to store permission information. Permission rules for each
* service is read form the permisson.json file.
*/
private Dictionary _servicePermissionMap = null;
/*
* Permission are read from this external file. Advantage of an external file is that
* same SOI can be used for multiple services and permission for all of these services
* is read from the permission.json file.
*
*/
private String _permissionFilePath = "C:\\arcgisserver\\permission.json"; //default path
private String _wsdlFilePath = "C:\\Program Files\\ArcGIS\\Server\\XmlSchema\\MapServer.wsdl"; //default path
public NetLayerAccessSOI ()
{
_soiName = this.GetType().Name;
}
private void InitFiltering ()
{
_operationMap = new Dictionary
{
{ RESTHandlerOpCode.root, new RestFilterOperation
{
PreFilter = null,
PostFilter = PostFilterRESTRoot
} },
{ RESTHandlerOpCode.rootExport, new RestFilterOperation
{
PreFilter = PreFilterExport,
PostFilter = null
} },
{ RESTHandlerOpCode.rootFind, new RestFilterOperation
{
PreFilter = PreFilterFindAndKml,
PostFilter = null
} },
{ RESTHandlerOpCode.rootGenerateKml, new RestFilterOperation
{
PreFilter = PreFilterFindAndKml,
PostFilter = null
} },
{ RESTHandlerOpCode.rootIdentify, new RestFilterOperation
{
PreFilter = PreFilterIdentify,
PostFilter = null
} },
{ RESTHandlerOpCode.rootLayers, new RestFilterOperation
{
PreFilter = null,
PostFilter = PostFilterRootLayers
} },
{ RESTHandlerOpCode.rootLegend, new RestFilterOperation
{
PreFilter = null,
PostFilter = PostFilterRootLegend
} },
{ RESTHandlerOpCode.layerGenerateRenderer, new RestFilterOperation
{
PreFilter = PreFilterLayerQuery,
PostFilter = null
} },
{ RESTHandlerOpCode.layerQuery, new RestFilterOperation
{
PreFilter = PreFilterLayerQuery,
PostFilter = null
} },
{ RESTHandlerOpCode.layerQueryRelatedRecords, new RestFilterOperation
{
PreFilter = PreFilterLayerQuery,
PostFilter = null
} }
};
}
public void Init ( IServerObjectHelper pSOH )
{
try
{
_soHelper = pSOH;
_serverLog = new ServerLogger();
_serverObject = pSOH.ServerObject;
_restServiceSOI = new RestServiceSOI(_soHelper);
_soapServiceSOI = new SoapServiceSOI(_soHelper, _wsdlFilePath);
if (File.Exists(_permissionFilePath))
{
//TODO REMOVE
_serverLog.LogMessage(ServerLogger.msgType.infoStandard, _soiName + ".init()", 200, "Reading permissions from " + _permissionFilePath);
_servicePermissionMap = ReadPermissionFile(_permissionFilePath);
//TODO REMOVE
_serverLog.LogMessage(ServerLogger.msgType.infoStandard, _soiName + ".init()", 200, "Total permission entries: " + _servicePermissionMap.Count());
}
else
{
_serverLog.LogMessage(ServerLogger.msgType.error, _soiName + ".init()", 500, "Permission file does not exist at " + _permissionFilePath);
}
InitFiltering();
_serverLog.LogMessage(ServerLogger.msgType.infoStandard, _soiName + ".init()", 200, "Initialized " + _soiName + " SOI.");
}
catch (Exception e)
{
_serverLog.LogMessage(ServerLogger.msgType.error, _soiName + ".init()", 500, "Exception: " + e.Message + " in " + e.StackTrace);
}
}
public void Shutdown ()
{
_serverLog.LogMessage(ServerLogger.msgType.infoStandard, _soiName + ".init()", 200, "Shutting down " + _soiName + " SOE.");
}
#region REST interceptors
public string GetSchema ()
{
try
{
IRESTRequestHandler restRequestHandler = _restServiceSOI.FindRequestHandlerDelegate();
if (restRequestHandler == null)
return null;
return restRequestHandler.GetSchema();
}
catch (Exception e)
{
_serverLog.LogMessage(ServerLogger.msgType.error, _soiName + ".GetSchema()", 500, "Exception: " + e.Message + " in " + e.StackTrace);
return null;
}
}
private byte[] FilterRESTRequest (
RestFilterOperation restFilterOp,
RESTRequestParameters restInput,
HashSet authorizedLayers,
out string responseProperties )
{
try
{
responseProperties = null;
IRESTRequestHandler restRequestHandler = _restServiceSOI.FindRequestHandlerDelegate();
if (restRequestHandler == null)
throw new RESTErrorException("Service handler not found");
if (null != restFilterOp && null != restFilterOp.PreFilter)
restInput = restFilterOp.PreFilter(restInput, authorizedLayers);
byte[] response =
restRequestHandler.HandleRESTRequest(restInput.Capabilities, restInput.ResourceName, restInput.OperationName, restInput.OperationInput,
restInput.OutputFormat, restInput.RequestProperties, out responseProperties);
if (null == restFilterOp || null == restFilterOp.PostFilter)
return response;
String responseStr = System.Text.Encoding.UTF8.GetString(response);
responseStr = restFilterOp.PostFilter(restInput, responseStr, authorizedLayers);
// TODO: remove ------------------------------------
_serverLog.LogMessage(ServerLogger.msgType.infoDetailed, "HandleRESTRequest.FilterRESTRequest", 0, "Filtered REST resource response :: " + responseStr);
// TODO: remove ------------------------------------
return System.Text.Encoding.UTF8.GetBytes(responseStr);
}
catch (RESTErrorException restException)
{
// pre- or post- filters can throw restException with the error JSON output in the Message property.
// we catch them here and return JSON response.
responseProperties = "{\"Content-Type\":\"text/plain;charset=utf-8\"}";
//catch and return a JSON error from the pre- or postFilter.
return System.Text.Encoding.UTF8.GetBytes(restException.Message);
}
}
private static RESTHandlerOpCode GetHandlerOpCode ( RESTRequestParameters restInput )
{
var resName = restInput.ResourceName.TrimStart('/'); //remove leading '/' to prevent empty string at index 0
var resourceTokens = (resName ?? "").ToLower().Split('/');
string opName = (restInput.OperationName ?? "").ToLower();
switch (resourceTokens[0])
{
case "":
switch (opName)
{
case "":
return RESTHandlerOpCode.root;
case "export":
return RESTHandlerOpCode.rootExport;
case "find":
return RESTHandlerOpCode.rootFind;
case "identify":
return RESTHandlerOpCode.rootIdentify;
case "generatekml":
return RESTHandlerOpCode.rootGenerateKml;
default:
return RESTHandlerOpCode.defaultNoOp;
}
case "layers":
{
var tokenCount = resourceTokens.GetLength(0);
if (1 == tokenCount)
return RESTHandlerOpCode.rootLayers;
if (2 == tokenCount)
switch (opName)
{
case "":
return RESTHandlerOpCode.layerRoot;
case "query":
return RESTHandlerOpCode.layerQuery;
case "queryRelatedRecords":
return RESTHandlerOpCode.layerQueryRelatedRecords;
default:
return RESTHandlerOpCode.defaultNoOp;
}
}
break;
case "legend":
return RESTHandlerOpCode.rootLegend;
default:
return RESTHandlerOpCode.defaultNoOp;
}
return RESTHandlerOpCode.defaultNoOp;
}
public byte[] HandleRESTRequest ( string capabilities, string resourceName, string operationName,
string operationInput, string outputFormat, string requestProperties, out string responseProperties )
{
try
{
responseProperties = null;
_serverLog.LogMessage(ServerLogger.msgType.infoStandard, _soiName + ".HandleRESTRequest()",
200, "Request received in Layer Access SOI for handleRESTRequest");
var authorizedLayerSet = GetAuthorizedLayers(_restServiceSOI);
var restInput = new RESTRequestParameters
{
Capabilities = capabilities,
ResourceName = resourceName,
OperationName = operationName,
OperationInput = operationInput,
OutputFormat = outputFormat,
RequestProperties = requestProperties
};
var opCode = GetHandlerOpCode(restInput);
RestFilterOperation filterOp = _operationMap.ContainsKey(opCode) ? _operationMap[opCode] : null;
return FilterRESTRequest(filterOp, restInput, authorizedLayerSet, out responseProperties);
}
catch (Exception e)
{
_serverLog.LogMessage(ServerLogger.msgType.error, _soiName + ".HandleRESTRequest()", 500, "Exception: " + e.Message + " in " + e.StackTrace);
throw;
}
}
#endregion
#region REST Pre-filters
private static RESTRequestParameters PreFilterExport ( RESTRequestParameters restInput, HashSet authorizedLayers )
{
JavaScriptSerializer sr = new JavaScriptSerializer { MaxJsonLength = int.MaxValue };
var operationInputJson = sr.DeserializeObject(restInput.OperationInput) as IDictionary;
operationInputJson["layers"] = "show:" + String.Join(",", authorizedLayers);
restInput.OperationInput = sr.Serialize(operationInputJson);
return restInput;
}
private static RESTRequestParameters PreFilterFindAndKml ( RESTRequestParameters restInput, HashSet authorizedLayers )
{
JavaScriptSerializer sr = new JavaScriptSerializer { MaxJsonLength = int.MaxValue };
var operationInputJson = sr.DeserializeObject(restInput.OperationInput) as IDictionary;
var removeSpacesRegEx = new Regex("\\s+");
String requestedLayers = operationInputJson.ContainsKey("layers") ? operationInputJson["layers"].ToString() : "";
requestedLayers = removeSpacesRegEx.Replace(requestedLayers, "");
operationInputJson["layers"] = RemoveUnauthorizedLayersFromRequestedLayers(requestedLayers, authorizedLayers);
restInput.OperationInput = sr.Serialize(operationInputJson);
return restInput;
}
private static RESTRequestParameters PreFilterIdentify ( RESTRequestParameters restInput, HashSet authorizedLayers )
{
JavaScriptSerializer sr = new JavaScriptSerializer { MaxJsonLength = int.MaxValue };
var operationInputJson = sr.DeserializeObject(restInput.OperationInput) as IDictionary;
String requestedLayers = operationInputJson.ContainsKey("layers") ? operationInputJson["layers"].ToString() : "";
if (string.IsNullOrEmpty(requestedLayers) || requestedLayers.StartsWith("top") || requestedLayers.StartsWith("all"))
{
operationInputJson["layers"] = "visible:" + authorizedLayers;
}
else if (requestedLayers.StartsWith("visible"))
{
var reqLayerParts = requestedLayers.Split(':');
var removeSpacesRegEx = new Regex("\\s+");
operationInputJson["layers"] = "visible:" +
RemoveUnauthorizedLayersFromRequestedLayers(
removeSpacesRegEx.Replace(reqLayerParts[1], ""), authorizedLayers);
}
else
{
operationInputJson["layers"] = "visible:" + authorizedLayers;
}
restInput.OperationInput = sr.Serialize(operationInputJson);
return restInput;
}
private static RESTRequestParameters PreFilterLayerQuery ( RESTRequestParameters restInput, HashSet authorizedLayers )
{
JavaScriptSerializer sr = new JavaScriptSerializer { MaxJsonLength = int.MaxValue };
var operationInputJson = sr.DeserializeObject(restInput.OperationInput) as IDictionary;
var resName = restInput.ResourceName.TrimStart('/');
var rnameParts = resName.Split('/');
var requestedLayerId = rnameParts[1];
if (!authorizedLayers.Contains(requestedLayerId))
{
var errorReturn = new Dictionary
{
{"error", new Dictionary
{
{"code", 404},
{"message", "Access Denied"}
}
}
};
throw new RESTErrorException(sr.Serialize(errorReturn));
}
restInput.OperationInput = sr.Serialize(operationInputJson);
return restInput;
}
#endregion
#region REST Post-filters
///
///
/// Filter REST root service info response.
/// IMPORTANT: the JSON structure returned by the REST handler root resource
/// differs from what you usually see as the response from the service REST endpoint.
///
///
///
///
///
/// Filtered json
private static String PostFilterRESTRoot ( RESTRequestParameters restInput, String originalResponse, HashSet authorizedLayerSet )
{
if (null == authorizedLayerSet)
return null;
//restInput is not used here, but may be used later as needed
try
{
// Perform JSON filtering
// Note: The JSON syntax can change and you might have to adjust this piece of code accordingly
if (authorizedLayerSet == null)
return null;
/*
* Remove unauthorized layer information from 1. Under 'contents' tag 2. Under 'resources' tag
* 2.1 'layers' 2.2 'tables' 2.3 'legend'
*/
JavaScriptSerializer sr = new JavaScriptSerializer { MaxJsonLength = int.MaxValue };
var jsonResObj = sr.DeserializeObject(originalResponse) as IDictionary;
// Filter for 'contents' tag
var contentsJO = jsonResObj["contents"] as IDictionary;
var layersJA = contentsJO["layers"] as object[];
var updatedLayers = new List