【愚公系列】2023年07月 .NET CORE工具案例-GraphQL.Server(服务注入)


前言

1.GraphQL的概念

GraphQL是一种用于API的查询语言和运行时环境。它是由Facebook于2012年开始内部使用,2015年对外宣布开源。GraphQL旨在解决RESTful API的一些限制,如需要多次请求才能获取完整数据、难以扩展、无法精确控制数据返回等。

GraphQL的核心理念是:客户端定义数据需要的结构及其关系,服务器返回与此结构相匹配的数据。因此,GraphQL非常适用于前后端分离的应用,客户端可以精确地控制数据返回,提高了API的可靠性和灵活性。

2.GraphQL的应用场景和使用过程

GraphQL具体应用场景如下:

  1. 前后端分离的应用

  2. 移动应用后端API

  3. 多语言(多平台)支持的API

  4. 大型API架构

GraphQL的详细使用过程如下:

  1. 定义Schema:Schema定义了所有可用的查询、关系和数据类型,可以理解为GraphQL的API文档。

  2. 定义Resolver:Resolver是用来实际获取数据的代码,根据Schema中的查询字段获取实际数据。

  3. 编写查询:根据需要编写查询,查询可以包含多个字段和参数,并且可以进行嵌套。

  4. 发送查询:使用GraphQL客户端发送查询请求,返回与查询匹配的数据。

  5. 处理错误:错误处理是GraphQL重要的一个方面,通过使用错误处理器和异常捕获来管理错误。

GraphQL是一种强大而灵活的API开发工具,通过其独特的查询语言和运行时环境,可以大大提高API的可用性和灵活性,是API开发的一种重要的选择。

3.RESTful API和GraphQL的比较

RESTful API和GraphQL都是用于构建Web API的工具,但它们有一些不同的特点。

  1. 数据获取方式

RESTful API是基于HTTP的协议,使用HTTP动词(GET、POST、PUT、DELETE等)来获取或修改数据。每个资源都有一个唯一的URI,每个动词都对应一个特定的操作。RESTful API的响应是基于请求中的URI和动词的,不能跨越这些边界。

GraphQL使用单个端点来获取和修改数据,使用查询语言来指定所需的数据。GraphQL允许客户端请求精确的数据,而不是只返回整个资源。

  1. 查询效率

RESTful API有一个缺点,就是当需要获取多个资源时,需要进行多个HTTP请求。这种“资源瀑布”的方式会导致多个请求的延迟和带宽消耗,限制了应用程序的性能。

GraphQL允许在一个请求中获取多个资源,并且可以指定所需的字段和关系,避免了不必要的数据传输,更加高效。

  1. 缓存和版本控制

RESTful API通常使用HTTP标准的缓存机制(例如,使用Last-Modified和ETag头),以减少带宽和服务器负载。RESTful API也支持版本控制,可以更好地管理API的变化。

GraphQL没有内置的缓存机制,但可以使用其他缓存机制(例如,HTTP缓存)来缓存GraphQL查询。版本控制通常由客户端管理,GraphQL会返回错误,告知客户端如何修正查询。

  1. 开发效率

RESTful API和GraphQL各自有其优势,RESTful API更容易理解和学习,而GraphQL更加灵活。

GraphQL在数据获取方面更加强大和灵活,能够更好地适应复杂的数据需求。然而,由于GraphQL的语法较为复杂,开发人员需要更多的学习和编写时间。并且,GraphQL还需要一个运行时,并且需要进行更严格的代码检查,使其更难以使用。

  1. API的可发现性

RESTful API的资源URI的结构能够传达出API的基本结构。每个资源都必须在API文档中进行描述。

GraphQL没有URI能够传达出API的结构,API的可发现性需要通过不同的方式进行实现。例如,开发人员需要提供一个文档来描述所有可用的查询和变异操作。

RESTful API和GraphQL各有优劣。选择哪种API取决于项目的需求。如果需要简单和可靠的API,RESTful API是一个不错的选择;如果需要快速、灵活、精确的数据获取方式,GraphQL则是一个更好的选择。

4.GraphQL语法

GraphQL是一种查询语言,用于API的编写和操作。以下是GraphQL的基本语法:

  1. 查询

使用关键字“query”来声明查询类型,后面跟上查询的字段和参数。例如:

query {
    
    
  person(id: "1") {
    
    
    name
    age
  }
}
  1. 变量

使用$符号来声明变量,可以在查询中重用变量。

例如:

