diff --git "a/doc/AutoWasm\347\211\210\346\234\254\346\227\245\345\277\227.md" "b/doc/AutoWasm\347\211\210\346\234\254\346\227\245\345\277\227.md" index 24c4880..ca373dd 100644 --- "a/doc/AutoWasm\347\211\210\346\234\254\346\227\245\345\277\227.md" +++ "b/doc/AutoWasm\347\211\210\346\234\254\346\227\245\345\277\227.md" @@ -1,5 +1,19 @@ # 版本功能更新记录 +## v2026.05.15 +- 🛠`ApiInvoker` -> `ApiClient` +- 🛠重构`WebControllerGenerator`代码,支持`MinimalApi`模式(默认) +```csharp + +[assembly: WebControllerAssembly(Mode = ApiMode.Controller)] +[assembly: WebControllerAssembly] + +// minimal api 模式 +app.MapAutoWasmApiEndPoints(); +// controller 模式 +app.MapControllers(); +``` + ## v2026.02.12 - ⚡️方法标注`WebMethodNotSupportedAttribute`时,理应同时处理为`ApiInvokeNotSupported` diff --git a/src/AutoWasmApi.Roslyn/ApiInvokeErrorResultGenerator.cs b/src/AutoWasmApi.Roslyn/ApiClientErrorResultGenerator.cs similarity index 95% rename from src/AutoWasmApi.Roslyn/ApiInvokeErrorResultGenerator.cs rename to src/AutoWasmApi.Roslyn/ApiClientErrorResultGenerator.cs index 8010b37..353983b 100644 --- a/src/AutoWasmApi.Roslyn/ApiInvokeErrorResultGenerator.cs +++ b/src/AutoWasmApi.Roslyn/ApiClientErrorResultGenerator.cs @@ -6,7 +6,7 @@ //namespace AutoWasmApiGenerator; //[Generator(LanguageNames.CSharp)] -//public class ApiInvokeErrorResultGenerator : IIncrementalGenerator +//public class ApiClientErrorResultGenerator : IIncrementalGenerator //{ // public void Initialize(IncrementalGeneratorInitializationContext context) // { diff --git a/src/AutoWasmApi.Roslyn/ApiInvokerGenerator.cs b/src/AutoWasmApi.Roslyn/ApiClientGenerator.cs similarity index 96% rename from src/AutoWasmApi.Roslyn/ApiInvokerGenerator.cs rename to src/AutoWasmApi.Roslyn/ApiClientGenerator.cs index e16864f..aec4107 100644 --- a/src/AutoWasmApi.Roslyn/ApiInvokerGenerator.cs +++ b/src/AutoWasmApi.Roslyn/ApiClientGenerator.cs @@ -15,14 +15,14 @@ namespace AutoWasmApiGenerator { [Generator(LanguageNames.CSharp)] - public class ApiInvokerGenerator : IIncrementalGenerator + public class ApiClientGenerator : IIncrementalGenerator { private const string CUSTOM_JSON_OPTION = "AutoWasmApiGenerator.AutoWasmApiGeneratorJsonHelper.Option"; public void Initialize(IncrementalGeneratorInitializationContext context) { //var list = context.SyntaxProvider.ForAttributeWithMetadataName( - // ApiInvokerAssemblyAttributeFullName, + // ApiClientAssemblyAttributeFullName, // static (node, token) => true, // static (c, t) => c); @@ -30,11 +30,11 @@ public void Initialize(IncrementalGeneratorInitializationContext context) { try { - if (!compilation.Assembly.HasAttribute(ApiInvokerAssemblyAttributeFullName)) + if (!compilation.Assembly.HasAttribute(ApiClientAssemblyAttributeFullName)) { return; } - _ = compilation.Assembly.GetAttribute(ApiInvokerAssemblyAttributeFullName, out var asmAttributeData); + _ = compilation.Assembly.GetAttribute(ApiClientAssemblyAttributeFullName, out var asmAttributeData); var all = compilation.GetAllSymbols(WebControllerAttributeFullName); foreach (var item in all) { @@ -54,7 +54,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) id: "ERROR00001", title: "生成错误", messageFormat: ex.Message, - category: typeof(ApiInvokerGenerator).FullName!, + category: typeof(ApiClientGenerator).FullName!, defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true ), Location.None)); @@ -98,7 +98,7 @@ private static bool CreateCodeFile(INamedTypeSymbol interfaceSymbol members.AddRange(fields); members.Add(constructor); - file = CodeFile.New($"{interfaceSymbol.FormatFileName()}ApiInvoker.g.cs") + file = CodeFile.New($"{interfaceSymbol.FormatFileName()}ApiClient.g.cs") .AddUsings("using Microsoft.Extensions.DependencyInjection;") .AddMembers(ns.AddMembers(invokeClass.AddMembers([.. members]))); @@ -127,7 +127,7 @@ private static (MethodBuilder?, Diagnostic?) BuildMethod(INamedTypeSymbol iSymbo .Generic([.. methodSymbol.GetTypeParameters()]) .ReturnType(methodSymbol.ReturnType.ToDisplayString()) .AddParameter([.. methodSymbol.Parameters.Select(p => $"{p.Type.ToDisplayString()} {p.Name}")]) - .AddGeneratedCodeAttribute(typeof(ApiInvokerGenerator)) + .AddGeneratedCodeAttribute(typeof(ApiClientGenerator)) .Lambda("throw new global::System.NotSupportedException()"); return (b, null); } @@ -489,7 +489,7 @@ string CheckFieldNameAndType(IFieldSymbol field) .Async() .ReturnType(methodSymbol.ReturnType.ToDisplayString()) .AddParameter([.. methodSymbol.Parameters.Select(p => $"{p.Type.ToDisplayString()} {p.Name}")]) - .AddGeneratedCodeAttribute(typeof(ApiInvokerGenerator)) + .AddGeneratedCodeAttribute(typeof(ApiClientGenerator)) .AddBody([.. statements]); return (builder, null); } @@ -589,7 +589,7 @@ private static IEnumerable BuildField() .MemberType("global::System.Net.Http.IHttpClientFactory") .FieldName("clientFactory"); yield return FieldBuilder.Default - .MemberType("global::AutoWasmApiGenerator.IGeneratedApiInvokeDelegatingHandler") + .MemberType("global::AutoWasmApiGenerator.IGeneratedApiClientDelegatingHandler") .FieldName("delegatingHandler"); yield return FieldBuilder.Default .MemberType("global::System.IServiceProvider") @@ -606,11 +606,11 @@ private static ConstructorBuilder BuildConstructor(INamedTypeSymbol classSymbol) parameters.Add("global::System.IServiceProvider services"); body.Add("serviceProvider = services"); - body.Add("delegatingHandler = services.GetService() ?? global::AutoWasmApiGenerator.GeneratedApiInvokeDelegatingHandler.Default"); + body.Add("delegatingHandler = services.GetService() ?? global::AutoWasmApiGenerator.GeneratedApiClientDelegatingHandler.Default"); return ConstructorBuilder.Default - .MethodName($"{FormatClassName(classSymbol.MetadataName)}ApiInvoker") + .MethodName($"{FormatClassName(classSymbol.MetadataName)}ApiClient") .AddParameter([.. parameters]) .AddBody([.. body]); } @@ -618,19 +618,19 @@ private static ConstructorBuilder BuildConstructor(INamedTypeSymbol classSymbol) private static ClassBuilder CreateHttpClassBuilder(INamedTypeSymbol interfaceSymbol) { // IEnumerable additionalAttribute = []; - // if (interfaceSymbol.GetAttribute(ApiInvokerAttributeFullName, out var data)) + // if (interfaceSymbol.GetAttribute(ApiClientAttributeFullName, out var data)) // { - // //var o = data.GetAttributeValue(nameof(ApiInvokerGeneraAttribute.Attribute)); - // additionalAttribute = interfaceSymbol.GetAttributeInitInfo(ApiInvokerAttributeFullName, data!); + // //var o = data.GetAttributeValue(nameof(ApiClientGeneraAttribute.Attribute)); + // additionalAttribute = interfaceSymbol.GetAttributeInitInfo(ApiClientAttributeFullName, data!); // } var attchAttribute = "AutoWasmApiGenerator.Attributes.GeneratedByAutoWasmApiGeneratorAttribute"; (string, string?)[] attrParams = [ ("InterfaceType", $"typeof({interfaceSymbol.ToDisplayString()})"), - ("Part", "AutoWasmApiGenerator.Attributes.PartType.ApiInvoker") + ("Part", "AutoWasmApiGenerator.Attributes.PartType.ApiClient") ]; return ClassBuilder.Default - .ClassName($"{FormatClassName(interfaceSymbol.MetadataName)}ApiInvoker") - .AddGeneratedCodeAttribute(typeof(ApiInvokerGenerator)) + .ClassName($"{FormatClassName(interfaceSymbol.MetadataName)}ApiClient") + .AddGeneratedCodeAttribute(typeof(ApiClientGenerator)) .Attribute(attchAttribute, attrParams) // .Attribute([.. additionalAttribute.Select(i => i.ToString())]) .BaseType(interfaceSymbol.ToDisplayString()); diff --git a/src/AutoWasmApi.Roslyn/ControllerGenerator.cs b/src/AutoWasmApi.Roslyn/ControllerGenerator.cs index 526b47c..81353fe 100644 --- a/src/AutoWasmApi.Roslyn/ControllerGenerator.cs +++ b/src/AutoWasmApi.Roslyn/ControllerGenerator.cs @@ -6,6 +6,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading; +using AutoWasmApiGenerator.Models; using Generators.Shared; using Generators.Shared.Builder; using Microsoft.CodeAnalysis; @@ -21,21 +22,6 @@ public class ControllerGenerator : IIncrementalGenerator public void Initialize(IncrementalGeneratorInitializationContext context) { - //var ctx = context.SyntaxProvider.ForAttributeWithMetadataName( - // WebControllerAssemblyAttributeFullName, - // static (node, token) => true, - // static (c, t) => c); - //var items = context.SyntaxProvider.ForAttributeWithMetadataName( - // WebControllerAttributeFullName - // , static (node, _) => true - // , static (ctx, _) => ctx); -#if DEBUG && false - if (!Debugger.IsAttached) - { - Debugger.Launch(); // This will launch the debugger when the source generator runs. - } -#endif - context.RegisterSourceOutput(context.CompilationProvider, static (context, compilation) => { try @@ -44,17 +30,50 @@ public void Initialize(IncrementalGeneratorInitializationContext context) { return; } - + compilation.Assembly.GetAttribute(WebControllerAssemblyAttributeFullName, out var apiType); + object? mode = null; + apiType?.GetNamedValue("Mode", out mode); + var isController = mode is int m && m == 1; var all = compilation.GetAllSymbols(WebControllerAttributeFullName); + List endpoints = []; foreach (var item in all) { - if (!CreateCodeFile(item, context, out var file)) + CodeFile? file; + var (apiContext, error) = CollectApiContext(item); + if (error is not null) + { + context.ReportDiagnostic(error); continue; + } + if (isController) + { + file = CreateControllerCodeFile(apiContext); + } + else + { + file = CreateMinimalCodeFile(apiContext, out var ep); + endpoints.Add(ep); + } #if DEBUG var ss = file.ToString(); #endif context.AddSource(file); } + if (endpoints.Count > 0) + { + var allEndpoints = string.Join(", ", endpoints.Select(e => $"\"{e}\"")); + var np = NamespaceBuilder.Default.Namespace("AutoWasmApiGenerator").FileScoped(); + var c = ClassBuilder.Default.ClassName("AutoWasmApiEndPointsExtensions") + .Modifiers("public static") + .AddGeneratedCodeAttribute(typeof(ControllerGenerator)) + .AddMembers(MethodBuilder.Default.MethodName("MapAutoWasmApiEndPoints").Modifiers("public static") + .AddParameter("this WebApplication app") + .AddBody([.. endpoints.Select(e => $"{e}(app)")])); + var af = CodeFile.New("AutoWasmApiEndPointsExtensions") + .AddUsings("using Microsoft.AspNetCore.Builder;") + .AddMembers(np.AddMembers(c)); + context.AddSource(af); + } } catch (Exception ex) { @@ -62,7 +81,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) id: "ControllerGenerator_ERROR00001", title: "生成错误", messageFormat: ex.Message, - category: typeof(ApiInvokerGenerator).FullName!, + category: typeof(ApiClientGenerator).FullName!, defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true ), Location.None)); @@ -70,114 +89,418 @@ public void Initialize(IncrementalGeneratorInitializationContext context) }); } - private static bool CreateCodeFile(INamedTypeSymbol interfaceSymbol, SourceProductionContext context, - [NotNullWhen(true)] out CodeFile? file) + private static (ApiContext apiContext, Diagnostic? error) CollectApiContext(INamedTypeSymbol interfaceSymbol) { var methods = interfaceSymbol.GetAllMethodWithAttribute(WebMethodAttributeFullName).ToArray(); if (methods.Any(a => (!a.Symbol.HasAttribute(ApiNotSupported) && !a.Symbol.HasAttribute(ControllerNotSupported)) && (a.Symbol.IsGenericMethod) || interfaceSymbol.IsGenericType)) { - file = null; - context.ReportDiagnostic(DiagnosticDefinitions.WAG00004(interfaceSymbol.Locations.FirstOrDefault())); - return false; + var dia = DiagnosticDefinitions.WAG00004(interfaceSymbol.Locations.FirstOrDefault()); + return (null!, dia); } - - var ns = NamespaceBuilder.Default.Namespace(interfaceSymbol.ContainingNamespace.ToDisplayString()); - var controllerClass = CreateControllerClass(interfaceSymbol); - List members = []; - var localField = BuildLocalField(interfaceSymbol); - var constructor = BuildConstructor(interfaceSymbol); - members.Add(localField); - members.Add(constructor); _ = interfaceSymbol.GetAttribute(WebControllerAttributeFullName, out var attributeData); - var needAuth = attributeData.GetNamedValue("Authorize") ?? false; - var authScheme = attributeData.GetNamedValue("AuthorizationScheme")?.ToString(); - foreach (var methodSymbol in methods) + var needAuth = (bool)(attributeData.GetNamedValue("Authorize") ?? false); + var authScheme = attributeData.GetNamedValue("Scheme")?.ToString(); + var route = attributeData.GetNamedValue("Route")?.ToString(); + var authorize = new AuthorizeInfo() + { + RequiredAuthorize = needAuth, + AuthorizeScheme = authScheme, + }; + List apiMethods = []; + foreach (var (symbol, attrData) in methods) { - if (methodSymbol.Symbol.HasAttribute(ControllerNotSupported)) + if (symbol.HasAttribute(ControllerNotSupported)) { continue; } - var httpMethod = TryGetHttpMethod(methodSymbol); - var methodSyntax = BuildMethod(methodSymbol, httpMethod, (bool)needAuth, authScheme); - members.Add(methodSyntax); + var httpMethod = TryGetHttpMethod(attrData); + var methodScoped = symbol.Name.Replace("Async", ""); + var customRoute = attrData?.GetNamedValue("Route")?.ToString(); + string methodRoute; + if (string.IsNullOrEmpty(customRoute)) + { + methodRoute = methodScoped; + } + else if (Regex.Match(customRoute, "{.+}").Success) + { + methodRoute = $"{methodScoped}/{customRoute}"; + } + else + { + methodRoute = customRoute!; + } + var allowAnonymous = (bool)(attrData?.GetNamedValue("AllowAnonymous") ?? false); + var methodAuth = (bool)(attrData?.GetNamedValue("Authorize") ?? false); + var ami = new ApiMethodInfo(symbol) + { + RouteUrl = methodRoute, + HttpMethod = httpMethod, + MethodAuthorize = new() + { + AllowAnonymous = allowAnonymous, + RequiredAuthorize = methodAuth + } + }; + apiMethods.Add(ami); } - file = CodeFile.New($"{interfaceSymbol.FormatFileName()}Controller.g.cs") - .AddMembers(ns.AddMembers(controllerClass.AddMembers([.. members]))); - //.AddUsings(source.GetTargetUsings()); - - return true; + return (new(interfaceSymbol) + { + RouteUrl = route, + AuthorizeInfo = authorize, + Methods = apiMethods + }, null); } - private static MethodBuilder BuildMethod((IMethodSymbol, AttributeData?) data, string httpMethod, bool needAuth, string? authScheme) + #region + //private static bool CreateControllerCodeFile(INamedTypeSymbol interfaceSymbol, SourceProductionContext context, + // [NotNullWhen(true)] out CodeFile? file) + //{ + // //var interfaceSymbol = context.InterfaceType; + // var methods = interfaceSymbol.GetAllMethodWithAttribute(WebMethodAttributeFullName).ToArray(); + // if (methods.Any(a => (!a.Symbol.HasAttribute(ApiNotSupported) && !a.Symbol.HasAttribute(ControllerNotSupported)) && (a.Symbol.IsGenericMethod) || interfaceSymbol.IsGenericType)) + // { + // file = null; + // context.ReportDiagnostic(DiagnosticDefinitions.WAG00004(interfaceSymbol.Locations.FirstOrDefault())); + // return false; + // } + + // var ns = NamespaceBuilder.Default.Namespace(interfaceSymbol.ContainingNamespace.ToDisplayString()); + // var controllerClass = CreateControllerClass(interfaceSymbol); + // List members = []; + // var localField = BuildLocalField(interfaceSymbol); + // var constructor = BuildConstructor(interfaceSymbol); + // members.Add(localField); + // members.Add(constructor); + // _ = interfaceSymbol.GetAttribute(WebControllerAttributeFullName, out var attributeData); + // var needAuth = attributeData.GetNamedValue("Authorize") ?? false; + // var authScheme = attributeData.GetNamedValue("AuthorizationScheme")?.ToString(); + // foreach (var methodSymbol in methods) + // { + // if (methodSymbol.Symbol.HasAttribute(ControllerNotSupported)) + // { + // continue; + // } + + // var httpMethod = TryGetHttpMethod(methodSymbol.AttrData); + // var methodSyntax = BuildMethod(methodSymbol, httpMethod, (bool)needAuth, authScheme); + // members.Add(methodSyntax); + // } + + // file = CodeFile.New($"{interfaceSymbol.FormatFileName()}Controller.g.cs") + // .AddMembers(ns.AddMembers(controllerClass.AddMembers([.. members]))); + // //.AddUsings(source.GetTargetUsings()); + + // return true; + + // static MethodBuilder BuildMethod((IMethodSymbol, AttributeData?) data, string httpMethod, bool needAuth, string? authScheme) + // { + // /* + // * [global::Microsoft.AspNetCore.Mvc.{httpMethod}("...")] + // * [global::System.CodeDom.Compiler.GeneratedCode("...", "...")] + // * public ( p) => _proxyService_gen.(p); + // */ + // var a = data.Item2; + // var methodSymbol = data.Item1; + // var methodScoped = methodSymbol.Name.Replace("Async", ""); + // var customRoute = a?.GetNamedValue("Route")?.ToString(); + // string methodRoute; + // if (string.IsNullOrEmpty(customRoute)) + // { + // methodRoute = methodScoped; + // } + // else if (Regex.Match(customRoute, "{.+}").Success) + // { + // methodRoute = $"{methodScoped}/{customRoute}"; + // } + // else + // { + // methodRoute = customRoute!; + // } + + // var methodRouteAttribute = + // $"global::Microsoft.AspNetCore.Mvc.Http{httpMethod}(\"{methodRoute}\")"; + // var allowAnonymous = (bool)(a?.GetNamedValue("AllowAnonymous") ?? false); + // var methodAuth = (bool)(a?.GetNamedValue("Authorize") ?? false); + // var authorizeAttribute = string.IsNullOrEmpty(authScheme) + // ? "global::Microsoft.AspNetCore.Authorization.Authorize" + // : $"""global::Microsoft.AspNetCore.Authorization.Authorize(AuthenticationSchemes = "{authScheme}")"""; + // var (IsTask, HasReturn, ReturnType) = methodSymbol.GetReturnTypeInfo(); + // if (HasReturn && ReturnType.IsTupleType) + // { + // var methodReturn = IsTask ? "global::System.Threading.Tasks.Task" : "object"; + // var methodReturnResult = $"var _return_gen = {(IsTask ? "await " : "")}{PROXY_INSTANCE}.{methodSymbol.Name}({string.Join(", ", methodSymbol.Parameters.Select(p => p.Name))})"; + // var tuple = (INamedTypeSymbol)ReturnType; + // StringBuilder obj = new(); + // ConvertToAnonymousObject(obj, tuple, "_return_gen"); + // var finalReturn = $"var _anonymous_gen = {obj};"; + // var finalJsonReturn = $"return global::System.Text.Json.JsonSerializer.Serialize(_anonymous_gen, {TUPLE_JSON_OPTION})"; + // return MethodBuilder.Default + // .MethodName(methodSymbol.Name) + // .Async(IsTask) + // .ReturnType(methodReturn) + // .Attribute(methodRouteAttribute) + // .AttributeIf(allowAnonymous, "global::Microsoft.AspNetCore.Authorization.AllowAnonymous") + // .AttributeIf((methodAuth || needAuth) && !allowAnonymous, authorizeAttribute) + // .AddGeneratedCodeAttribute(typeof(ControllerGenerator)) + // .AddParameter(GenerateParameter(httpMethod, methodSymbol)) + // .AddBody(methodReturnResult, finalReturn, finalJsonReturn); + // } + // else + // { + // return MethodBuilder.Default + // .MethodName(methodSymbol.Name) + // .ReturnType(methodSymbol.ReturnType.ToDisplayString()) + // .Attribute(methodRouteAttribute) + // .AttributeIf(allowAnonymous, "global::Microsoft.AspNetCore.Authorization.AllowAnonymous") + // .AttributeIf((methodAuth || needAuth) && !allowAnonymous, authorizeAttribute) + // .AddGeneratedCodeAttribute(typeof(ControllerGenerator)) + // .AddParameter(GenerateParameter(httpMethod, methodSymbol)) + // .Lambda( + // $"{PROXY_INSTANCE}.{methodSymbol.Name}({string.Join(", ", methodSymbol.Parameters.Select(p => p.Name))});"); + // } + // } + //} + + #endregion + + #region controller + + private static CodeFile CreateControllerCodeFile(ApiContext context) { - /* - * [global::Microsoft.AspNetCore.Mvc.{httpMethod}("...")] - * [global::System.CodeDom.Compiler.GeneratedCode("...", "...")] - * public ( p) => _proxyService_gen.(p); - */ - var a = data.Item2; - var methodSymbol = data.Item1; - var methodScoped = methodSymbol.Name.Replace("Async", ""); - var customRoute = a?.GetNamedValue("Route")?.ToString(); - string methodRoute; - if (string.IsNullOrEmpty(customRoute)) + var methods = context.Methods; + var ns = NamespaceBuilder.Default.Namespace(context.NameSpace); + var controllerClass = CreateControllerClass(context); + List members = []; + var localField = BuildLocalField(context); + var constructor = BuildConstructor(context); + members.Add(localField); + members.Add(constructor); + //var needAuth = context.AuthorizeInfo is not null; + //var authScheme = context.AuthorizeInfo?.AuthorizeScheme; + foreach (var ami in methods) { - methodRoute = methodScoped; + var methodSyntax = BuildMethod(context, ami); + members.Add(methodSyntax); } - else if (Regex.Match(customRoute, "{.+}").Success) + + var file = CodeFile.New($"{context.InterfaceType.FormatFileName()}Controller.g.cs") + .AddMembers(ns.AddMembers(controllerClass.AddMembers([.. members]))); + //.AddUsings(source.GetTargetUsings()); + + return file; + + static ClassBuilder CreateControllerClass(ApiContext apiContext) { - methodRoute = $"{methodScoped}/{customRoute}"; + var route = apiContext.RouteUrl ?? "[controller]"; + var needAuth = apiContext.AuthorizeInfo.RequiredAuthorize; + //var additionalAttribute = source.TargetSymbol.GetAttributeInitInfo(); + var attchAttribute = "AutoWasmApiGenerator.Attributes.GeneratedByAutoWasmApiGeneratorAttribute"; + (string, string?)[] attrParams = [ + ("InterfaceType", $"typeof({apiContext.TypeName})"), + ("Part", "AutoWasmApiGenerator.Attributes.PartType.Controller") + ]; + return ClassBuilder.Default + .ClassName($"{FormatClassName(apiContext.FileName)}Controller") + .Modifiers("public") + .BaseType("global::Microsoft.AspNetCore.Mvc.ControllerBase") + .Attribute("global::Microsoft.AspNetCore.Mvc.ApiController") + .Attribute($"global::Microsoft.AspNetCore.Mvc.Route(\"api/{route}\")") + .AttributeIf((bool)needAuth, "global::Microsoft.AspNetCore.Authorization.Authorize") + //.Attribute([..additionalAttribute.Select(i => i.ToString())]) + .AddGeneratedCodeAttribute(typeof(ControllerGenerator)) + .Attribute(attchAttribute, attrParams); } - else + + static FieldBuilder BuildLocalField(ApiContext apiContext) { - methodRoute = customRoute!; + // private readonly _proxyService_gen; + return FieldBuilder.Default + .MemberType(apiContext.TypeName) + .FieldName(PROXY_INSTANCE); } - var methodRouteAttribute = - $"global::Microsoft.AspNetCore.Mvc.Http{httpMethod}(\"{methodRoute}\")"; - var allowAnonymous = (bool)(a?.GetNamedValue("AllowAnonymous") ?? false); - var methodAuth = (bool)(a?.GetNamedValue("Authorize") ?? false); - var authorizeAttribute = string.IsNullOrEmpty(authScheme) - ? "global::Microsoft.AspNetCore.Authorization.Authorize" - : $"""global::Microsoft.AspNetCore.Authorization.Authorize(AuthenticationSchemes = "{authScheme}")"""; - var (IsTask, HasReturn, ReturnType) = methodSymbol.GetReturnTypeInfo(); - if (HasReturn && ReturnType.IsTupleType) + static ConstructorBuilder BuildConstructor(ApiContext apiContext) { - var methodReturn = IsTask ? "global::System.Threading.Tasks.Task" : "object"; - var methodReturnResult = $"var _return_gen = {(IsTask ? "await " : "")}{PROXY_INSTANCE}.{methodSymbol.Name}({string.Join(", ", methodSymbol.Parameters.Select(p => p.Name))})"; - var tuple = (INamedTypeSymbol)ReturnType; - StringBuilder obj = new(); - ConvertToAnonymousObject(obj, tuple, "_return_gen"); - var finalReturn = $"var _anonymous_gen = {obj};"; - var finalJsonReturn = $"return global::System.Text.Json.JsonSerializer.Serialize(_anonymous_gen, {TUPLE_JSON_OPTION})"; - return MethodBuilder.Default - .MethodName(methodSymbol.Name) - .Async(IsTask) - .ReturnType(methodReturn) - .Attribute(methodRouteAttribute) - .AttributeIf(allowAnonymous, "global::Microsoft.AspNetCore.Authorization.AllowAnonymous") - .AttributeIf((methodAuth || needAuth) && !allowAnonymous, authorizeAttribute) + /* + * public Controller( service) + * { + * _proxyService_gen = service; + * } + */ + + return ConstructorBuilder.Default + .MethodName($"{FormatClassName(apiContext.FileName)}Controller") .AddGeneratedCodeAttribute(typeof(ControllerGenerator)) - .AddParameter(GenerateParameter(httpMethod, methodSymbol)) - .AddBody(methodReturnResult, finalReturn, finalJsonReturn); + .AddBody($"{PROXY_INSTANCE} = service;") + .AddParameter($"{apiContext.TypeName} service"); } - else + + static MethodBuilder BuildMethod(ApiContext api, ApiMethodInfo method) { - return MethodBuilder.Default - .MethodName(methodSymbol.Name) - .ReturnType(methodSymbol.ReturnType.ToDisplayString()) - .Attribute(methodRouteAttribute) - .AttributeIf(allowAnonymous, "global::Microsoft.AspNetCore.Authorization.AllowAnonymous") - .AttributeIf((methodAuth || needAuth) && !allowAnonymous, authorizeAttribute) - .AddGeneratedCodeAttribute(typeof(ControllerGenerator)) - .AddParameter(GenerateParameter(httpMethod, methodSymbol)) - .Lambda( - $"{PROXY_INSTANCE}.{methodSymbol.Name}({string.Join(", ", methodSymbol.Parameters.Select(p => p.Name))});"); + /* + * [global::Microsoft.AspNetCore.Mvc.{httpMethod}("...")] + * [global::System.CodeDom.Compiler.GeneratedCode("...", "...")] + * public ( p) => _proxyService_gen.(p); + */ + string methodRoute = method.RouteUrl; + string httpMethod = method.HttpMethod; + var methodRouteAttribute = + $"global::Microsoft.AspNetCore.Mvc.Http{httpMethod}(\"{methodRoute}\")"; + var allowAnonymous = method.MethodAuthorize.AllowAnonymous; + var methodAuth = method.MethodAuthorize.RequiredAuthorize; + var apiAuth = api.AuthorizeInfo.RequiredAuthorize; + var apiAuthScheme = api.AuthorizeInfo.AuthorizeScheme; + var methodSymbol = method.Symbol; + var authorizeAttribute = string.IsNullOrEmpty(apiAuthScheme) + ? "global::Microsoft.AspNetCore.Authorization.Authorize" + : $"""global::Microsoft.AspNetCore.Authorization.Authorize(AuthenticationSchemes = "{apiAuthScheme}")"""; + var (IsTask, HasReturn, ReturnType) = methodSymbol.GetReturnTypeInfo(); + if (HasReturn && ReturnType.IsTupleType) + { + var methodReturn = IsTask ? "global::System.Threading.Tasks.Task" : "object"; + var methodReturnResult = $"var _return_gen = {(IsTask ? "await " : "")}{PROXY_INSTANCE}.{methodSymbol.Name}({string.Join(", ", methodSymbol.Parameters.Select(p => p.Name))})"; + var tuple = (INamedTypeSymbol)ReturnType; + StringBuilder obj = new(); + ConvertToAnonymousObject(obj, tuple, "_return_gen"); + var finalReturn = $"var _anonymous_gen = {obj};"; + var finalJsonReturn = $"return global::System.Text.Json.JsonSerializer.Serialize(_anonymous_gen, {TUPLE_JSON_OPTION})"; + return MethodBuilder.Default + .MethodName(methodSymbol.Name) + .Async(IsTask) + .ReturnType(methodReturn) + .Attribute(methodRouteAttribute) + .AttributeIf(allowAnonymous, "global::Microsoft.AspNetCore.Authorization.AllowAnonymous") + .AttributeIf((methodAuth || apiAuth) && !allowAnonymous, authorizeAttribute) + .AddGeneratedCodeAttribute(typeof(ControllerGenerator)) + .AddParameter(GenerateParameter(httpMethod, methodSymbol)) + .AddBody(methodReturnResult, finalReturn, finalJsonReturn); + } + else + { + return MethodBuilder.Default + .MethodName(methodSymbol.Name) + .ReturnType(methodSymbol.ReturnType.ToDisplayString()) + .Attribute(methodRouteAttribute) + .AttributeIf(allowAnonymous, "global::Microsoft.AspNetCore.Authorization.AllowAnonymous") + .AttributeIf((methodAuth || apiAuth) && !allowAnonymous, authorizeAttribute) + .AddGeneratedCodeAttribute(typeof(ControllerGenerator)) + .AddParameter(GenerateParameter(httpMethod, methodSymbol)) + .Lambda( + $"{PROXY_INSTANCE}.{methodSymbol.Name}({string.Join(", ", methodSymbol.Parameters.Select(p => p.Name))});"); + } } + } + #endregion + + private static CodeFile CreateMinimalCodeFile(ApiContext context, out string endPointsName) + { + var methods = context.Methods; + var ns = NamespaceBuilder.Default.Namespace(context.NameSpace); + var endPointClass = CreateEndPointExtensionClass(context) + .Modifiers("public static "); + List members = []; + var methodName = $"Map{context.FileName}EndPoints"; + endPointsName = $"global::{ns.Namespace}.{endPointClass.Name}.{methodName}"; + var extensionMethod = MethodBuilder.Default.MethodName(methodName) + .Modifiers("public static") + .AddParameter("this WebApplication app"); + var extensionMethodBody = new List + { + $"""var group = app.MapGroup("api/{context.RouteUrl ?? context.FileName}")""" + }; + if (context.AuthorizeInfo?.RequiredAuthorize == true) + { + extensionMethodBody.Add("group.RequireAuthorization();"); + } + members.Add(extensionMethod); + foreach (var ami in methods) + { + + var methodSyntax = BuildPointDelegate(context, ami); + members.Add(methodSyntax); + var allowAnonymous = ami.MethodAuthorize.AllowAnonymous; + var methodAuth = ami.MethodAuthorize.RequiredAuthorize; + var apiAuth = context.AuthorizeInfo?.RequiredAuthorize ?? false; + var auth = allowAnonymous ? ".AllowAnonymous()" : ((methodAuth || apiAuth) && !allowAnonymous ? ".RequireAuthorization()" : ""); + extensionMethodBody.Add($"""group.Map{ami.HttpMethod}("{ami.RouteUrl}", {methodSyntax.Name}){auth}"""); + } + extensionMethod.AddBody([.. extensionMethodBody]); + + var file = CodeFile.New($"{context.InterfaceType.FormatFileName()}EndPointsExtensions.g.cs") + .AddUsings("using Microsoft.AspNetCore.Builder;") + .AddMembers(ns.AddMembers(endPointClass.AddMembers([.. members]))); + //.AddUsings(source.GetTargetUsings()); + + return file; + + static ClassBuilder CreateEndPointExtensionClass(ApiContext apiContext) + { + //var additionalAttribute = source.TargetSymbol.GetAttributeInitInfo(); + return ClassBuilder.Default + .ClassName($"{FormatClassName(apiContext.FileName)}EndPointsExtensions") + .Modifiers("public") + .AddGeneratedCodeAttribute(typeof(ControllerGenerator)); + } + + static MethodBuilder BuildPointDelegate(ApiContext api, ApiMethodInfo method) + { + /* + * [global::Microsoft.AspNetCore.Mvc.{httpMethod}("...")] + * [global::System.CodeDom.Compiler.GeneratedCode("...", "...")] + * public ( p) => _proxyService_gen.(p); + */ + string methodRoute = method.RouteUrl; + string httpMethod = method.HttpMethod; + var allowAnonymous = method.MethodAuthorize.AllowAnonymous; + var methodAuth = method.MethodAuthorize.RequiredAuthorize; + var apiAuth = api.AuthorizeInfo.RequiredAuthorize; + var apiAuthScheme = api.AuthorizeInfo.AuthorizeScheme; + var methodSymbol = method.Symbol; + var authorizeAttribute = string.IsNullOrEmpty(apiAuthScheme) + ? "global::Microsoft.AspNetCore.Authorization.Authorize" + : $"""global::Microsoft.AspNetCore.Authorization.Authorize(AuthenticationSchemes = "{apiAuthScheme}")"""; + var (IsTask, HasReturn, ReturnType) = methodSymbol.GetReturnTypeInfo(); + if (HasReturn && ReturnType.IsTupleType) + { + var methodReturn = IsTask ? "global::System.Threading.Tasks.Task" : "object"; + var methodReturnResult = $"var _return_gen = {(IsTask ? "await " : "")}{PROXY_INSTANCE}.{methodSymbol.Name}({string.Join(", ", methodSymbol.Parameters.Select(p => p.Name))})"; + var tuple = (INamedTypeSymbol)ReturnType; + StringBuilder obj = new(); + ConvertToAnonymousObject(obj, tuple, "_return_gen"); + var finalReturn = $"var _anonymous_gen = {obj};"; + var finalJsonReturn = $"return global::System.Text.Json.JsonSerializer.Serialize(_anonymous_gen, {TUPLE_JSON_OPTION})"; + return MethodBuilder.Default + .Modifiers("private static") + .MethodName(methodSymbol.Name) + .Async(IsTask) + .ReturnType(methodReturn) + .AttributeIf(allowAnonymous, "global::Microsoft.AspNetCore.Authorization.AllowAnonymous") + .AttributeIf((methodAuth || apiAuth) && !allowAnonymous, authorizeAttribute) + .AddGeneratedCodeAttribute(typeof(ControllerGenerator)) + .AddParameter(GenerateParameter(httpMethod, methodSymbol)) + .AddParameter($"{api.TypeName} {PROXY_INSTANCE}") + .AddBody(methodReturnResult, finalReturn, finalJsonReturn); + } + else + { + return MethodBuilder.Default + .Modifiers("private static") + .MethodName(methodSymbol.Name) + .ReturnType(methodSymbol.ReturnType.ToDisplayString()) + .AttributeIf(allowAnonymous, "global::Microsoft.AspNetCore.Authorization.AllowAnonymous") + .AttributeIf((methodAuth || apiAuth) && !allowAnonymous, authorizeAttribute) + .AddGeneratedCodeAttribute(typeof(ControllerGenerator)) + .AddParameter(GenerateParameter(httpMethod, methodSymbol)) + .AddParameter($"{api.TypeName} {PROXY_INSTANCE}") + .Lambda( + $"{PROXY_INSTANCE}.{methodSymbol.Name}({string.Join(", ", methodSymbol.Parameters.Select(p => p.Name))});"); + } + } + } private static void ConvertToAnonymousObject(StringBuilder obj, INamedTypeSymbol tuple, string tupleObject) { obj.Append("new {"); @@ -232,7 +555,7 @@ private static string[] GenerateParameter(string httpMethod, IMethodSymbol metho var (bindingType, p) = b; return $"{CreateMethodParameterBinding(bindingType, httpMethod)}{p.Type.ToDisplayString()} {p.Name}"; }); - return enumerable.ToArray(); + return [.. enumerable]; static string CreateMethodParameterOriginAttribute(string method) { @@ -262,94 +585,15 @@ static string CreateMethodParameterBinding(int bindingType, string method) } } - private static FieldBuilder BuildLocalField(INamedTypeSymbol interfaceSymbol) + private static string TryGetHttpMethod(AttributeData? data) { - // private readonly _proxyService_gen; - return FieldBuilder.Default - .MemberType(interfaceSymbol.ToDisplayString()) - .FieldName(PROXY_INSTANCE); - } - - private static ConstructorBuilder BuildConstructor(INamedTypeSymbol interfaceSymbol) - { - /* - * public Controller( service) - * { - * _proxyService_gen = service; - * } - */ - - return ConstructorBuilder.Default - .MethodName($"{FormatClassName(interfaceSymbol.FormatClassName())}Controller") - .AddGeneratedCodeAttribute(typeof(ControllerGenerator)) - .AddBody($"{PROXY_INSTANCE} = service;") - .AddParameter($"{interfaceSymbol.ToDisplayString()} service"); - } - - private static ClassBuilder CreateControllerClass(INamedTypeSymbol interfaceSymbol) - { - _ = interfaceSymbol.GetAttribute(WebControllerAttributeFullName, out var controllerAttribute); - var route = controllerAttribute.GetNamedValue("Route") ?? "[controller]"; - var needAuth = controllerAttribute.GetNamedValue("Authorize") ?? false; - //var additionalAttribute = source.TargetSymbol.GetAttributeInitInfo(); - var attchAttribute = "AutoWasmApiGenerator.Attributes.GeneratedByAutoWasmApiGeneratorAttribute"; - (string, string?)[] attrParams = [ - ("InterfaceType", $"typeof({interfaceSymbol.ToDisplayString()})"), - ("Part", "AutoWasmApiGenerator.Attributes.PartType.Controller") - ]; - return ClassBuilder.Default - .ClassName($"{FormatClassName(interfaceSymbol.FormatClassName())}Controller") - .Modifiers("public") - .BaseType("global::Microsoft.AspNetCore.Mvc.ControllerBase") - .Attribute("global::Microsoft.AspNetCore.Mvc.ApiController") - .Attribute($"global::Microsoft.AspNetCore.Mvc.Route(\"api/{route}\")") - .AttributeIf((bool)needAuth, "global::Microsoft.AspNetCore.Authorization.Authorize") - //.Attribute([..additionalAttribute.Select(i => i.ToString())]) - .AddGeneratedCodeAttribute(typeof(ControllerGenerator)) - .Attribute(attchAttribute, attrParams); - } - - private static string TryGetHttpMethod((IMethodSymbol, AttributeData?) data) - { - if (data.Item2 != null) + if (data != null) { - if (data.Item2.GetNamedValue("Method", out var m)) + if (data.GetNamedValue("Method", out var m)) { return WebMethod[(int)m!]; } } - - //没有指定Method,就默认Post return WebMethod[1]; - - //var symbol = data.Item1; - ////if (symbol.Parameters.Any(p =>p.Type.isr)) - - //var name = symbol.Name; - //if (name.StartsWith("create", StringComparison.OrdinalIgnoreCase) - // || name.StartsWith("add", StringComparison.OrdinalIgnoreCase)) - //{ - // return WebMethod[1]; - //} - //else if (name.StartsWith("get", StringComparison.OrdinalIgnoreCase) - // || name.StartsWith("find", StringComparison.OrdinalIgnoreCase) - // || name.StartsWith("query", StringComparison.OrdinalIgnoreCase)) - //{ - // return WebMethod[0]; - //} - //else if (name.StartsWith("update", StringComparison.OrdinalIgnoreCase) - // || name.StartsWith("put", StringComparison.OrdinalIgnoreCase)) - //{ - // return WebMethod[2]; - //} - //else if (name.StartsWith("delete", StringComparison.OrdinalIgnoreCase) - // || name.StartsWith("remove", StringComparison.OrdinalIgnoreCase)) - //{ - // return WebMethod[3]; - //} - //else - //{ - // return WebMethod[1]; - //} } } \ No newline at end of file diff --git a/src/AutoWasmApi.Roslyn/DiagnosticDefinitions.cs b/src/AutoWasmApi.Roslyn/DiagnosticDefinitions.cs index 4ed3bb3..676294b 100644 --- a/src/AutoWasmApi.Roslyn/DiagnosticDefinitions.cs +++ b/src/AutoWasmApi.Roslyn/DiagnosticDefinitions.cs @@ -29,7 +29,7 @@ public static Diagnostic WAG00002(Location? location) => Diagnostic.Create(new D id: "WAG00002", title: "无法为该类型生成WebApi调用类,缺少接口", messageFormat: "无法为该类型生成WebApi调用类,缺少接口", - category: typeof(ApiInvokerGenerator).FullName!, + category: typeof(ApiClientGenerator).FullName!, defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true), location); @@ -42,7 +42,7 @@ public static Diagnostic WAG00003(Location? location) => Diagnostic.Create(new D id: "WAG00003", title: "方法参数过多", messageFormat: "方法参数过多", - category: typeof(ApiInvokerGenerator).FullName!, + category: typeof(ApiClientGenerator).FullName!, defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true), location); @@ -55,7 +55,7 @@ public static Diagnostic WAG00004(Location? location) => Diagnostic.Create(new D id: "WAG00004", title: "控制器(controller)不能包含泛型", messageFormat: "控制器(controller)不能包含泛型", - category: typeof(ApiInvokerGenerator).FullName!, + category: typeof(ApiClientGenerator).FullName!, defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true), location); @@ -68,7 +68,7 @@ public static Diagnostic WAG00005(Location? location) => Diagnostic.Create(new D id: "WAG00005", title: "仅支持异步方法", messageFormat: "仅支持异步方法", - category: typeof(ApiInvokerGenerator).FullName!, + category: typeof(ApiClientGenerator).FullName!, defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true), location); @@ -81,7 +81,7 @@ public static Diagnostic WAG00006(Location? location, string? symbolString = nul id: "WAG00006", title: "路由中未包含路由参数", messageFormat: $"路由中未包含路由参数({symbolString})", - category: typeof(ApiInvokerGenerator).FullName!, + category: typeof(ApiClientGenerator).FullName!, defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true), location); @@ -94,7 +94,7 @@ public static Diagnostic WAG00007(Location? location, string? symbolString = nul id: "WAG00007", title: "不能同时设置[FromBody]和[FromForm]", messageFormat: $"不能同时设置[FromBody]和[FromForm]({symbolString})", - category: typeof(ApiInvokerGenerator).FullName!, + category: typeof(ApiClientGenerator).FullName!, defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true), location); @@ -107,7 +107,7 @@ public static Diagnostic WAG00008(Location? location, string? symbolString = nul id: "WAG00008", title: "不能设置多个[FromBody]", messageFormat: $"不能设置多个[FromBody]({symbolString})", - category: typeof(ApiInvokerGenerator).FullName!, + category: typeof(ApiClientGenerator).FullName!, defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true), location); @@ -120,7 +120,7 @@ public static Diagnostic WAG00009(Location? location, string? symbolString = nul id: "WAG00009", title: "暂不支持的返回值类型", messageFormat: $"暂不支持的返回值类型({symbolString})", - category: typeof(ApiInvokerGenerator).FullName!, + category: typeof(ApiClientGenerator).FullName!, defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true), location); @@ -133,7 +133,7 @@ public static Diagnostic WAG00010(Location? location, string? message = null) => id: "WAG00010", title: "不支持的元组属性类型", messageFormat: $"不支持的元组属性类型({message})", - category: typeof(ApiInvokerGenerator).FullName!, + category: typeof(ApiClientGenerator).FullName!, defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true), location); } diff --git a/src/AutoWasmApi.Roslyn/GeneratorHelpers.cs b/src/AutoWasmApi.Roslyn/GeneratorHelpers.cs index 67cfa03..f510278 100644 --- a/src/AutoWasmApi.Roslyn/GeneratorHelpers.cs +++ b/src/AutoWasmApi.Roslyn/GeneratorHelpers.cs @@ -8,12 +8,12 @@ public static class GeneratorHelpers { public const string WebControllerAttributeFullName = "AutoWasmApiGenerator.WebControllerAttribute"; public const string WebControllerAssemblyAttributeFullName = "AutoWasmApiGenerator.WebControllerAssemblyAttribute"; - public const string ApiInvokerAttributeFullName = "AutoWasmApiGenerator.ApiInvokerGenerateAttribute"; - public const string ApiInvokerAssemblyAttributeFullName = "AutoWasmApiGenerator.ApiInvokerAssemblyAttribute"; + public const string ApiClientAttributeFullName = "AutoWasmApiGenerator.ApiClientGenerateAttribute"; + public const string ApiClientAssemblyAttributeFullName = "AutoWasmApiGenerator.ApiClientAssemblyAttribute"; public const string WebMethodAttributeFullName = "AutoWasmApiGenerator.WebMethodAttribute"; public const string DisableWebApiGenerator = "DisableWebApiGenerator"; - public const string DisableApiInvokerGenerator = "DisableApiInvokerGenerator"; - public const string ApiNotSupported = "AutoWasmApiGenerator.ApiInvokeNotSupportedAttribute"; + public const string DisableApiClientGenerator = "DisableApiClientGenerator"; + public const string ApiNotSupported = "AutoWasmApiGenerator.ApiClientNotSupportedAttribute"; public const string ControllerNotSupported = "AutoWasmApiGenerator.WebMethodNotSupportedAttribute"; public const string WebMethodParameterBindingAttribute = "AutoWasmApiGenerator.WebMethodParameterBindingAttribute"; diff --git a/src/AutoWasmApi.Roslyn/Models/ApiContext.cs b/src/AutoWasmApi.Roslyn/Models/ApiContext.cs new file mode 100644 index 0000000..d660d9b --- /dev/null +++ b/src/AutoWasmApi.Roslyn/Models/ApiContext.cs @@ -0,0 +1,35 @@ +using Generators.Shared; +using Microsoft.CodeAnalysis; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace AutoWasmApiGenerator.Models; + +internal class ApiContext(INamedTypeSymbol namedTypeSymbol) +{ + public string? RouteUrl { get; set; } + public string TypeName => InterfaceType.ToDisplayString(); + public string FileName => InterfaceType.FormatClassName(); + public string NameSpace => InterfaceType.ContainingNamespace.ToDisplayString(); + public INamedTypeSymbol InterfaceType { get; set; } = namedTypeSymbol; + public AuthorizeInfo AuthorizeInfo { get; set; } = new(); + + public List Methods { get; set; } = []; + +} + +internal class AuthorizeInfo +{ + public bool AllowAnonymous { get; set; } + public bool RequiredAuthorize { get; set; } + public string? AuthorizeScheme { get; set; } +} + +internal class ApiMethodInfo(IMethodSymbol symbol) +{ + public IMethodSymbol Symbol { get; set; } = symbol; + [NotNull] public string? RouteUrl { get; set; } + public AuthorizeInfo MethodAuthorize { get; set; } = new(); + [NotNull] public string? HttpMethod { get; set; } + +} \ No newline at end of file diff --git a/src/AutoWasmApiGenerator/Attributes/ApiInvokerAssemblyAttribute.cs b/src/AutoWasmApiGenerator/Attributes/ApiClientAssemblyAttribute.cs similarity index 95% rename from src/AutoWasmApiGenerator/Attributes/ApiInvokerAssemblyAttribute.cs rename to src/AutoWasmApiGenerator/Attributes/ApiClientAssemblyAttribute.cs index 176401f..447cfdc 100644 --- a/src/AutoWasmApiGenerator/Attributes/ApiInvokerAssemblyAttribute.cs +++ b/src/AutoWasmApiGenerator/Attributes/ApiClientAssemblyAttribute.cs @@ -6,7 +6,7 @@ namespace AutoWasmApiGenerator; /// 指定API调用类生成位置 /// [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] -public class ApiInvokerAssemblyAttribute : Attribute +public class ApiClientAssemblyAttribute : Attribute { /// /// 当接口调用发生异常时,需要返回一个有实际意义的返回值 diff --git a/src/AutoWasmApiGenerator/Attributes/ApiInvokeNotSupportedAttribute.cs b/src/AutoWasmApiGenerator/Attributes/ApiClientNotSupportedAttribute.cs similarity index 79% rename from src/AutoWasmApiGenerator/Attributes/ApiInvokeNotSupportedAttribute.cs rename to src/AutoWasmApiGenerator/Attributes/ApiClientNotSupportedAttribute.cs index 14d92b3..6dc0722 100644 --- a/src/AutoWasmApiGenerator/Attributes/ApiInvokeNotSupportedAttribute.cs +++ b/src/AutoWasmApiGenerator/Attributes/ApiClientNotSupportedAttribute.cs @@ -6,7 +6,7 @@ namespace AutoWasmApiGenerator /// 不生成接口调用的代码 /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] - public class ApiInvokeNotSupportedAttribute : Attribute + public class ApiClientNotSupportedAttribute : Attribute { } diff --git a/src/AutoWasmApiGenerator/Attributes/GeneratedByAutoWasmApiGeneratorAttribute.cs b/src/AutoWasmApiGenerator/Attributes/GeneratedByAutoWasmApiGeneratorAttribute.cs index 8ebb7d1..e785edb 100644 --- a/src/AutoWasmApiGenerator/Attributes/GeneratedByAutoWasmApiGeneratorAttribute.cs +++ b/src/AutoWasmApiGenerator/Attributes/GeneratedByAutoWasmApiGeneratorAttribute.cs @@ -16,7 +16,7 @@ public enum PartType /// /// 调用类 /// - ApiInvoker + ApiClient } /// diff --git a/src/AutoWasmApiGenerator/Attributes/WebControllerAssemblyAttribute.cs b/src/AutoWasmApiGenerator/Attributes/WebControllerAssemblyAttribute.cs index 37762e8..83a426c 100644 --- a/src/AutoWasmApiGenerator/Attributes/WebControllerAssemblyAttribute.cs +++ b/src/AutoWasmApiGenerator/Attributes/WebControllerAssemblyAttribute.cs @@ -2,11 +2,28 @@ namespace AutoWasmApiGenerator; +/// +/// 默认生成模式 +/// +public enum ApiMode +{ + /// + /// 生成Minimal API + /// + MinimalApi = 0, + /// + /// 生成Controller + /// + Controller = 1, +} /// /// 指定Controller生成位置 /// [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)] public class WebControllerAssemblyAttribute : Attribute { - + /// + /// Api生成模式 + /// + public ApiMode Mode { get; set; } } diff --git a/src/AutoWasmApiGenerator/Attributes/WebControllerAttribute.cs b/src/AutoWasmApiGenerator/Attributes/WebControllerAttribute.cs index 6266273..dd3f979 100644 --- a/src/AutoWasmApiGenerator/Attributes/WebControllerAttribute.cs +++ b/src/AutoWasmApiGenerator/Attributes/WebControllerAttribute.cs @@ -21,4 +21,19 @@ public class WebControllerAttribute : Attribute /// 是否需要授权才可以访问 /// public bool Authorize { get; set; } + + /// + /// 鉴权Scheme + /// + public string? Scheme { get; set; } + + /// + /// 鉴权Policy + /// + public string? Policy { get; set; } + + /// + /// 鉴权Roles + /// + public string? Roles { get; set; } } diff --git a/src/AutoWasmApiGenerator/Attributes/WebMethodAttribute.cs b/src/AutoWasmApiGenerator/Attributes/WebMethodAttribute.cs index 50ac70d..e9b432b 100644 --- a/src/AutoWasmApiGenerator/Attributes/WebMethodAttribute.cs +++ b/src/AutoWasmApiGenerator/Attributes/WebMethodAttribute.cs @@ -24,4 +24,19 @@ public class WebMethodAttribute : Attribute /// 是否需要授权 /// public bool Authorize { get; set; } + + /// + /// 鉴权Scheme + /// + public string? Scheme { get; set; } + + /// + /// 鉴权Policy + /// + public string? Policy { get; set; } + + /// + /// 鉴权Roles + /// + public string? Roles { get; set; } } diff --git a/src/AutoWasmApiGenerator/DefaultHttpClientHeaderHandler.cs b/src/AutoWasmApiGenerator/DefaultHttpClientHeaderHandler.cs deleted file mode 100644 index 900e62c..0000000 --- a/src/AutoWasmApiGenerator/DefaultHttpClientHeaderHandler.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Net.Http; -using System.Threading.Tasks; - -namespace AutoWasmApiGenerator; - -/// -/// 的空实现 -/// -[Obsolete] -public class DefaultHttpClientHeaderHandler : IHttpClientHeaderHandler -{ - private DefaultHttpClientHeaderHandler() - { - - } - private static readonly Lazy lazy = new(() => new DefaultHttpClientHeaderHandler()); - /// - /// 单例 - /// - public static IHttpClientHeaderHandler Default => lazy.Value; - /// - /// - /// - /// - /// - public Task SetRequestHeaderAsync(HttpRequestMessage request) - { - return Task.CompletedTask; - } -} diff --git a/src/AutoWasmApiGenerator/GeneratedApiInvokeDelegatingHandler.cs b/src/AutoWasmApiGenerator/GeneratedApiClientDelegatingHandler.cs similarity index 69% rename from src/AutoWasmApiGenerator/GeneratedApiInvokeDelegatingHandler.cs rename to src/AutoWasmApiGenerator/GeneratedApiClientDelegatingHandler.cs index ae1adbf..7cf511d 100644 --- a/src/AutoWasmApiGenerator/GeneratedApiInvokeDelegatingHandler.cs +++ b/src/AutoWasmApiGenerator/GeneratedApiClientDelegatingHandler.cs @@ -4,15 +4,15 @@ namespace AutoWasmApiGenerator; /// -/// 的空实现 +/// 的空实现 /// -public class GeneratedApiInvokeDelegatingHandler : IGeneratedApiInvokeDelegatingHandler +public class GeneratedApiClientDelegatingHandler : IGeneratedApiClientDelegatingHandler { - private static readonly Lazy lazy = new(() => new GeneratedApiInvokeDelegatingHandler()); + private static readonly Lazy lazy = new(() => new GeneratedApiClientDelegatingHandler()); /// /// 单例 /// - public static IGeneratedApiInvokeDelegatingHandler Default => lazy.Value; + public static IGeneratedApiClientDelegatingHandler Default => lazy.Value; /// /// /// diff --git a/src/AutoWasmApiGenerator/IGeneratedApiInvokeDelegatingHandler.cs b/src/AutoWasmApiGenerator/IGeneratedApiClientDelegatingHandler.cs similarity index 97% rename from src/AutoWasmApiGenerator/IGeneratedApiInvokeDelegatingHandler.cs rename to src/AutoWasmApiGenerator/IGeneratedApiClientDelegatingHandler.cs index 9b696c9..2076b45 100644 --- a/src/AutoWasmApiGenerator/IGeneratedApiInvokeDelegatingHandler.cs +++ b/src/AutoWasmApiGenerator/IGeneratedApiClientDelegatingHandler.cs @@ -7,7 +7,7 @@ namespace AutoWasmApiGenerator; /// /// API调用类的切面处理器 /// -public interface IGeneratedApiInvokeDelegatingHandler +public interface IGeneratedApiClientDelegatingHandler { /// /// 接口调用前 diff --git a/src/AutoWasmApiGenerator/ServiceCollectionExtensions.cs b/src/AutoWasmApiGenerator/ServiceCollectionExtensions.cs index 38a0fd6..08e2e2f 100644 --- a/src/AutoWasmApiGenerator/ServiceCollectionExtensions.cs +++ b/src/AutoWasmApiGenerator/ServiceCollectionExtensions.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; @@ -37,7 +38,7 @@ public static IServiceCollection AddAutoWasmErrorResultHandler(this IServiceColl #if NET8_0_OR_GREATER [RequiresUnreferencedCode("Uses reflection to load types and attributes.")] #endif - public static IServiceCollection AddGeneratedApiInvokerServices(this IServiceCollection services + public static IServiceCollection AddGeneratedApiClientServices(this IServiceCollection services , Func? overrideLifetime = null) { var entry = Assembly.GetEntryAssembly(); @@ -59,6 +60,7 @@ public static IServiceCollection AddGeneratedApiInvokerServices(this IServiceCol var interfaceType = attributeData.NamedArguments.FirstOrDefault(arg => arg.MemberName == nameof(GeneratedByAutoWasmApiGeneratorAttribute.InterfaceType)).TypedValue.Value as Type; var lifetime = overrideLifetime?.Invoke(interfaceType!) ?? ServiceLifetime.Scoped; services.Add(new ServiceDescriptor(interfaceType!, type, lifetime)); + Debug.WriteLine($"注册{interfaceType?.FullName} -> {type.FullName}"); } } diff --git a/src/AutoWasmApiGenerator/Versions.props b/src/AutoWasmApiGenerator/Versions.props index 61bacc7..8f8fc35 100644 --- a/src/AutoWasmApiGenerator/Versions.props +++ b/src/AutoWasmApiGenerator/Versions.props @@ -1,6 +1,6 @@  - 2026.02.12.1 + 2026.05.15.1 $(BuildVersion) $(BuildVersion) diff --git a/src/Blazor.Test/Blazor.Test.Client/Aop/HttpExceptionHandler.cs b/src/Blazor.Test/Blazor.Test.Client/Aop/HttpExceptionHandler.cs new file mode 100644 index 0000000..100b637 --- /dev/null +++ b/src/Blazor.Test/Blazor.Test.Client/Aop/HttpExceptionHandler.cs @@ -0,0 +1,25 @@ +using AutoWasmApiGenerator; +using Microsoft.JSInterop; + +namespace Blazor.Test.Client.Aop +{ + public class HttpExceptionHandler(IJSRuntime js) : GeneratedApiClientDelegatingHandler + { + public override async Task OnExceptionAsync(ExceptionContext context) + { + try + { + await js.InvokeVoidAsync("alert", context.Exception.Message); + } + catch (JSException) + { + + } + finally + { + context.Handled = true; + } + + } + } +} diff --git a/src/Blazor.Test/Blazor.Test.Client/Pages/Counter.razor b/src/Blazor.Test/Blazor.Test.Client/Pages/Counter.razor index ce0fcf6..72f3755 100644 --- a/src/Blazor.Test/Blazor.Test.Client/Pages/Counter.razor +++ b/src/Blazor.Test/Blazor.Test.Client/Pages/Counter.razor @@ -54,7 +54,7 @@ SayHello Response: @response currentCount++; name = $"Marvel{currentCount}"; var responseTest = await Service.TestQueryParameter(name); - response = responseTest.ToString(); + response = responseTest?.ToString(); StateHasChanged(); } @@ -63,7 +63,7 @@ SayHello Response: @response currentCount++; name = $"Marvel{currentCount}"; var responseTest = await Service.TestQueryParameter2(name, currentCount); - response = responseTest.ToString(); + response = responseTest?.ToString(); StateHasChanged(); } @@ -72,7 +72,7 @@ SayHello Response: @response currentCount++; name = $"Marvel{currentCount}"; var responseTest = await Service.TestHeaderParameter(name); - response = responseTest.ToString(); + response = responseTest?.ToString(); StateHasChanged(); } @@ -81,7 +81,7 @@ SayHello Response: @response currentCount++; name = $"Marvel{currentCount}"; var responseTest = await Service.TestRouterParameter(name); - response = responseTest.ToString(); + response = responseTest?.ToString(); StateHasChanged(); } @@ -90,7 +90,7 @@ SayHello Response: @response currentCount++; name = $"Marvel{currentCount}"; var responseTest = await Service.TestFormParameter(name); - response = responseTest.ToString(); + response = responseTest?.ToString(); StateHasChanged(); } @@ -99,7 +99,7 @@ SayHello Response: @response currentCount++; name = $"Marvel{currentCount}"; var responseTest = await Service.TestMultiParameter(currentCount, name); - response = responseTest.ToString(); + response = responseTest?.ToString(); StateHasChanged(); } diff --git a/src/Blazor.Test/Blazor.Test.Client/Program.cs b/src/Blazor.Test/Blazor.Test.Client/Program.cs index 95574fd..5ffc587 100644 --- a/src/Blazor.Test/Blazor.Test.Client/Program.cs +++ b/src/Blazor.Test/Blazor.Test.Client/Program.cs @@ -1,20 +1,21 @@ -using Blazor.Test.Client.Services; -using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using AutoWasmApiGenerator; using Blazor.Test.Client.Models; using AutoPageStateContainerGenerator; -[assembly:AutoWasmApiGenerator.ApiInvokerAssembly] +using Blazor.Test.Client.Aop; +[assembly: ApiClientAssembly] var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.Services.ConfigureHttpClientDefaults(c => { c.ConfigureHttpClient(h => { h.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress); }); }); -builder.Services.AddScoped(); - +//builder.Services.AddScoped(); +//builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddStateContainers(); //builder.Services.AddGeneratedContainerServices(); -builder.Services.AddGeneratedApiInvokerServices(); +builder.Services.AddGeneratedApiClientServices(); builder.Services.AddAutoWasmErrorResultHandler(config => { config.CreateErrorResult(context => diff --git a/src/Blazor.Test/Blazor.Test.Client/Services/IHelloService.cs b/src/Blazor.Test/Blazor.Test.Client/Services/IHelloService.cs index 5006959..af0c6b3 100644 --- a/src/Blazor.Test/Blazor.Test.Client/Services/IHelloService.cs +++ b/src/Blazor.Test/Blazor.Test.Client/Services/IHelloService.cs @@ -24,15 +24,15 @@ public interface IHelloService Task TestReturnQueryResult(string name); [WebMethod(Method = WebMethod.Get)] - //[ApiInvokeNotSupported] + Task<(bool Success, string Message, (string Prop, int Value) Info)> TestReturnTuple(string name); [WebMethod(Method = WebMethod.Get)] - [ApiInvokeNotSupported] + [ApiClientNotSupported] void TestReturnVoid(); - //[WebMethod(Route = "rp")] - Task TestRouterParameter(string test); + [WebMethod(Route = "{test}", Authorize = true)] + Task TestRouterParameter([WebMethodParameterBinding(BindingType.FromRoute)] string test); Task TestFormParameter([WebMethodParameterBinding(BindingType.FromForm)] string name); [WebMethod(Route = "{id}")] diff --git a/src/Blazor.Test/Blazor.Test/Class1.cs b/src/Blazor.Test/Blazor.Test/Class1.cs index fe0a5b5..b445168 100644 --- a/src/Blazor.Test/Blazor.Test/Class1.cs +++ b/src/Blazor.Test/Blazor.Test/Class1.cs @@ -11,7 +11,7 @@ namespace Blazor.Test [WebController(Authorize = true)] public interface ITest { - [ApiInvokeNotSupported] + [ApiClientNotSupported] void Log(string message); [WebMethod(Method = WebMethod.Get)] Task LogAsync(string message); diff --git a/src/Blazor.Test/Blazor.Test/Components/App.razor b/src/Blazor.Test/Blazor.Test/Components/App.razor index 0a3e6d3..698deac 100644 --- a/src/Blazor.Test/Blazor.Test/Components/App.razor +++ b/src/Blazor.Test/Blazor.Test/Components/App.razor @@ -9,11 +9,11 @@ - + - + diff --git a/src/Blazor.Test/Blazor.Test/Controllers/Account.cs b/src/Blazor.Test/Blazor.Test/Controllers/Account.cs index 0802a95..e5b7bdb 100644 --- a/src/Blazor.Test/Blazor.Test/Controllers/Account.cs +++ b/src/Blazor.Test/Blazor.Test/Controllers/Account.cs @@ -1,5 +1,8 @@ -using Microsoft.AspNetCore.Authentication; +using Blazor.Test.Client.Services; +using InjectTest; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Security.Claims; using System.Text.Json; @@ -8,6 +11,7 @@ namespace Blazor.Test.Controllers; [Route("[controller]")] +[Authorize] public class AccountController : ControllerBase { [HttpGet("[action]")] diff --git a/src/Blazor.Test/Blazor.Test/FileName.cs b/src/Blazor.Test/Blazor.Test/FileName.cs new file mode 100644 index 0000000..6dcdd55 --- /dev/null +++ b/src/Blazor.Test/Blazor.Test/FileName.cs @@ -0,0 +1,90 @@ +using InjectTest; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Mvc; +using System.Security.Claims; +using System.Text.Json; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Blazor.Test.Client.Services; + +public static class HelloServiceEndPoints +{ + //[global::System.CodeDom.Compiler.GeneratedCode("AutoWasmApiGenerator.ControllerGenerator", "2026.2.12.1")] + //public static void MapHelloServiceEndPoints(this WebApplication app) + //{ + // var group = app.MapGroup("api/hello/test"); + // group.MapPost("SayHello", ([global::Microsoft.AspNetCore.Mvc.FromQuery] string name, Blazor.Test.Client.Services.IHelloService service) => service.SayHelloAsync(name)); + + // group.MapGet("TestReturnTuple", TestReturnTuple); + + //} + + //public static async global::System.Threading.Tasks.Task TestReturnTuple([global::Microsoft.AspNetCore.Mvc.FromQuery] string name, IHelloService service) + //{ + // var _return_gen = await service.TestReturnTuple(name); + // var _anonymous_gen = new { Success = _return_gen.Success, Message = _return_gen.Message, Info = new { Prop = _return_gen.Info.Prop, Value = _return_gen.Info.Value, } }; + // return global::System.Text.Json.JsonSerializer.Serialize(_anonymous_gen, AutoWasmApiGenerator.AutoWasmApiGeneratorJsonHelper.TupleOption); + //} + + //[global::Microsoft.AspNetCore.Mvc.HttpPost("TestHeaderParameter")] + //[global::System.CodeDom.Compiler.GeneratedCode("AutoWasmApiGenerator.ControllerGenerator", "2026.2.12.1")] + //public System.Threading.Tasks.Task TestHeaderParameter([global::Microsoft.AspNetCore.Mvc.FromHeader] string name) + // => _proxyService_gen.TestHeaderParameter(name); + + //[global::Microsoft.AspNetCore.Mvc.HttpPost("TestQueryParameter")] + //[global::System.CodeDom.Compiler.GeneratedCode("AutoWasmApiGenerator.ControllerGenerator", "2026.2.12.1")] + //public System.Threading.Tasks.Task TestQueryParameter([global::Microsoft.AspNetCore.Mvc.FromQuery] string name) + // => _proxyService_gen.TestQueryParameter(name); + + //[global::Microsoft.AspNetCore.Mvc.HttpPost("TestQueryParameter2")] + //[global::System.CodeDom.Compiler.GeneratedCode("AutoWasmApiGenerator.ControllerGenerator", "2026.2.12.1")] + //public System.Threading.Tasks.Task TestQueryParameter2([global::Microsoft.AspNetCore.Mvc.FromQuery] string name, [global::Microsoft.AspNetCore.Mvc.FromQuery] int age) + // => _proxyService_gen.TestQueryParameter2(name, age); + + //[global::Microsoft.AspNetCore.Mvc.HttpPost("TestReturnQueryResultInt")] + //[global::System.CodeDom.Compiler.GeneratedCode("AutoWasmApiGenerator.ControllerGenerator", "2026.2.12.1")] + //public System.Threading.Tasks.Task> TestReturnQueryResultInt([global::Microsoft.AspNetCore.Mvc.FromQuery] string name) + // => _proxyService_gen.TestReturnQueryResultInt(name); + + //[global::Microsoft.AspNetCore.Mvc.HttpPost("TestReturnQueryResult")] + //[global::System.CodeDom.Compiler.GeneratedCode("AutoWasmApiGenerator.ControllerGenerator", "2026.2.12.1")] + //public System.Threading.Tasks.Task TestReturnQueryResult([global::Microsoft.AspNetCore.Mvc.FromQuery] string name) + // => _proxyService_gen.TestReturnQueryResult(name); + + //[global::Microsoft.AspNetCore.Mvc.HttpGet("TestReturnTuple")] + //[global::System.CodeDom.Compiler.GeneratedCode("AutoWasmApiGenerator.ControllerGenerator", "2026.2.12.1")] + //public async global::System.Threading.Tasks.Task TestReturnTuple([global::Microsoft.AspNetCore.Mvc.FromQuery] string name) + //{ + // var _return_gen = await _proxyService_gen.TestReturnTuple(name); + // var _anonymous_gen = new { Success = _return_gen.Success, Message = _return_gen.Message, Info = new { Prop = _return_gen.Info.Prop, Value = _return_gen.Info.Value, } }; + // return global::System.Text.Json.JsonSerializer.Serialize(_anonymous_gen, AutoWasmApiGenerator.AutoWasmApiGeneratorJsonHelper.TupleOption); + //} + + //[global::Microsoft.AspNetCore.Mvc.HttpGet("TestReturnVoid")] + //[global::System.CodeDom.Compiler.GeneratedCode("AutoWasmApiGenerator.ControllerGenerator", "2026.2.12.1")] + //public void TestReturnVoid() + // => _proxyService_gen.TestReturnVoid(); + + //[global::Microsoft.AspNetCore.Mvc.HttpPost("TestRouterParameter")] + //[global::System.CodeDom.Compiler.GeneratedCode("AutoWasmApiGenerator.ControllerGenerator", "2026.2.12.1")] + //public System.Threading.Tasks.Task TestRouterParameter([global::Microsoft.AspNetCore.Mvc.FromQuery] string test) + // => _proxyService_gen.TestRouterParameter(test); + + //[global::Microsoft.AspNetCore.Mvc.HttpPost("TestFormParameter")] + //[global::System.CodeDom.Compiler.GeneratedCode("AutoWasmApiGenerator.ControllerGenerator", "2026.2.12.1")] + //public System.Threading.Tasks.Task TestFormParameter([global::Microsoft.AspNetCore.Mvc.FromForm] string name) + // => _proxyService_gen.TestFormParameter(name); + + //[global::Microsoft.AspNetCore.Mvc.HttpPost("TestMultiParameter/{id}")] + //[global::System.CodeDom.Compiler.GeneratedCode("AutoWasmApiGenerator.ControllerGenerator", "2026.2.12.1")] + //public System.Threading.Tasks.Task TestMultiParameter([global::Microsoft.AspNetCore.Mvc.FromRoute] int id, [global::Microsoft.AspNetCore.Mvc.FromQuery] string name) + // => _proxyService_gen.TestMultiParameter(id, name); + + //[global::Microsoft.AspNetCore.Mvc.HttpPost("TestQueryAndBodyParameter")] + //[global::System.CodeDom.Compiler.GeneratedCode("AutoWasmApiGenerator.ControllerGenerator", "2026.2.12.1")] + //public System.Threading.Tasks.Task TestQueryAndBodyParameter([global::Microsoft.AspNetCore.Mvc.FromQuery] int id, [global::Microsoft.AspNetCore.Mvc.FromBody] Blazor.Test.Client.Services.RequestTest body) + // => _proxyService_gen.TestQueryAndBodyParameter(id, body); + +} \ No newline at end of file diff --git a/src/Blazor.Test/Blazor.Test/Program.cs b/src/Blazor.Test/Blazor.Test/Program.cs index 43fbc39..1ce4beb 100644 --- a/src/Blazor.Test/Blazor.Test/Program.cs +++ b/src/Blazor.Test/Blazor.Test/Program.cs @@ -7,8 +7,9 @@ using AutoPageStateContainerGenerator; using AutoGenMapperGenerator.ReflectMapper; using Blazor.Test.Client.Models; - -[assembly: AutoWasmApiGenerator.WebControllerAssembly] +using AutoWasmApiGenerator; +//[assembly: WebControllerAssembly(Mode = ApiMode.Controller)] +[assembly:WebControllerAssembly] var builder = WebApplication.CreateBuilder(args); // Add services to the container. @@ -21,7 +22,6 @@ builder.Services.AddScoped(); builder.Host.UseServiceProviderFactory(new AutoAopProxyGenerator.AutoAopProxyServiceProviderFactory()); builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(); -builder.Services.AddControllers(); builder.Services.Inject(); builder.Services.InjectHybrid(); builder.Services.AddHttpClient(); @@ -35,7 +35,6 @@ }); //builder.Services.(); var app = builder.Build(); - // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { @@ -53,5 +52,7 @@ .AddInteractiveServerRenderMode() .AddInteractiveWebAssemblyRenderMode() .AddAdditionalAssemblies(typeof(Blazor.Test.Client._Imports).Assembly); -app.MapControllers(); + +app.MapAutoWasmApiEndPoints(); + app.Run(); \ No newline at end of file diff --git a/src/Blazor.Test/Blazor.Test/Properties/launchSettings.json b/src/Blazor.Test/Blazor.Test/Properties/launchSettings.json index 8def060..62274d5 100644 --- a/src/Blazor.Test/Blazor.Test/Properties/launchSettings.json +++ b/src/Blazor.Test/Blazor.Test/Properties/launchSettings.json @@ -1,13 +1,5 @@ -{ +{ "$schema": "http://json.schemastore.org/launchsettings.json", - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:59873", - "sslPort": 0 - } - }, "profiles": { "http": { "commandName": "Project", @@ -18,14 +10,6 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } - }, - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } } } }