Search code examples
web-serviceswsdltypescripttypingjavascript-debugger

Typescript definitions for wsdl


I'm using asp:ScriptManager to call my webmethods from javascript. Now I migrate to TypeScript and would like to have my WebMethods and DTO definitions be usable from TypeScript. I searched, but couldn't not find anything (except a suggestion) that does this.

Now I'm implementing a code-generation tool that takes wsdl and generates typescript typings. I'll share it here when it is done, but meanwhile if you know any tool that does this, I'll be happy to hear.

Thanks in advance.


This is how I call webmethods from javascipt.

1) web.config

<system.serviceModel>
<services>
  <service name="Services.MyService" behaviorConfiguration="metadataBehavior">
    <endpoint binding="basicHttpBinding" contract="Services.MyService"/>
    <endpoint address="/ajax" behaviorConfiguration="Services.MyServiceAspNetAjaxBehavior" binding="webHttpBinding" contract="Services.MyService"/>
  </service>
</services>
<behaviors>
  <endpointBehaviors>
    <behavior name="Services.MyServiceAspNetAjaxBehavior">
      <enableWebScript />
    </behavior>
  </endpointBehaviors>
</behaviors>
</system.serviceModel>

2) Default.aspx

<form id="form1" runat="server">
    <asp:ScriptManager ID="Scriptmanager1" runat="server">
        <Services>
            <asp:ServiceReference Path="~/MyService.svc/ajax" />
        </Services>
    </asp:ScriptManager>
</form>

3) SomeFile.js

<script>
    MyService.SomeMethod(someParameter1, someParameter2, function(result) {
        alert("Success: " + result);
    }, function(err){
        alert("Error: " + err);
    });

</script>

I can see a js file that has functions to call my webservice on ~/MyService.svc/ajax/jsdebug


