GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。
——出自 https://graphql.cn
数据是有关系的,可以利用HotChocolate.Stitching功能,把业务逻辑相关联的实体数据,从后台的两个服务中关联起来,这有点api网关的组合功能,可以把前端的一次请求,分解成后端的多次请求,并把数据组合后返回回去。
下面有这样一个例子:有个学生服务,有两个api,一个是查询全部学生,一个是按学号查询学生;另一个是成绩服务,有两个api,一个是按学号查询这个学生的全部成绩,一个是按id查询成绩。现在可以在学生实体中组合成绩,也可以在成绩实体中组合学生,这是因为学生和成绩是一个一对多的关系。
下面来看实例:
学生服务:GraphQLDemo03_Students
引入NuGet
HotChocolate.AspNetCore
HotChocolate.Data
Starup.cs
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace GraphQLDemo03_Students { public class Startup { public void ConfigureServices(IServiceCollection services) { services .AddSingleton<IStudentRepository, StudentRepository>() .AddGraphQLServer() .AddQueryType<Query>() ; } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGraphQL(); }); } } }
Query.cs
using HotChocolate; using System.Collections.Generic; namespace GraphQLDemo03_Students { public class Query { public IEnumerable<Student> GetStudents([Service] IStudentRepository studentRepository) { return studentRepository.GetStudents(); } public Student GetStudent(string stuNo, [Service] IStudentRepository studentRepository) { return studentRepository.GetStudent(stuNo); } } }
StudentRepository.cs
using System.Collections.Generic; using System.Linq; namespace GraphQLDemo03_Students { public interface IStudentRepository { IEnumerable<Student> GetStudents(); Student GetStudent(string stuNo); } public class StudentRepository : IStudentRepository { public IEnumerable<Student> GetStudents() { var students = new List<Student>() { new Student("S0001","小张",20,true), new Student("S0002","小李",19,false), }; return students; } public Student GetStudent(string stuNo) { var students = new List<Student>() { new Student("S0001","小张",20,true), new Student("S0002","小李",19,false), }; return students.SingleOrDefault(s => s.StuNo == stuNo); } } public record Student(string StuNo, string Name, int Age, bool Sex); }
成绩服务:GraphQLDemo03_Grades
引入NuGet包
HotChocolate.AspNetCore
HotChocolate.Data
Startup.cs
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace GraphQLDemo03_Grades { public class Startup { public void ConfigureServices(IServiceCollection services) { services .AddSingleton<IGradeRepository, GradeRepository>() .AddGraphQLServer() .AddQueryType<Query>() ; } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGraphQL(); }); } } }
Query.cs
using HotChocolate; using System.Collections.Generic; namespace GraphQLDemo03_Grades { public class Query { public IEnumerable<Grade> GetGrades([Service] IGradeRepository gradeRepository, string stuNo) { return gradeRepository.GetGrades(stuNo); } public Grade GetGrade([Service] IGradeRepository gradeRepository, int id) { return gradeRepository.GetGrade(id); } } }
GradeRepository.cs
using System.Collections.Generic; using System.Linq; namespace GraphQLDemo03_Grades { public interface IGradeRepository { IEnumerable<Grade> GetGrades(string stuNo); Grade GetGrade(int id); } public class GradeRepository : IGradeRepository { public IEnumerable<Grade> GetGrades(string stuNo) { var grades = new List<Grade>(){ new Grade(1,"S0001",100,"语文"), new Grade(2,"S0001",99,"数学"), new Grade(3,"S0002",98,"语文"), new Grade(4,"S0002",97,"数学") }; return grades.Where(s => s.stuNo == stuNo); } public Grade GetGrade(int id) { var grades = new List<Grade>(){ new Grade(1,"S0001",100,"语文"), new Grade(2,"S0001",99,"数学"), new Grade(3,"S0002",98,"语文"), new Grade(4,"S0002",97,"数学") }; return grades.SingleOrDefault(s => s.ID == id); } } public record Grade(int ID, string stuNo, float score, string subject); }
最重要的是组合服务,即网关服务:GraphoQLDemo03_gateway
引入NuGet包
HotChocolate.AspNetCore
HotChocolate.Data
HotChocolate.Stitching
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using System; namespace GraphQLDemo03_gateway { public class Startup { const string Students = "students"; const string Grades = "grades"; public void ConfigureServices(IServiceCollection services) { services.AddHttpClient(Students, c => c.BaseAddress = new Uri("http://localhost:7000/graphql")); services.AddHttpClient(Grades, c => c.BaseAddress = new Uri("http://localhost:9000/graphql")); services .AddGraphQLServer() .AddRemoteSchema(Students, ignoreRootTypes: true) .AddRemoteSchema(Grades, ignoreRootTypes: true) .AddTypeExtensionsFromString("type Query { }") .AddTypeExtensionsFromFile("StudentStitching.graphql") .AddTypeExtensionsFromFile("GradeStitching.graphql") .AddTypeExtensionsFromFile("StudentExtendStitching.graphql") .AddTypeExtensionsFromFile("GradeExtendStitching.graphql") ; } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGraphQL(); }); } } }
学生的Query类型
extend type Query{ getonestudent(stuNo:String): Student @delegate(schema: "students", path:"student(stuNo:$arguments:stuNo)") getstudents: [Student] @delegate(schema: "students", path:"students") }
学生Student上扩展的属性grades集合
extend type Student{ grades: [Grade] @delegate(schema: "grades", path:"grades(stuNo: $fields:stuNo)") }
成绩的Query类型
extend type Query{ getGrades(stuNo:String): [Grade] @delegate(schema: "grades", path:"grades(stuNo:$arguments:stuNo)") getGrade(id:Int!): Grade @delegate(schema: "grades", path:"grade(id:$arguments:id)") }
成绩实体上扩展的学生student实体属性
extend type Grade{ student: Student @delegate(schema: "students", path:"student(stuNo: $fields:stuNo)") }
网关项目中通过AddHttpClient把子服务地址添加进来,再通过AddRemoteSchema把子项的架构引入进来,通过AddTypeExtensionsFromFile添加.graphql定义文件,实现类型的定义和拼接,使应用达到灵活性。
想要更快更方便的了解相关知识,可以关注微信公众号