从 .NET Core 应用程序同时提供 REST 和 GraphQL API

Posted

技术标签:

【中文标题】从 .NET Core 应用程序同时提供 REST 和 GraphQL API【英文标题】:Serve both REST and GraphQL APIs from .NET Core application 【发布时间】:2020-09-28 00:35:21 【问题描述】:

我有一个已经在为客户服务的 .NET Core REST API 服务器。

我能否通过添加 HotChocolate 库并定义查询来配置 API 服务器以支持 GraphQL?是否可以从我的 .NET Core 服务器同时提供 GraphQL 和 REST API?

【问题讨论】:

【参考方案1】:

是的,支持 REST API(控制器)和 GraphQL 完全没问题。

如果您不考虑库,处理 GraphQL 请求仅意味着处理传入的 POST 到 /graphql

如果需要,您可以编写处理这些 POST 的典型 ASP.NET Core 控制器。像 Hot Chocolate 这样的框架提供了像 .UseGraphQl() 这样的中间件,这使得配置更加方便,但从概念上讲,您可以将 .UseGraphQl() 视为添加一个仅处理 /graphql 路由的控制器。您的所有其他控制器将继续正常工作!

【讨论】:

【参考方案2】:

有一种方法可以让您使用 hotchocolate 和模式拼接自动同时启动和运行两个 API。

基本上,我遵循了 Ian webbington 提供的这个教程。 https://ian.bebbs.co.uk/posts/LessReSTMoreHotChocolate

Ian 使用其 API 中的 swagger 模式来创建一个 graphql 模式,如果我们考虑一下,这可以节省我们的时间。它很容易实现,但是您仍然需要编写代码来公开 graphql 端点。

这是我实现的,将我的所有 graphql 和其余 API 连接到一个 API 网关中。我正在分享我的自定义实现以在 hotchocolate (Graphql) 下运行 swagger 模式 (REST):

using System;
using HotChocolate;
using HotChocolate.AspNetCore;
using HotChocolate.AspNetCore.Playground;
using HotChocolate.AspNetCore.Voyager;
using HotChocolate.AspNetCore.Subscriptions;
using HotChocolate.Stitching;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using SmartGateway.Api.Filters;
using SmartGateway.Api.RestServices.SmartConfig;

namespace SmartGateway.Api.Extensions

    public static class GraphQlExtensions
    
        public static IServiceCollection AddGraphQlApi(this IServiceCollection services)
        
            services.AddHttpClient("smartauth", (sp, client) =>
            
                sp.SetUpContext(client); //GRAPHQL API
                client.BaseAddress = new Uri(AppSettings.SmartServices.SmartAuth.Endpoint);
            );
            services.AddHttpClient("smartlog", (sp, client) =>
            
                sp.SetUpContext(client); //GRAPHQL API
                client.BaseAddress = new Uri(AppSettings.SmartServices.SmartLog.Endpoint);
            );
            services.AddHttpClient("smartway", (sp, client) =>
            
                sp.SetUpContext(client); //GRAPHQL API
                client.BaseAddress = new Uri(AppSettings.SmartServices.SmartWay.Endpoint);
            );

            services.AddHttpClient<ISmartConfigSession, SmartConfigSession>((sp, client) =>
                
                    sp.SetUpContext(client); //REST API
                    client.BaseAddress = new Uri(AppSettings.SmartServices.SmartConfig.Endpoint);
                
            );

            services.AddDataLoaderRegistry();

            services.AddGraphQLSubscriptions();

            services.AddStitchedSchema(builder => builder
                .AddSchemaFromHttp("smartauth")
                .AddSchemaFromHttp("smartlog")
                .AddSchemaFromHttp("smartway")
                .AddSchema(new NameString("smartconfig"), SmartConfigSchema.Build())
                .AddSchemaConfiguration(c =>
                
                    c.RegisterExtendedScalarTypes();
                ));

            services.AddErrorFilter<GraphQLErrorFilter>();

            return services;
        

        public static IApplicationBuilder UseGraphQlApi(this IApplicationBuilder app)
        
            app.UseWebSockets();
            app.UseGraphQL("/graphql");
            app.UsePlayground(new PlaygroundOptions
            
                Path = "/ui/playground",
                QueryPath = "/graphql"
            );
            app.UseVoyager(new PathString("/graphql"), new PathString("/ui/voyager"));

            return app;
        
    

设置 HttpContext 扩展:

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

namespace SmartGateway.Api.Extensions

    public static class HttpContextExtensions
    
        public static void SetUpContext(this IServiceProvider servicesProvider, HttpClient httpClient)
        
            HttpContext context = servicesProvider.GetRequiredService<IHttpContextAccessor>().HttpContext;

            if (context?.Request?.Headers?.ContainsKey("Authorization") ?? false)
            
                httpClient.DefaultRequestHeaders.Authorization =
                    AuthenticationHeaderValue.Parse(context.Request.Headers["Authorization"].ToString());
            
        
    

您需要它来处理 HTTPClient 并将其传递给您的 swagger Sdk。

using System.Net.Http;

namespace SmartGateway.Api.RestServices.SmartConfig

    public interface ISmartConfigSession
    
        HttpClient GetHttpClient();
    

    public class SmartConfigSession : ISmartConfigSession
    
        private readonly HttpClient _httpClient;

        public SmartConfigSession(HttpClient httpClient)
        
            _httpClient = httpClient;
        

        public HttpClient GetHttpClient()
        
            return _httpClient;
        
    

这是我的 graphql 架构:

namespace SmartGateway.Api.RestServices.SmartConfig

    public static class SmartConfigSchema
    
        public static ISchema Build()
        
            return SchemaBuilder.New()
                .AddQueryType<SmartConfigQueries>()
                .AddMutationType<SmartConfigMutations>()
                .ModifyOptions(o => o.RemoveUnreachableTypes = true)
                .Create();
        
    

    public class SmartConfigMutations
    
        private readonly ISmartConfigClient _client;

        public SmartConfigMutations(ISmartConfigSession session)
        
            _client = new SmartConfigClient(AppSettings.SmartServices.SmartConfig.Endpoint, session.GetHttpClient());
        

        public UserConfigMutations UserConfig => new UserConfigMutations(_client);
    

最后,这是您发布端点的方式:

using System.Threading.Tasks;
using SmartConfig.Sdk;

namespace SmartGateway.Api.RestServices.SmartConfig.UserConfigOps

    public class UserConfigMutations
    
        private readonly ISmartConfigClient _client;

        public UserConfigMutations(ISmartConfigClient session)
        
            _client = session;
        

        public async Task<UserConfig> CreateUserConfig(CreateUserConfigCommand createUserConfigInput)
        
            var result = await _client.CreateUserConfigAsync(createUserConfigInput);
            return result.Response;
        

        public async Task<UserConfig> UpdateUserConfig(UpdateUserConfigCommand updateUserConfigInput)
        
            var result = await _client.UpdateUserConfigAsync(updateUserConfigInput);
            return result.Response;
        
    

更多关于热巧克力和模式拼接的文档在这里: https://hotchocolate.io/docs/stitching

【讨论】:

以上是关于从 .NET Core 应用程序同时提供 REST 和 GraphQL API的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 6框架揭秘实例演示[30]:利用路由开发REST API

依赖注入[6]: .NET Core DI编程体验

ASP.NET Core 项目的 Visual Studio 中的“REST API 客户端”选项?

CORS 错误:LinkedIn 身份验证; .NET Core 5 REST API

创建一个 .NET Core Rest API 应用程序以支持来自应用程序的不同数据库

csharp .NET Core中的Azure通知中心REST API [读取/删除通道的所有注册]