Пример #1
0
        public virtual ApiServiceDesc AddService(Type controller)
        {
            if (!IsSupportedController(controller))
            {
                throw new Exception($"Unsupported controller type: {controller.FullName}");
            }

            var service = new ApiServiceDesc();

            Api.Services.Add(service);
            AddAssembly(controller.Assembly);
            service.Controller = controller;
            service.Name       = GetServiceName(controller);

            foreach (var method in GetMethods(controller).Where(MethodFilter))
            {
                var desc = GetMethodDesc(service, method);
                if (desc == null)
                {
                    continue;
                }
                service.Methods.Add(desc);
            }

            PostProcessService(service);

            return(service);
        }
Пример #2
0
        protected void OutputService(TypeScriptWriter writer, ApiServiceDesc s)
        {
            _convertersUsedFrom = new HashSet <TypeConverter>();
            _convertersUsedTo   = new HashSet <TypeConverter>();
            writer.WriteLine($"export class {s.Name}Service extends ApiServiceBase {{");
            writer.WriteLine();
            using (writer.Indent())
            {
                writer.WriteLine("private _hostname: string;");
                writer.WriteLine();
                writer.WriteLine("public constructor(hostname: string) {");
                using (writer.Indent())
                {
                    writer.WriteLine("super();");
                    writer.WriteLine("this._hostname = (hostname.substr(-1) == '/') ? hostname : hostname + '/';");
                }
                writer.WriteLine("}");
                writer.WriteLine();
                foreach (var method in s.Methods.OrderBy(m => m.TsName))
                {
                    foreach (var httpMethod in method.HttpMethods.Order())
                    {
                        bool canDirectReturn = getConverter(method.TsReturnType) == null;
                        writer.Write($"public {(canDirectReturn ? "" : "async ")}{getMethodName(method, httpMethod)}(");
                        bool first = true;
                        foreach (var p in method.Parameters)
                        {
                            if (!first)
                            {
                                writer.Write(", ");
                            }
                            writer.Write($"{p.TsName}: {p.TsType.GetTypeScript()}");
                            first = false;
                        }
                        writer.WriteLine($"): {string.Format(ReturnTypeTemplate, method.TsReturnType.GetTypeScript())} {{");
                        using (writer.Indent())
                        {
                            var  url    = method.UrlPath;
                            bool first2 = true;
                            foreach (var p in method.Parameters.Where(p => p.Location == ParameterLocation.QueryString).OrderBy(p => p.TsName))
                            {
                                url   += (first2 ? '?' : '&') + p.TsName + "=${encodeURIComponent('' + " + p.TsName + ")}";
                                first2 = false;
                            }
                            writer.WriteLine($"let url = this._hostname + `{url}`;");

                            // Output parameter type conversions
                            foreach (var p in method.Parameters)
                            {
                                OutputTypeConversion(writer, p.TsName, p.TsType, toTypeScript: false);
                            }

                            // Build request body
                            var bodyParams = method.Parameters.Where(p => p.Location == ParameterLocation.RequestBody).OrderBy(p => p.TsName).ToList();
                            if (bodyParams.Count > 0 && httpMethod == "GET")
                            {
                                throw new InvalidOperationException($"GET requests must not have any body parameters. Offending parameter: {bodyParams[0].TsName}, method {method.Method.Name}, controller {s.Controller.FullName}");
                            }
                            if (bodyParams.Count > 1 && (method.BodyEncoding == BodyEncoding.Raw || method.BodyEncoding == BodyEncoding.Json))
                            {
                                throw new InvalidOperationException($"The body encoding for this method allows for at most one body parameter. Offending parameters: [{bodyParams.Select(p => p.TsName).JoinString(", ")}], method {method.Method.Name}, controller {s.Controller.FullName}");
                            }
                            var bodyCode = "";
                            if (bodyParams.Count > 0)
                            {
                                if (method.BodyEncoding == BodyEncoding.Raw)
                                {
                                    bodyCode = $", body: {bodyParams[0].TsName}";
                                }
                                else if (method.BodyEncoding == BodyEncoding.Json)
                                {
                                    bodyCode = $", body: JSON.stringify({bodyParams[0].TsName}), headers: {{ 'Content-Type': 'application/json' }}";
                                }
                                else if (method.BodyEncoding == BodyEncoding.FormUrlEncoded)
                                {
                                    writer.WriteLine("let __body = new URLSearchParams();");
                                    foreach (var bp in bodyParams)
                                    {
                                        writer.WriteLine($"__body.append('{bp.TsName}', '' + {bp.TsName});");
                                    }
                                    bodyCode = ", body: __body";
                                }
                                else if (method.BodyEncoding == BodyEncoding.MultipartFormData)
                                {
                                    writer.WriteLine("let __body = new FormData();");
                                    foreach (var bp in bodyParams)
                                    {
                                        writer.WriteLine($"__body.append('{bp.TsName}', '' + {bp.TsName});");
                                    }
                                    bodyCode = ", body: __body";
                                    throw new NotImplementedException("FormData encoding is not fully implemented."); // no support for file name, parameter is always stringified with no support for Blob
                                }
                                else
                                {
                                    throw new Exception($"Unexpected {nameof(method.BodyEncoding)}: {method.BodyEncoding}");
                                }
                            }

                            // Output call
                            var sendCookies = s.SendCookies == SendCookies.Always ? "include" : s.SendCookies == SendCookies.SameOriginOnly ? "same-origin" : s.SendCookies == SendCookies.Never ? "omit" : throw new Exception();
                            if (canDirectReturn)
                            {
                                writer.Write("return ");
                            }
                            else
                            {
                                writer.Write("let result = await ");
                            }
                            writer.WriteLine($"this.{httpMethod}<{method.TsReturnType.GetTypeScript()}>(url, {{ credentials: '{sendCookies}'{bodyCode} }});");

                            if (!canDirectReturn)
                            {
                                // Output return type conversion
                                OutputTypeConversion(writer, "result", method.TsReturnType, toTypeScript: true);

                                writer.WriteLine("return result;");
                            }
                        }
                        writer.WriteLine("}");
                        writer.WriteLine();
                    }
                }

                // Output type converters
                foreach (var tc in _typeConverters.Values.Where(c => c != null).OrderBy(c => c.ForType.GetHash()))
                {
                    if (_convertersUsedFrom.Contains(tc))
                    {
                        writer.WriteLine($"private {tc.FunctionName(toTypeScript: false)}(val: {tc.ForType.GetTypeScript()}): any {{");
                        using (writer.Indent())
                            tc.WriteFunctionBody(writer, false);
                        writer.WriteLine("}");
                        writer.WriteLine();
                    }
                    if (_convertersUsedTo.Contains(tc))
                    {
                        writer.WriteLine($"private {tc.FunctionName(toTypeScript: true)}(val: any): {tc.ForType.GetTypeScript()} {{");
                        using (writer.Indent())
                            tc.WriteFunctionBody(writer, true);
                        writer.WriteLine("}");
                        writer.WriteLine();
                    }
                }
            }
            writer.WriteLine("}");
        }