query getPerson($id: ID!) {
    
    
  person(id: $id) {
    
    
    name
    age
  }
}

在执行时,需要提供变量的值。例如:

{
    
    
  "id": "1"
}
  1. 片段

可以在查询中使用片段来重用查询部分。片段以“…”开头,后面跟上片段名称。

例如:

query {
    
    
  person(id: "1") {
    
    
    name
    age
    address {
    
    
      ...addressFields
    }
  }
}

fragment addressFields on Address {
    
    
  street
  city
  state
}
  1. 操作名称

可以使用操作名称来标识查询,方便调试和追踪。操作名称在关键字“query”或“mutation”之后的花括号之间定义。

例如:

query getPerson($id: ID!) {
    
    
  person(id: $id) {
    
    
    name
    age
  }
}
  1. 变更

使用关键字“mutation”来声明变更操作。变更可以用来进行数据的修改和创建。

例如:

mutation {
    
    
  createPerson(name: "Sam", age: 25) {
    
    
    id
    name
    age
  }
}

以上是GraphQL的基本语法,还有更多高级特性和语法详情可以参考GraphQL官方文档。

一、服务注入

1.相关服务代码

1、会话类

public interface IChat
{
    
    
    int Count {
    
     get; }
    Message? LastMessage {
    
     get; }

    int ClearMessages();
    Message? DeleteMessage(int id);
    IEnumerable<Message> GetAllMessages();
    IEnumerable<Message> GetMessageFromUser(string from);
    Message PostMessage(MessageInput message);
    IObservable<Message> SubscribeAll();
    IObservable<Event> SubscribeEvents();
    IObservable<Message> SubscribeFromUser(string from);
}
public class Chat : IChat
{
    
    
    private readonly List<Message> _messages = new();
    private int _messageId;
    private readonly Subject<Event> _broadcaster = new();

    public Message? LastMessage {
    
     get; private set; }

    public IEnumerable<Message> GetAllMessages()
    {
    
    
        lock (_messages)
            return _messages.ToList();
    }

    public IEnumerable<Message> GetMessageFromUser(string from)
    {
    
    
        lock (_messages)
            return _messages.Where(x => string.Equals(x.From, from, StringComparison.OrdinalIgnoreCase)).ToList();
    }

    public Message PostMessage(MessageInput message)
    {
    
    
        var newMessage = new Message
        {
    
    
            Id = Interlocked.Increment(ref _messageId),
            Value = message.Message,
            From = message.From,
            Sent = DateTime.UtcNow,
        };
        LastMessage = newMessage;
        lock (_messages)
            _messages.Add(newMessage);
        _broadcaster.OnNext(new Event {
    
     Type = EventType.NewMessage, Message = newMessage });
        return newMessage;
    }

    public Message? DeleteMessage(int id)
    {
    
    
        Message? deletedMessage = null;
        lock (_messages)
        {
    
    
            for (int i = 0; i < _messages.Count; i++)
            {
    
    
                if (_messages[i].Id == id)
                {
    
    
                    deletedMessage = _messages[i];
                    _messages.RemoveAt(i);
                    break;
                }
            }
        }
        if (deletedMessage != null)
            _broadcaster.OnNext(new Event {
    
     Type = EventType.DeleteMessage, Message = deletedMessage });
        return deletedMessage;
    }
    public IObservable<Message> SubscribeAll() => _broadcaster.Where(x => x.Type == EventType.NewMessage).Select(x => x.Message!);

    public IObservable<Message> SubscribeFromUser(string from)
        => SubscribeAll().Where(x => string.Equals(x.From, from, StringComparison.OrdinalIgnoreCase));

    public IObservable<Event> SubscribeEvents() => _broadcaster;

    public int ClearMessages()
    {
    
    
        int count;
        lock (_messages)
        {
    
    
            count = _messages.Count;
            _messages.Clear();
        }
        _broadcaster.OnNext(new Event {
    
     Type = EventType.ClearMessages });
        return count;
    }

    public int Count
    {
    
    
        get
        {
    
    
            lock (_messages)
                return _messages.Count;
        }
    }
}

2、相关类

public class Chat : IChat
{
    
    
    private readonly List<Message> _messages = new();
    private int _messageId;
    private readonly Subject<Event> _broadcaster = new();

    public Message? LastMessage {
    
     get; private set; }

    public IEnumerable<Message> GetAllMessages()
    {
    
    
        lock (_messages)
            return _messages.ToList();
    }

