private Dictionary <HttpServiceActionTypeTransferObject, string> GetActionTypes(IAspDotNetOptions options) { Dictionary <HttpServiceActionTypeTransferObject, string> dictionary = new(); if (options.HttpGet) { dictionary.Add(HttpServiceActionTypeTransferObject.Get, options.HttpGetRoute); } if (options.HttpPost) { dictionary.Add(HttpServiceActionTypeTransferObject.Post, options.HttpPostRoute); } if (options.HttpPatch) { dictionary.Add(HttpServiceActionTypeTransferObject.Patch, options.HttpPatchRoute); } if (options.HttpPut) { dictionary.Add(HttpServiceActionTypeTransferObject.Put, options.HttpPutRoute); } if (options.HttpDelete) { dictionary.Add(HttpServiceActionTypeTransferObject.Delete, options.HttpDeleteRoute); } return(dictionary); }
public virtual void Read(AspDotNetReadConfiguration configuration) { configuration.Controller.AssertIsNotNull($"ASP: {nameof(configuration.Controller)}"); configuration.Controller.Name.AssertIsNotNull($"ASP: {nameof(configuration.Controller)}.{nameof(configuration.Controller.Name)}"); configuration.Controller.Namespace.AssertIsNotNull($"ASP: {nameof(configuration.Controller)}.{nameof(configuration.Controller.Namespace)}"); Logger.Trace($"Read ASP.NET controller {configuration.Controller.Namespace}.{configuration.Controller.Name}..."); Type type = GeneratorTypeLoader.Get(configuration.Controller.Assembly, configuration.Controller.Namespace, configuration.Controller.Name); if (type == null) { return; } HttpServiceTransferObject controller = new(); controller.Name = type.Name; controller.Language = ReflectionLanguage.Instance; IOptions typeOptions = this.options.Get(type); this.options.Set(controller, typeOptions); IAspDotNetOptions typeAspOptions = this.aspOptions.Get(type); this.aspOptions.Set(controller, typeAspOptions); controller.Route = typeAspOptions.Route; controller.Version = typeAspOptions.ApiVersion?.LastOrDefault(); List <MethodInfo> methods = new(); Type currentTyp = type; while (currentTyp?.Namespace != null && !currentTyp.Namespace.StartsWith("Microsoft") && !currentTyp.Namespace.StartsWith("System")) { IOptions currentOptions = this.options.Get(currentTyp); if (currentOptions.Ignore) { break; } methods.AddRange(currentTyp.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly) // Remove all overwritten methods .Where(method => methods.All(m => m.GetBaseDefinition() != method)) ); currentTyp = currentTyp.BaseType; } foreach (MethodInfo method in methods) { IOptions methodOptions = this.options.Get(method); IAspDotNetOptions methodAspOptions = this.aspOptions.Get(method); if (methodOptions.Ignore || methodAspOptions.IsNonAction) { continue; } Dictionary <HttpServiceActionTypeTransferObject, string> actionTypes = this.GetActionTypes(methodAspOptions); if (actionTypes.Count == 0) { Logger.Error($"{type.FullName}.{method.Name} has to be decorated with at least one of [HttpGet], [HttpPost], [HttpPut], [HttpPatch], [HttpDelete], [NonAction] or with [GenerateIgnore]."); continue; } Type returnType = (methodAspOptions.Produces ?? method.ReturnType) .IgnoreGeneric("System.Threading.Tasks", "Task") .IgnoreGeneric("Microsoft.AspNetCore.Mvc", "IActionResult") .IgnoreGeneric("Microsoft.AspNetCore.Mvc", "ActionResult") .IgnoreGeneric("System.Web.Mvc", "ActionResult") .IgnoreGeneric("System.Web.Mvc", "ContentResult") .IgnoreGeneric("System.Web.Mvc", "EmptyResult") .IgnoreGeneric("System.Web.Mvc", "FileContentResult") .IgnoreGeneric("System.Web.Mvc", "FilePathResult") .IgnoreGeneric("System.Web.Mvc", "FileResult") .IgnoreGeneric("System.Web.Mvc", "FileStreamResult") .IgnoreGeneric("System.Web.Mvc", "JsonResult") .IgnoreGeneric(typeAspOptions.IgnoreGenerics) .IgnoreGeneric(methodAspOptions.IgnoreGenerics); Type returnEntryType = returnType.IgnoreGeneric(typeof(IEnumerable <>)).IgnoreGeneric(typeof(List <>)).IgnoreGeneric(typeof(IList <>)); IAspDotNetOptions returnEntryTypeOptions = this.aspOptions.Get(returnEntryType, methodAspOptions); foreach (KeyValuePair <HttpServiceActionTypeTransferObject, string> actionType in actionTypes) { HttpServiceActionTransferObject action = new(); action.Name = actionTypes.Count == 1 ? method.Name : $"{actionType.Key}{method.Name.FirstCharToUpper()}"; action.ReturnType = this.modelReader.Read(returnType, methodOptions); action.Route = actionType.Value ?? methodAspOptions.Route; if (action.Route?.Contains(":") ?? false) { action.Route = Regex.Replace(action.Route, "({[^:]*)((:apiVersion)|:[^}?]+)(\\??})", "$1$3$4"); } action.Type = actionType.Key; action.Version = methodAspOptions.ApiVersion?.OrderByDescending(x => x).FirstOrDefault(); action.FixCasingWithMapping = returnEntryTypeOptions.FixCasingWithMapping || methodAspOptions.FixCasingWithMapping; action.RequireBodyParameter = action.Type.IsBodyParameterRequired(); List <ParameterInfo> parameters = method.GetParameters().Where( parameter => !this.aspOptions.Get(parameter, methodAspOptions).IsFromHeader && !this.aspOptions.Get(parameter, methodAspOptions).IsFromServices && parameter.ParameterType.FullName != "System.Threading.CancellationToken" ).ToList(); foreach (ParameterInfo parameter in parameters) { IAspDotNetOptions parameterOptions = this.aspOptions.Get(parameter, methodAspOptions); string fullRoute = $"{controller.Route}/{action.Route}"; HttpServiceActionParameterTransferObject actionParameter = new(); actionParameter.Name = parameter.Name; actionParameter.Type = this.modelReader.Read(parameter.ParameterType, methodOptions); actionParameter.FromBody = parameterOptions.IsFromBody || action.Type != HttpServiceActionTypeTransferObject.Get && !parameter.ParameterType.IsValueType && parameter.ParameterType != typeof(string); actionParameter.FromQuery = parameterOptions.IsFromQuery; actionParameter.Inline = fullRoute.Contains($"{{{parameter.Name}}}"); actionParameter.InlineIndex = actionParameter.Inline && action.Route != null?action.Route.IndexOf($"{{{parameter.Name}}}") : 0; actionParameter.IsOptional = parameter.IsOptional; if (fullRoute.Contains($"{{{parameter.Name}?}}")) { actionParameter.Inline = true; actionParameter.IsOptional = true; actionParameter.InlineIndex = fullRoute.IndexOf($"{{{parameter.Name}?}}"); action.Route = action.Route.Replace($"{{{parameter.Name}?}}", $"{{{parameter.Name}}}"); } action.Parameters.Add(actionParameter); if (action.Type == HttpServiceActionTypeTransferObject.Get && actionParameter.Type.Name == "List" && !actionParameter.FromQuery) { Logger.Error($"HttpGet methods with list parameter '{parameter.Name}' of {type.FullName}.{method.Name} has to be decorated with [FromQuery]"); } } if (action.RequireBodyParameter) { if (action.Parameters.Count == 0) { throw new InvalidOperationException($"Can not write {method.Name}. {action.Type} requires at least one parameter, but no parameter found. Use [GenerateIgnore] to skip generation for that method"); } if (action.Parameters.Count == 1) { action.Parameters.Single().FromBody = true; } else if (action.Parameters.All(x => !x.FromBody)) { throw new InvalidOperationException($"Can not write {method.Name}. {action.Type} requires at least one parameter marked with [FromBody] or can have only one parameter"); } } if (action.Parameters.Count(x => x.FromBody) > 1) { throw new InvalidOperationException($"Can not write {method.Name}. {action.Type} can have only one parameter marked with [FromBody] or only one reference type"); } int tries = 0; while (controller.Actions.Any(a => a.Name == action.Name)) { if (tries > 10) { throw new InvalidOperationException($"Can not find a suitable name for {action.Name}"); } if (parameters.Count > 0 && tries == 0) { action.Name += "By" + parameters.First().Name.FirstCharToUpper(); } else if (parameters.Count > tries) { action.Name += "And" + parameters.Skip(tries).First().Name.FirstCharToUpper(); } else { action.Name += tries - parameters.Count + 1; } tries++; } controller.Actions.Add(action); } } transferObjects.Add(controller); }
public void Read(AspDotNetReadConfiguration configuration) { configuration.Hub.AssertIsNotNull($"SignalR: {nameof(configuration.Hub)}"); configuration.Hub.Name.AssertIsNotNull($"SignalR: {nameof(configuration.Hub)}.{nameof(configuration.Hub.Name)}"); configuration.Hub.Namespace.AssertIsNotNull($"SignalR: {nameof(configuration.Hub)}.{nameof(configuration.Hub.Namespace)}"); Logger.Trace($"Read SignalR hub {configuration.Hub.Namespace}.{configuration.Hub.Name}..."); Type type = GeneratorTypeLoader.Get(configuration.Hub.Assembly, configuration.Hub.Namespace, configuration.Hub.Name); if (type == null || type.BaseType == null || type.BaseType.Name != "Hub`1") { return; } if (!type.BaseType.IsGenericType) { Logger.Error("Implement generic Hub<T> instead of non-generic Hub type"); } SignalRHubTransferObject hub = new(); hub.Name = type.Name; hub.Language = ReflectionLanguage.Instance; IAspDotNetOptions typeOptions = this.aspOptions.Get(type); this.aspOptions.Set(hub, typeOptions); MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly); foreach (MethodInfo method in methods) { if (method.Name == "OnConnectedAsync" || method.Name == "OnDisconnectedAsync") { continue; } IOptions methodOptions = this.options.Get(method); HttpServiceActionTransferObject action = new(); action.Name = method.Name; if (method.ReturnType.Name != typeof(void).Name && method.ReturnType.Name != nameof(Task)) { Logger.Error($"Return type of method {method.Name} in {hub.Name} has to be void or Task"); } foreach (ParameterInfo parameter in method.GetParameters()) { action.Parameters.Add(new HttpServiceActionParameterTransferObject { Name = parameter.Name, Type = this.modelReader.Read(parameter.ParameterType, methodOptions) }); } hub.Actions.Add(action); } Type notificationType = type.BaseType.GetGenericArguments().Single(); MethodInfo[] notificationMethods = notificationType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly); foreach (MethodInfo method in notificationMethods) { IOptions methodOptions = this.options.Get(method); HttpServiceActionTransferObject action = new(); action.Name = method.Name; foreach (ParameterInfo parameter in method.GetParameters()) { action.Parameters.Add(new HttpServiceActionParameterTransferObject { Name = parameter.Name, Type = this.modelReader.Read(parameter.ParameterType, methodOptions) }); } hub.Events.Add(action); } transferObjects.Add(hub); }