文章目录
前言
1.GraphQL的概念
GraphQL是一种用于API的查询语言和运行时环境。它是由Facebook于2012年开始内部使用,2015年对外宣布开源。GraphQL旨在解决RESTful API的一些限制,如需要多次请求才能获取完整数据、难以扩展、无法精确控制数据返回等。
GraphQL的核心理念是:客户端定义数据需要的结构及其关系,服务器返回与此结构相匹配的数据。因此,GraphQL非常适用于前后端分离的应用,客户端可以精确地控制数据返回,提高了API的可靠性和灵活性。
2.GraphQL的应用场景和使用过程
GraphQL具体应用场景如下:
-
前后端分离的应用
-
移动应用后端API
-
多语言(多平台)支持的API
-
大型API架构
GraphQL的详细使用过程如下:
-
定义Schema:Schema定义了所有可用的查询、关系和数据类型,可以理解为GraphQL的API文档。
-
定义Resolver:Resolver是用来实际获取数据的代码,根据Schema中的查询字段获取实际数据。
-
编写查询:根据需要编写查询,查询可以包含多个字段和参数,并且可以进行嵌套。
-
发送查询:使用GraphQL客户端发送查询请求,返回与查询匹配的数据。
-
处理错误:错误处理是GraphQL重要的一个方面,通过使用错误处理器和异常捕获来管理错误。
GraphQL是一种强大而灵活的API开发工具,通过其独特的查询语言和运行时环境,可以大大提高API的可用性和灵活性,是API开发的一种重要的选择。
3.RESTful API和GraphQL的比较
RESTful API和GraphQL都是用于构建Web API的工具,但它们有一些不同的特点。
- 数据获取方式
RESTful API是基于HTTP的协议,使用HTTP动词(GET、POST、PUT、DELETE等)来获取或修改数据。每个资源都有一个唯一的URI,每个动词都对应一个特定的操作。RESTful API的响应是基于请求中的URI和动词的,不能跨越这些边界。
GraphQL使用单个端点来获取和修改数据,使用查询语言来指定所需的数据。GraphQL允许客户端请求精确的数据,而不是只返回整个资源。
- 查询效率
RESTful API有一个缺点,就是当需要获取多个资源时,需要进行多个HTTP请求。这种“资源瀑布”的方式会导致多个请求的延迟和带宽消耗,限制了应用程序的性能。
GraphQL允许在一个请求中获取多个资源,并且可以指定所需的字段和关系,避免了不必要的数据传输,更加高效。
- 缓存和版本控制
RESTful API通常使用HTTP标准的缓存机制(例如,使用Last-Modified和ETag头),以减少带宽和服务器负载。RESTful API也支持版本控制,可以更好地管理API的变化。
GraphQL没有内置的缓存机制,但可以使用其他缓存机制(例如,HTTP缓存)来缓存GraphQL查询。版本控制通常由客户端管理,GraphQL会返回错误,告知客户端如何修正查询。
- 开发效率
RESTful API和GraphQL各自有其优势,RESTful API更容易理解和学习,而GraphQL更加灵活。
GraphQL在数据获取方面更加强大和灵活,能够更好地适应复杂的数据需求。然而,由于GraphQL的语法较为复杂,开发人员需要更多的学习和编写时间。并且,GraphQL还需要一个运行时,并且需要进行更严格的代码检查,使其更难以使用。
- API的可发现性
RESTful API的资源URI的结构能够传达出API的基本结构。每个资源都必须在API文档中进行描述。
GraphQL没有URI能够传达出API的结构,API的可发现性需要通过不同的方式进行实现。例如,开发人员需要提供一个文档来描述所有可用的查询和变异操作。
RESTful API和GraphQL各有优劣。选择哪种API取决于项目的需求。如果需要简单和可靠的API,RESTful API是一个不错的选择;如果需要快速、灵活、精确的数据获取方式,GraphQL则是一个更好的选择。
4.GraphQL语法
GraphQL是一种查询语言,用于API的编写和操作。以下是GraphQL的基本语法:
- 查询
使用关键字“query”来声明查询类型,后面跟上查询的字段和参数。例如:
query {
person(id: "1") {
name
age
}
}
- 变量
使用$符号来声明变量,可以在查询中重用变量。
例如:
query getPerson($id: ID!) {
person(id: $id) {
name
age
}
}
在执行时,需要提供变量的值。例如:
{
"id": "1"
}
- 片段
可以在查询中使用片段来重用查询部分。片段以“…”开头,后面跟上片段名称。
例如:
query {
person(id: "1") {
name
age
address {
...addressFields
}
}
}
fragment addressFields on Address {
street
city
state
}
- 操作名称
可以使用操作名称来标识查询,方便调试和追踪。操作名称在关键字“query”或“mutation”之后的花括号之间定义。
例如:
query getPerson($id: ID!) {
person(id: $id) {
name
age
}
}
- 变更
使用关键字“mutation”来声明变更操作。变更可以用来进行数据的修改和创建。
例如:
mutation {
createPerson(name: "Sam", age: 25) {
id
name
age
}
}
以上是GraphQL的基本语法,还有更多高级特性和语法详情可以参考GraphQL官方文档。
一、使用MVC控制器进行配置
1.UI展示
using GraphQL;
using Chat = GraphQL.Samples.Schemas.Chat;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddSingleton<Chat.IChat, Chat.Chat>();
builder.Services.AddGraphQL(b => b
.AddAutoSchema<Chat.Query>(s => s
.WithMutation<Chat.Mutation>()
.WithSubscription<Chat.Subscription>())
.AddSystemTextJson());
var app = builder.Build();
app.UseDeveloperExceptionPage();
app.UseWebSockets();
app.UseRouting();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
await app.RunAsync();
public class HomeController : Controller
{
private readonly IDocumentExecuter<ISchema> _executer;
private readonly IGraphQLTextSerializer _serializer;
public HomeController(IDocumentExecuter<ISchema> executer, IGraphQLTextSerializer serializer)
{
_executer = executer;
_serializer = serializer;
}
/************ Display the GraphiQL interface *************/
public IActionResult Index()
=> new GraphiQLActionResult(opts =>
{
opts.GraphQLEndPoint = "/Home/graphql";
opts.SubscriptionsEndPoint = "/Home/graphql";
});
/******* Sample using GraphQLExecutionActionResult, letting the middleware handle the request ********/
[HttpGet]
[HttpPost]
[ActionName("graphql")]
public IActionResult GraphQLAsync()
=> new GraphQLExecutionActionResult();
}
2.案例
using GraphQL;
using GraphQL.Server.Transports.AspNetCore;
using GraphQL.Server.Ui.GraphiQL;
using GraphQL.Transport;
using GraphQL.Types;
using GraphQL.Validation;
using Microsoft.AspNetCore.Mvc;
namespace ControllerSample.Controllers;
public class HomeController : Controller
{
private readonly IDocumentExecuter<ISchema> _executer;
private readonly IGraphQLTextSerializer _serializer;
public HomeController(IDocumentExecuter<ISchema> executer, IGraphQLTextSerializer serializer)
{
_executer = executer;
_serializer = serializer;
}
/************ Display the GraphiQL interface *************/
public IActionResult Index()
=> new GraphiQLActionResult(opts =>
{
opts.GraphQLEndPoint = "/Home/graphql";
opts.SubscriptionsEndPoint = "/Home/graphql";
});
/******* Sample using GraphQLExecutionActionResult, letting the middleware handle the request ********/
[HttpGet]
[HttpPost]
[ActionName("graphql")]
public IActionResult GraphQLAsync()
=> new GraphQLExecutionActionResult();
/******* Sample with custom logic only using ExecutionResultActionResult to return the result ********/
[HttpGet]
[ActionName("graphql2")]
public Task<IActionResult> GraphQL2GetAsync(string query, string? operationName)
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
return Task.FromResult<IActionResult>(BadRequest());
}
else
{
return ExecuteGraphQLRequestAsync(BuildRequest(query, operationName));
}
}
[HttpPost]
[ActionName("graphql2")]
public async Task<IActionResult> GraphQL2PostAsync()
{
if (HttpContext.Request.HasFormContentType)
{
var form = await HttpContext.Request.ReadFormAsync(HttpContext.RequestAborted);
return await ExecuteGraphQLRequestAsync(BuildRequest(form["query"].ToString(), form["operationName"].ToString(), form["variables"].ToString(), form["extensions"].ToString()));
}
else if (HttpContext.Request.HasJsonContentType())
{
var request = await _serializer.ReadAsync<GraphQLRequest>(HttpContext.Request.Body, HttpContext.RequestAborted);
return await ExecuteGraphQLRequestAsync(request);
}
return BadRequest();
}
private GraphQLRequest BuildRequest(string query, string? operationName, string? variables = null, string? extensions = null)
=> new GraphQLRequest
{
Query = query == "" ? null : query,
OperationName = operationName == "" ? null : operationName,
Variables = _serializer.Deserialize<Inputs>(variables == "" ? null : variables),
Extensions = _serializer.Deserialize<Inputs>(extensions == "" ? null : extensions),
};
private async Task<IActionResult> ExecuteGraphQLRequestAsync(GraphQLRequest? request)
{
try
{
var opts = new ExecutionOptions
{
Query = request?.Query,
OperationName = request?.OperationName,
Variables = request?.Variables,
Extensions = request?.Extensions,
CancellationToken = HttpContext.RequestAborted,
RequestServices = HttpContext.RequestServices,
User = HttpContext.User,
};
IValidationRule rule = HttpMethods.IsGet(HttpContext.Request.Method) ? new HttpGetValidationRule() : new HttpPostValidationRule();
opts.ValidationRules = DocumentValidator.CoreRules.Append(rule);
opts.CachedDocumentValidationRules = new[] {
rule };
return new ExecutionResultActionResult(await _executer.ExecuteAsync(opts));
}
catch
{
return BadRequest();
}
}
}