    public IEnumerable<Message> GetMessageFromUser(string from)
    {
    
    
        lock (_messages)
            return _messages.Where(x => string.Equals(x.From, from, StringComparison.OrdinalIgnoreCase)).ToList();
    }

    public Message PostMessage(MessageInput message)
    {
    
    
        var newMessage = new Message
        {
    
    
            Id = Interlocked.Increment(ref _messageId),
            Value = message.Message,
            From = message.From,
            Sent = DateTime.UtcNow,
        };
        LastMessage = newMessage;
        lock (_messages)
            _messages.Add(newMessage);
        _broadcaster.OnNext(new Event {
    
     Type = EventType.NewMessage, Message = newMessage });
        return newMessage;
    }

    public Message? DeleteMessage(int id)
    {
    
    
        Message? deletedMessage = null;
        lock (_messages)
        {
    
    
            for (int i = 0; i < _messages.Count; i++)
            {
    
    
                if (_messages[i].Id == id)
                {
    
    
                    deletedMessage = _messages[i];
                    _messages.RemoveAt(i);
                    break;
                }
            }
        }
        if (deletedMessage != null)
            _broadcaster.OnNext(new Event {
    
     Type = EventType.DeleteMessage, Message = deletedMessage });
        return deletedMessage;
    }
    public IObservable<Message> SubscribeAll() => _broadcaster.Where(x => x.Type == EventType.NewMessage).Select(x => x.Message!);

    public IObservable<Message> SubscribeFromUser(string from)
        => SubscribeAll().Where(x => string.Equals(x.From, from, StringComparison.OrdinalIgnoreCase));

    public IObservable<Event> SubscribeEvents() => _broadcaster;

    public int ClearMessages()
    {
    
    
        int count;
        lock (_messages)
        {
    
    
            count = _messages.Count;
            _messages.Clear();
        }
        _broadcaster.OnNext(new Event {
    
     Type = EventType.ClearMessages });
        return count;
    }

    public int Count
    {
    
    
        get
        {
    
    
            lock (_messages)
                return _messages.Count;
        }
    }
}
public enum EventType
{
    
    
    NewMessage,
    DeleteMessage,
    ClearMessages,
}
public class Message
{
    
    
    [Id]
    public int Id {
    
     get; set; }

    [Name("Message")]
    public string Value {
    
     get; set; } = null!;

    public string From {
    
     get; set; } = null!;

    public DateTime Sent {
    
     get; set; }
}
public class MessageInput
{
    
    
    public string Message {
    
     get; set; } = null!;
    public string From {
    
     get; set; } = null!;
}

2.注入

using GraphQL;
using GraphQL.Samples.Schemas.Chat;
using Chat = GraphQL.Samples.Schemas.Chat;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<Chat.IChat, Chat.Chat>();

// Add services to the container.
builder.Services.AddGraphQL(b => b
    .AddAutoSchema<Query>(s => s
        .WithMutation<Chat.Mutation>()
        .WithSubscription<Chat.Subscription>())  // schema
    .AddSystemTextJson());   // serializer


var app = builder.Build();

// Configure the HTTP request pipeline.
app.UseDeveloperExceptionPage();
app.UseWebSockets();
app.UseGraphQL("/graphql");
// configure Playground at "/"
app.UseGraphQLVoyager("/ui/voyager");
app.UseGraphQLPlayground(
    "/",
    new GraphQL.Server.Ui.Playground.PlaygroundOptions
    {
    
    
        GraphQLEndPoint = "/graphql",
        SubscriptionsEndPoint = "/graphql",
    });
await app.RunAsync();

3.使用

public class Query
{
    
    
    public static string Hero() => "Luke Skywalker";
    public static int Count([FromServices] IChat chatService)
        => chatService.Count;
}
public class Mutation
{
    
    
    public static Message AddMessage([FromServices] IChat chatService, MessageInput message)
        => chatService.PostMessage(message);

    public static Message? DeleteMessage([FromServices] IChat chatService, [Id] int id)
        => chatService.DeleteMessage(id);

    public static int ClearMessages([FromServices] IChat chatService)
        => chatService.ClearMessages();
}
public class Subscription
{
    
    
    public static IObservable<Message> NewMessages([FromServices] IChat chatService, string? from = null)
        => from == null ? chatService.SubscribeAll() : chatService.SubscribeFromUser(from);

    public static IObservable<Event> Events([FromServices] IChat chatService)
        => chatService.SubscribeEvents();
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/aa2528877987/article/details/131884147