Пример #3
0
        protected override ApiMethodDesc GetMethodDesc(ApiServiceDesc service, MethodInfo method)
        {
            var controllerRoute       = service.Controller.GetAttributes("System.Web.Http.RouteAttribute").SingleOrDefault();
            var controllerRoutePrefix = service.Controller.GetAttributes("System.Web.Http.RoutePrefixAttribute").SingleOrDefault();
            var methodRoute           = method.GetAttributes("System.Web.Http.RouteAttribute").SingleOrDefault();

            if (controllerRoute == null && methodRoute == null)
            {
                return(null);
            }
            var iHttpMethod     = "System.Web.Http.Controllers.IActionHttpMethodProvider";
            var httpMethodsAttr = method.GetAttributesByInterface(iHttpMethod).SingleOrDefault();

            if (httpMethodsAttr == null)
            {
                return(null);
            }

            var m = new ApiMethodDesc();

            m.Method       = method;
            m.Service      = service;
            m.HttpMethods  = ((IEnumerable <object>)httpMethodsAttr.ReadProperty("HttpMethods", Reflection.KnownTypes[iHttpMethod])).Select(hm => ((string)hm.ReadProperty("Method")).ToUpper()).ToList();
            m.BodyEncoding = BodyEncoding.Json;
            m.TsName       = method.Name;
            m.TsReturnType = MapType(method.ReturnType);
            if (m.TsReturnType == null)
            {
                throw new InvalidOperationException($"Cannot map return type {method.ReturnType.FullName} of method {method.Name}, controller {service.Controller.FullName}");
            }
            foreach (var par in method.GetParameters())
            {
                var p = new ApiMethodParameterDesc();
                m.Parameters.Add(p);
                p.Method    = m;
                p.Parameter = par;
                p.TsName    = par.Name;
                p.TsType    = MapType(par.ParameterType);
                if (p.TsType == null)
                {
                    throw new InvalidOperationException($"Cannot map parameter type {par.ParameterType.FullName} of parameter {par.Name}, method {method.Name}, controller {service.Controller.FullName}");
                }
                p.Location = IsUrlParameter(p) ? ParameterLocation.QueryString : ParameterLocation.RequestBody;
            }
            m.UrlPath = "/";
            if (controllerRoutePrefix != null)
            {
                m.UrlPath += (string)controllerRoutePrefix.ReadProperty("Prefix") + "/";
            }
            if (methodRoute != null)
            {
                m.UrlPath += (string)methodRoute.ReadProperty("Template");
            }
            else // controllerRoute != null
            {
                m.UrlPath += (string)controllerRoute.ReadProperty("Template");
            }
            m.UrlPath = m.UrlPath.Substring(1);

            var pars = m.Parameters.ToDictionary(p => p.TsName);

            m.UrlPath = Regex.Replace(m.UrlPath, @"{([a-zA-Z0-9]+)}", match =>
            {
                var parName = match.Groups[1].Value;
                if (!pars.ContainsKey(parName))
                {
                    throw new InvalidOperationException($"No matching method parameter found for URL segment \"{match.Value}\", method {method.Name}, controller {service.Controller.FullName}");
                }
                pars[parName].Location = ParameterLocation.UrlSegment;
                return("${encodeURIComponent('' + " + parName + ")}");
            });

            return(m);
        }
Пример #4
0
 protected abstract ApiMethodDesc GetMethodDesc(ApiServiceDesc service, MethodInfo method);
Пример #5
0
 protected virtual void PostProcessService(ApiServiceDesc service)
 {
 }