Solution

  • Well here is the code i write and use, but you need to change some parameters for your own wsdl. Feedbacks are appriciated.

    Also if someone upgrades and makes this into a blog post, that would be great I guess (I don't have a blog) many people would need this I think, this saves a lot of time on our project and tidy things up.

    By the way I run this code in Linqpad. That's why methods are not static and there is a object.Dump() call in the last line of Main()

    void Main()
    {
        var svcUrl = "http://localhost:12345/MyService.svc";
        var declaration = "declare var MyService: MyServiceClient;";
        var koRef = "/// <reference path=\"../../Scripts/typings/knockout.mapping/knockout.mapping.d.ts" +
        "\" />\r\n/// <reference path=\"../../Scripts/typings/knockout/knockout.d.ts\" />";
    
    
        TypeScriptWebServiceAndKnockOutTypings(svcUrl, declaration, koRef).Dump();
    }
    
    
    string TypeScriptWebServiceAndKnockOutTypings(string svcUrl, string declaration, string koRef)
    {
        var outputPath = @"d:\output";
    
        File.Delete(outputPath + ".cs");
        File.Delete(outputPath + ".dll");
        Process.Start(@"C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools\x64\svcutil.exe",  " /language:cs /out:" + outputPath + ".cs " + svcUrl).WaitForExit();
        Process.Start(@"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe", " /t:library /out:"+ outputPath + ".dll  "+ outputPath + ".cs").WaitForExit();
    
        var a = Assembly.LoadFile(outputPath + ".dll");
    
        var sbEnums = new StringBuilder();
        var sbClasses = new StringBuilder();
    
        #region types conversions
    
        var dictTypesNormal = new Dictionary<Type, string>();
        dictTypesNormal.Add(typeof(Int32), "number");
        dictTypesNormal.Add(typeof(UInt32), "number");
        dictTypesNormal.Add(typeof(Int64), "number");
        dictTypesNormal.Add(typeof(UInt64), "number");
        dictTypesNormal.Add(typeof(float), "number");
        dictTypesNormal.Add(typeof(double), "number");
        dictTypesNormal.Add(typeof(bool), "bool");
        dictTypesNormal.Add(typeof(string), "string");
        dictTypesNormal.Add(typeof(Object), "any");
        dictTypesNormal.Add(typeof(DateTime), "Date");
    
        var dictTypesKnockout = new Dictionary<Type, string>();
        dictTypesKnockout.Add(typeof(Int32), "KnockoutObservableNumber");
        dictTypesKnockout.Add(typeof(UInt32), "KnockoutObservableNumber");
        dictTypesKnockout.Add(typeof(Int64), "KnockoutObservableNumber");
        dictTypesKnockout.Add(typeof(UInt64), "KnockoutObservableNumber");
        dictTypesKnockout.Add(typeof(float), "KnockoutObservableNumber");
        dictTypesKnockout.Add(typeof(double), "KnockoutObservableNumber");
        dictTypesKnockout.Add(typeof(bool), "KnockoutObservableBool");
        dictTypesKnockout.Add(typeof(string), "KnockoutObservableString");
        dictTypesKnockout.Add(typeof(Object), "KnockoutObservableAny");
        dictTypesKnockout.Add(typeof(DateTime), "KnockoutObservableDate");
    
        var classNames = a.DefinedTypes.Where(t => !t.IsEnum).Select(t=>t.Name).ToList();
        var enumNames = a.DefinedTypes.Where(t => t.IsEnum).Select(t=>t.Name).ToList();
    
        #region get type name
    
        Func<Type, string> getTypeNameNormal = t => {
            if(dictTypesNormal.ContainsKey(t)) 
                return dictTypesNormal[t];
    
            if(classNames.Contains(t.Name.Replace("[]", ""))) 
                return t.Name;
    
            if(enumNames.Contains(t.Name.Replace("[]", ""))) 
                return "Enum" + t.Name;
    
            throw new Exception(string.Format("Unknown type [{0}]", t.Name));
        };
    
        Func<Type, string> getTypeNameKnockout = t => {
            if(dictTypesKnockout.ContainsKey(t)) 
                return dictTypesKnockout[t];
    
            if(classNames.Contains(t.Name.Replace("[]", ""))) 
                return t.Name + "_KnockoutMapped";
    
            if(enumNames.Contains(t.Name.Replace("[]", ""))) 
                return "KnockoutObservableEnum" + t.Name + "EnumMember";
    
            throw new Exception(string.Format("Unknown type [{0}]", t.Name));
        };
    
        #endregion
    
        #region convert field 
    
        Func<string, Type, string> convertFieldNormal = null; convertFieldNormal = (pName, f) => {
    
            // void
            if(f == typeof(void)) 
                return "";
    
            // array
            if(f.IsArray)
                return string.Format("{0}{1}[]", pName, convertFieldNormal("", f.GetElementType()));
    
            // property
            if(!f.IsGenericType) 
                return string.Format("{0}: {1}", pName, getTypeNameNormal(f));
    
            // nullable
            if(f.GetGenericTypeDefinition() == typeof(Nullable<>)) 
                return string.Format("{0}: {1}", pName, getTypeNameNormal(f.GetGenericArguments()[0]));
    
            // dictionaries
            if(f.GetGenericTypeDefinition().ToString().Contains("Dictionary")) 
                return string.Format("{0}: {{ {1}; {2}; }}[]", pName, convertFieldNormal("Key", f.GetGenericArguments()[0]), convertFieldNormal("Value", f.GetGenericArguments()[1]));
    
            throw new Exception(string.Format("Unknown generic type [{0}] for property {1}", f.Name, pName));
        };
    
        Func<string, Type, string> convertFieldKnockout = null; convertFieldKnockout = (pName, f) => {
    
            // void
            if(f == typeof(void)) 
                return "";
    
            // array
            if(f.IsArray)
                return string.Format("{0}{1}[]", pName, convertFieldKnockout("", f.GetElementType()));
    
            // property
            if(!f.IsGenericType) 
                return string.Format("{0}: {1}", pName, getTypeNameKnockout(f));
    
            // nullable
            if(f.GetGenericTypeDefinition() == typeof(Nullable<>)) 
                return string.Format("{0}: {1}", pName, getTypeNameKnockout(f.GetGenericArguments()[0]));
    
            // dictionaries
            if(f.GetGenericTypeDefinition().ToString().Contains("Dictionary")) 
                return string.Format("{0}: {{ {1}; {2}; }}[]", pName, convertFieldKnockout("Key", f.GetGenericArguments()[0]), convertFieldKnockout("Value", f.GetGenericArguments()[1]));
    
            throw new Exception(string.Format("Unknown generic type [{0}] for property {1}", f.Name, pName));
        };
    
        #endregion
    
        #endregion
    
        #region enums
    
        foreach (var e in a.DefinedTypes.Where(t => t.IsEnum))
        {
            #region enum definition
            sbEnums.AppendLine(string.Format("enum Enum{0} {{", e.Name));
    
            sbEnums.AppendLine(string.Format("\t{0} = {1},", "Tümü", -1)); 
    
            foreach (var v in Enum.GetValues(e))
            {
                sbEnums.AppendLine(string.Format("\t{0} = {1},", v.ToString(), (int)v)); 
            }
    
            sbEnums.AppendLine("}");
            #endregion
    
            #region knockout
    
            sbEnums.AppendLine(string.Format(
                @"interface KnockoutObservable{0} extends KnockoutObservableBase {{
                    (): {0};
                    (value: {0}): void;
    
                    subscribe(callback: (newValue: {0}) => void , target?: any, topic?: string): KnockoutSubscription;
                    notifySubscribers(valueToWrite: {0}, topic?: string);
                }}", "Enum" + e.Name + "EnumMember"));
    
            #endregion
    
            #region enum member
    
            sbEnums.AppendLine(string.Format(
                @"class Enum{0}EnumMember {{
                    constructor(public Key: Enum{0}, public Value: string) {{ }};
                    public toString() {{ return this.Value }};
                  }}", e.Name));
    
            #endregion
    
            #region combobox definition
            sbEnums.AppendLine(string.Format("var {0}s: Enum{0}EnumMember[] = [", e.Name));
    
            sbEnums.AppendLine(string.Format("\t new Enum{0}EnumMember(Enum{0}.{1}, \"{1}\"),", e.Name, "Tümü")); 
    
            foreach (var v in Enum.GetValues(e))
            {
                sbEnums.AppendLine(string.Format("\t new Enum{0}EnumMember(Enum{0}.{1}, \"{1}\"),", e.Name, v.ToString())); 
            }
    
            sbEnums.AppendLine("];");
            #endregion
    
            //public OnayDurumlari = ko.observable([{ Key: 3, Value: "Tümü" }, { Key: 0, Value: "Onay Bekliyor" }, { Key: 1, Value: "Onaylı" }, { Key: 2, Value: "Red" }]); 
    
            sbEnums.AppendLine();
        }
    
        #endregion
    
        #region classes
    
        #region find classes & methods
    
        var classes =  (from t in a.DefinedTypes
                        where t.IsEnum == false
                        let methods = (from m in t.GetMethods()
                                        where !m.IsSpecialName 
                                        && m.IsFinal 
                                        && m.IsVirtual 
                                        && m.IsHideBySig 
                                        && m.IsSecurityCritical 
                                        && !m.Name.EndsWith("Async")
                                        select m)
                        where t.DeclaredProperties.Any()
                            || methods.Any()
                        select new
                        {
                            t.Name,
                            t.DeclaredProperties,
                            methods,
                        });
    
        #endregion
    
        foreach (var t in classes)
        {
            #region interface definition
    
            sbClasses.AppendLine(string.Format("{1} {0} {{", t.Name, t.DeclaredProperties.Any() ? "class" : "interface"));
    
            foreach (var p in t.DeclaredProperties)
            {
                if(p.Name == "ExtensionData") continue;
                var f = p.GetGetMethod().ReturnType;
    
                sbClasses.AppendLine(string.Format("\t{0};", convertFieldNormal(p.Name, f)));
            }
    
            #region methods
    
            foreach (var m in t.methods)
            {
                sbClasses.AppendLine(string.Format("\t{0}({1}Success: ({2}) => void, Fail?: (err: Sys$Net$WebServiceError) => void ) : void;", 
                    m.Name, 
                    string.Concat(m.GetParameters().Select(p => convertFieldNormal(p.Name, p.ParameterType) + ", ")),
                    convertFieldNormal("result", m.ReturnType)));
            }
    
            #endregion
    
            sbClasses.AppendLine("}");
    
            #endregion
    
            #region knockout
    
            if(t.DeclaredProperties.Any())
            {
                sbClasses.AppendLine(string.Format(@"
                    interface KnockoutObservable{0} extends KnockoutObservableBase {{
                        (): {0};
                        (value: {0}): void;
    
                        subscribe(callback: (newValue: {0}) => void , target?: any, topic?: string): KnockoutSubscription;
                        notifySubscribers(valueToWrite: {0}, topic?: string);
                    }}", t.Name));
    //  
    //          sbClasses.AppendLine(string.Format(@"
    //              interface KnockoutObservableStatic {{
    //                  (value: {0}): KnockoutObservable{0};
    //                  new (value: {0}): KnockoutObservable{0};
    //              }}", t.Name));
    
                #region KnockoutObservableArray
    
                sbClasses.AppendLine(string.Format(
                    @"interface {0}_KnockoutObservableArray extends KnockoutObservableArrayFunctions {{
                        (): {0}_KnockoutMapped[];
                        (value: {0}_KnockoutMapped[]): void;
    
                        subscribe(callback: (newValue: {0}_KnockoutMapped[]) => void, target?:any, topic?: string): KnockoutSubscription;
                        notifySubscribers(valueToWrite: {0}_KnockoutMapped[], topic?: string);
                    }}", t.Name));
    
                #endregion
    
                #region _KnockoutMapped
                sbClasses.AppendLine(string.Format("interface {0}_KnockoutMapped {{", t.Name));
    
                foreach (var p in t.DeclaredProperties)
                {
                    if(p.Name == "ExtensionData") continue;
                    var f = p.GetGetMethod().ReturnType;
    
                    sbClasses.AppendLine(string.Format("\t{0};", convertFieldKnockout(p.Name, f)));
                }               
                sbClasses.AppendLine("}");
                #endregion
    
            }
            #endregion
    
            sbClasses.AppendLine();
        }
    
        #endregion
    
        #region knockout
    
        var sbKnockout = new StringBuilder();
    
        #region mapper
        sbKnockout.AppendLine("module Mapper {");
        classes.Where(c=>c.DeclaredProperties.Any()).ToList().ForEach(c=>{
            sbKnockout.AppendLine(string.Format("\texport function fromJs_{0}(r : {0}) : {0}_KnockoutMapped {{return ko.mapping.fromJS(r);}}", c.Name));
            sbKnockout.AppendLine(string.Format("\texport function toJs_{0}(r : {0}_KnockoutMapped) : {0} {{return ko.mapping.toJS(r);}}", c.Name));
            sbKnockout.AppendLine(string.Format("\texport function fromJsArray_{0}(r : {0}[]) : {0}_KnockoutMapped[] {{return r.map(k => ko.mapping.fromJS(k));}}", c.Name));
            sbKnockout.AppendLine(string.Format("\texport function toJsArray_{0}(r : {0}_KnockoutMapped[]) : {0}[] {{return r.map(k => ko.mapping.toJS(k));}}", c.Name));
        });
        sbKnockout.AppendLine("}");
        sbKnockout.AppendLine();
        #endregion
    
        #region knockout definitions
        sbKnockout.AppendLine("declare var ko;");
        dictTypesKnockout.Select(k=>k.Value).Distinct().ToList().ForEach(k=>{
            sbKnockout.AppendLine(string.Format("interface {0} {{}}", k));
        });
        sbKnockout.AppendLine("interface KnockoutSubscription {}");
        sbKnockout.AppendLine("interface KnockoutObservableArrayFunctions {}");
        sbKnockout.AppendLine();
        #endregion
    
        #endregion
    
        return koRef 
             + "interface Sys$Net$WebServiceError {_timedOut: bool;_message: string;_stackTrace: string;_exceptionType: string;_errorObject: {ExceptionDetail: {HelpLink: any; InnerException: any; Message: string; StackTrace: string;Type: string;};ExceptionType: string;Message: string;StackTrace: string;};}\r\n\r\n"
             + sbEnums.ToString()
             + sbClasses.ToString()
             + sbKnockout
             + declaration;
    }