262 lines
11 KiB
C#
262 lines
11 KiB
C#
|
//------------------------------------------------------------------------------
|
|||
|
// <copyright file="WCFServiceClientProxyGenerator.cs" company="Microsoft">
|
|||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|||
|
// </copyright>
|
|||
|
//------------------------------------------------------------------------------
|
|||
|
|
|||
|
namespace System.ServiceModel.Description
|
|||
|
{
|
|||
|
using System;
|
|||
|
using System.Globalization;
|
|||
|
using System.ServiceModel.Channels;
|
|||
|
using System.ServiceModel.Description;
|
|||
|
using System.ServiceModel.Web;
|
|||
|
using System.Text;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Xml;
|
|||
|
using System.Collections;
|
|||
|
using System.Web.Script.Services;
|
|||
|
|
|||
|
internal class WCFServiceClientProxyGenerator : ClientProxyGenerator
|
|||
|
{
|
|||
|
const int MaxIdentifierLength = 511;
|
|||
|
const string DataContractXsdBaseNamespace = @"http://schemas.datacontract.org/2004/07/";
|
|||
|
const string DefaultCallbackParameterName = "callback";
|
|||
|
private string path;
|
|||
|
private ServiceEndpoint serviceEndpoint;
|
|||
|
|
|||
|
// Similar to proxy generation code in WCF System.Runtime.Serialization.CodeExporter
|
|||
|
// to generate CLR namespace from DataContract namespace
|
|||
|
private static void AddToNamespace(StringBuilder builder, string fragment)
|
|||
|
{
|
|||
|
if (fragment == null)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
bool isStart = true;
|
|||
|
|
|||
|
for (int i = 0; i < fragment.Length && builder.Length < MaxIdentifierLength; i++)
|
|||
|
{
|
|||
|
char c = fragment[i];
|
|||
|
|
|||
|
if (IsValid(c))
|
|||
|
{
|
|||
|
if (isStart && !IsValidStart(c))
|
|||
|
{
|
|||
|
builder.Append("_");
|
|||
|
}
|
|||
|
builder.Append(c);
|
|||
|
isStart = false;
|
|||
|
}
|
|||
|
else if ((c == '.' || c == '/' || c == ':') && (builder.Length == 1
|
|||
|
|| (builder.Length > 1 && builder[builder.Length - 1] != '.')))
|
|||
|
{
|
|||
|
builder.Append('.');
|
|||
|
isStart = true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
protected override string GetProxyPath()
|
|||
|
{
|
|||
|
return this.path;
|
|||
|
}
|
|||
|
|
|||
|
protected override string GetJsonpCallbackParameterName()
|
|||
|
{
|
|||
|
if (this.serviceEndpoint == null)
|
|||
|
{
|
|||
|
return null;
|
|||
|
}
|
|||
|
WebMessageEncodingBindingElement webEncodingBindingElement = this.serviceEndpoint.Binding.CreateBindingElements().Find<WebMessageEncodingBindingElement>();
|
|||
|
if (webEncodingBindingElement != null && webEncodingBindingElement.CrossDomainScriptAccessEnabled)
|
|||
|
{
|
|||
|
if (this.serviceEndpoint.Contract.Behaviors.Contains(typeof(JavascriptCallbackBehaviorAttribute)))
|
|||
|
{
|
|||
|
JavascriptCallbackBehaviorAttribute behavior = (JavascriptCallbackBehaviorAttribute)this.serviceEndpoint.Contract.Behaviors[typeof(JavascriptCallbackBehaviorAttribute)];
|
|||
|
return behavior.UrlParameterName;
|
|||
|
}
|
|||
|
return DefaultCallbackParameterName;
|
|||
|
}
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
protected override bool GetSupportsJsonp()
|
|||
|
{
|
|||
|
return !String.IsNullOrEmpty(GetJsonpCallbackParameterName());
|
|||
|
}
|
|||
|
|
|||
|
private static Type ReplaceMessageWithObject(Type t)
|
|||
|
{
|
|||
|
return (typeof(Message).IsAssignableFrom(t)) ? typeof(object) : t;
|
|||
|
}
|
|||
|
|
|||
|
static WebServiceData GetWebServiceData(ContractDescription contract)
|
|||
|
{
|
|||
|
WebServiceData serviceData = new WebServiceData();
|
|||
|
|
|||
|
//build method dictionary
|
|||
|
Dictionary<string, WebServiceMethodData> methodDataDictionary = new Dictionary<string, WebServiceMethodData>();
|
|||
|
|
|||
|
// set service type
|
|||
|
serviceData.Initialize(new WebServiceTypeData(XmlConvert.DecodeName(contract.Name), XmlConvert.DecodeName(contract.Namespace), contract.ContractType),
|
|||
|
methodDataDictionary);
|
|||
|
|
|||
|
foreach (OperationDescription operation in contract.Operations)
|
|||
|
{
|
|||
|
Dictionary<string, WebServiceParameterData> parameterDataDictionary = new Dictionary<string, WebServiceParameterData>();
|
|||
|
bool useHttpGet = operation.Behaviors.Find<WebGetAttribute>() != null;
|
|||
|
WebServiceMethodData methodData = new WebServiceMethodData(serviceData, XmlConvert.DecodeName(operation.Name), parameterDataDictionary, useHttpGet);
|
|||
|
// build parameter dictionary
|
|||
|
MessageDescription requestMessage = operation.Messages[0];
|
|||
|
if (requestMessage != null)
|
|||
|
{
|
|||
|
int numMessageParts = requestMessage.Body.Parts.Count;
|
|||
|
for (int p = 0; p < numMessageParts; p++)
|
|||
|
{
|
|||
|
MessagePartDescription messagePart = requestMessage.Body.Parts[p];
|
|||
|
// DevDiv 129964:JS proxy generation fails for a WCF service that uses an untyped message
|
|||
|
// Message or its derived class are special, used for untyped operation contracts.
|
|||
|
// As per the WCF team proxy generated for them should treat Message equivalent to Object type.
|
|||
|
Type paramType = ReplaceMessageWithObject(messagePart.Type);
|
|||
|
WebServiceParameterData parameterData = new WebServiceParameterData(XmlConvert.DecodeName(messagePart.Name), paramType, p);
|
|||
|
parameterDataDictionary[parameterData.ParameterName] = parameterData;
|
|||
|
serviceData.ProcessClientType(paramType, false, true);
|
|||
|
}
|
|||
|
}
|
|||
|
if (operation.Messages.Count > 1)
|
|||
|
{
|
|||
|
// its a two way operation, get type information from return message
|
|||
|
MessageDescription responseMessage = operation.Messages[1];
|
|||
|
if (responseMessage != null)
|
|||
|
{
|
|||
|
if (responseMessage.Body.ReturnValue != null && responseMessage.Body.ReturnValue.Type != null)
|
|||
|
{
|
|||
|
// operation has a return type, add type to list of type proxy to generate
|
|||
|
serviceData.ProcessClientType(ReplaceMessageWithObject(responseMessage.Body.ReturnValue.Type), false, true);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//add known types at operation level
|
|||
|
for (int t = 0; t < operation.KnownTypes.Count; t++)
|
|||
|
{
|
|||
|
serviceData.ProcessClientType(operation.KnownTypes[t], false, true);
|
|||
|
}
|
|||
|
|
|||
|
methodDataDictionary[methodData.MethodName] = methodData;
|
|||
|
}
|
|||
|
serviceData.ClearProcessedTypes();
|
|||
|
return serviceData;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
internal static string GetClientProxyScript(Type contractType, string path, bool debugMode, ServiceEndpoint serviceEndpoint)
|
|||
|
{
|
|||
|
ContractDescription contract = ContractDescription.GetContract(contractType);
|
|||
|
WebServiceData webServiceData = GetWebServiceData(contract);
|
|||
|
WCFServiceClientProxyGenerator proxyGenerator = new WCFServiceClientProxyGenerator(path, debugMode, serviceEndpoint);
|
|||
|
return proxyGenerator.GetClientProxyScript(webServiceData);
|
|||
|
}
|
|||
|
|
|||
|
// Similar to proxy generation code in WCF System.Runtime.Serialization.CodeExporter
|
|||
|
// to generate CLR namespace from DataContract namespace
|
|||
|
protected override string GetClientTypeNamespace(string ns)
|
|||
|
{
|
|||
|
if (string.IsNullOrEmpty(ns))
|
|||
|
{
|
|||
|
return String.Empty;
|
|||
|
}
|
|||
|
|
|||
|
Uri uri = null;
|
|||
|
StringBuilder builder = new StringBuilder();
|
|||
|
if (Uri.TryCreate(ns, UriKind.RelativeOrAbsolute, out uri))
|
|||
|
{
|
|||
|
if (!uri.IsAbsoluteUri)
|
|||
|
{
|
|||
|
AddToNamespace(builder, uri.OriginalString);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
string uriString = uri.AbsoluteUri;
|
|||
|
if (uriString.StartsWith(DataContractXsdBaseNamespace, StringComparison.Ordinal))
|
|||
|
{
|
|||
|
AddToNamespace(builder, uriString.Substring(DataContractXsdBaseNamespace.Length));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
string host = uri.Host;
|
|||
|
if (host != null)
|
|||
|
{
|
|||
|
AddToNamespace(builder, host);
|
|||
|
}
|
|||
|
string path = uri.PathAndQuery;
|
|||
|
if (path != null)
|
|||
|
{
|
|||
|
AddToNamespace(builder, path);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (builder.Length == 0)
|
|||
|
{
|
|||
|
return String.Empty;
|
|||
|
}
|
|||
|
|
|||
|
int length = builder.Length;
|
|||
|
if (builder[builder.Length - 1] == '.')
|
|||
|
{
|
|||
|
length--;
|
|||
|
}
|
|||
|
length = Math.Min(MaxIdentifierLength, length);
|
|||
|
|
|||
|
return builder.ToString(0, length);
|
|||
|
}
|
|||
|
|
|||
|
protected override string GetProxyTypeName(WebServiceData data)
|
|||
|
{
|
|||
|
return GetClientTypeNamespace(data.TypeData.TypeName);
|
|||
|
}
|
|||
|
|
|||
|
// Similar to proxy generation code in WCF System.Runtime.Serialization.CodeExporter
|
|||
|
// to generate CLR namespace from DataContract namespace
|
|||
|
private static bool IsValid(char c)
|
|||
|
{
|
|||
|
UnicodeCategory uc = Char.GetUnicodeCategory(c);
|
|||
|
|
|||
|
// each char must be Lu, Ll, Lt, Lm, Lo, Nd, Mn, Mc, Pc
|
|||
|
|
|||
|
switch (uc)
|
|||
|
{
|
|||
|
case UnicodeCategory.UppercaseLetter: // Lu
|
|||
|
case UnicodeCategory.LowercaseLetter: // Ll
|
|||
|
case UnicodeCategory.TitlecaseLetter: // Lt
|
|||
|
case UnicodeCategory.ModifierLetter: // Lm
|
|||
|
case UnicodeCategory.OtherLetter: // Lo
|
|||
|
case UnicodeCategory.DecimalDigitNumber: // Nd
|
|||
|
case UnicodeCategory.NonSpacingMark: // Mn
|
|||
|
case UnicodeCategory.SpacingCombiningMark: // Mc
|
|||
|
case UnicodeCategory.ConnectorPunctuation: // Pc
|
|||
|
return true;
|
|||
|
default:
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Similar to proxy generation code in WCF System.Runtime.Serialization.CodeExporter
|
|||
|
// to generate CLR namespace from DataContract namespace
|
|||
|
private static bool IsValidStart(char c)
|
|||
|
{
|
|||
|
return (Char.GetUnicodeCategory(c) != UnicodeCategory.DecimalDigitNumber);
|
|||
|
}
|
|||
|
|
|||
|
internal WCFServiceClientProxyGenerator(string path, bool debugMode, ServiceEndpoint serviceEndpoint)
|
|||
|
{
|
|||
|
this.path = path;
|
|||
|
_debugMode = debugMode;
|
|||
|
this.serviceEndpoint = serviceEndpoint;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|