public ApiGraph Build(IEnumerable <Type> controllerTypes)
        {
            var controllers = new List <ControllerElement>();

            foreach (var controllerType in controllerTypes)
            {
                var methods     = new List <MethodElement>();
                var methodInfos = controllerType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
                foreach (var methodInfo in methodInfos)
                {
                    ParameterElement bodyParameter = null;
                    var urlParameters = new List <ParameterElement>();
                    var fullRoute     = GetFullRoute(controllerType, methodInfo);
                    var methodName    = methodInfo.Name.EndsWith("Async")
                        ? methodInfo.Name.Substring(0, methodInfo.Name.Length - 5)
                        : methodInfo.Name;

                    foreach (var parameter in methodInfo.GetParameters())
                    {
                        if (parameter.GetCustomAttribute <FromUriAttribute>() != null || RouteUtility.GetPlaceholders(parameter.Name).Any(fullRoute.Contains))
                        {
                            urlParameters.Add(new ParameterElement(parameter.Name, parameter.ParameterType, !parameter.HasDefaultValue));
                        }
                        else if (bodyParameter == null)
                        {
                            bodyParameter = new ParameterElement(parameter.Name, parameter.ParameterType, true);
                        }
                        else
                        {
                            throw new Exception("Multiple body parameters detected for " + methodInfo.Name);
                        }
                    }

                    methods.Add(new MethodElement(
                                    GetHttpMethod(methodInfo),
                                    methodName,
                                    fullRoute,
                                    urlParameters,
                                    bodyParameter,
                                    TryGetReturnType(controllerType, methodInfo)));
                }

                var controllerName = controllerType.Name.Substring(0, controllerType.Name.Length - ControllerSuffix.Length);
                controllers.Add(new ControllerElement(controllerName, methods));
            }

            return(new ApiGraph(controllers));
        }
        public static string GetRouteBuilder(MethodElement method)
        {
            var route = "'" + method.Route + "'";
            var queryParameters = method.UrlParameters.ToList();
            foreach (var urlParameter in method.UrlParameters)
            {
                foreach (var placeholder in RouteUtility.GetPlaceholders(urlParameter.Name))
                {
                    if (!route.Contains(placeholder))
                    {
                        continue;
                    }

                    queryParameters.Remove(urlParameter);
                    route = route.Replace(placeholder, string.Format("' + encodeURIComponent({0}) + '", urlParameter.Name));
                    break;
                }
            }

            if (route.StartsWith("'' + "))
            {
                route = route.Substring(5);
            }

            if (route.EndsWith(" + ''"))
            {
                route = route.Substring(0, route.Length - 5);
            }

            if (queryParameters.Count > 0)
            {
                if (route.EndsWith("'"))
                {
                    route = route.Substring(0, route.Length - 1);
                    route += "?'";
                }
                else
                {
                    route += " + '?'";
                }

                foreach (var parameter in queryParameters)
                {
                    if (parameter.Type.IsPrimitiveEx())
                    {
                        route += string.Format(
                            " + ({0} === undefined ? '' : '{0}=' + encodeURIComponent({0}) + '&')",
                            parameter.Name);
                    }
                    else
                    {
                        // Flatten complex types used as URL parameters.
                        var flattenedComplexType = parameter.Type.GetProperties().Where(_ => _.PropertyType.IsPrimitiveEx() && _.CanWrite);
                        foreach (var property in flattenedComplexType)
                        {
                            route += string.Format(
                                " + ({0} === undefined ? '' : '{1}=' + encodeURIComponent({0}) + '&')",
                                parameter.Name + "." + property.Name.Camelize(),
                                property.Name.Camelize());
                        }
                    }
                }
            }

            return route;
        }