• ASP.NET Core中使用GraphQL


    ASP.NET Core中使用GraphQL


    字段

    我们已经很好的理解了GraphQL中的字段。在之前HelloWorldQuery的例子中,我们添加了2个字段hellohowdy. 它们都是标量字段。正如GraphQL官网文档中声明的那样

    "At its simplest, GraphQL is about asking for specific fields on objects"

    简单来说,GraphQL就是询问对象中的一些特定字段

    来源: graphql.org

    下面我们来为我们的实例程序添加一些复杂的类型。比如,现在我们需要编写一个库存系统,我们首先添加一个货物类Item, 其代码如下:

    public class Item  
    {
        public string Barcode { get; set; }
    
        public string Title { get; set; }
    
        public decimal SellingPrice { get; set; }
    }
    

    但是我们不希望直接针对这个对象创建查询,因为它不是一个GraphQL对象,它没有继承自ObjectGraphType, 为了创建一个GraphQL查询,我们需要创建一个新类ItemType, 它继承自ObjectGraphType类。

    另外ObjectGraphType类是一个泛型类,所以这里我们需要指定它的泛型参数是Item

    public class ItemType : ObjectGraphType<Item>  
    {
        public ItemType()
        {
            Field(i => i.Barcode);
            Field(i => i.Title);
            Field(i => i.SellingPrice);
        }
    }
    

    这里有2点需要注意。首先我们不在针对字段进行类型声明了。GraphQL库将实体类属性字段类型映射成GraphQL的内置类型。例如这里Barcode的类型string会被映射成GraphQL的内置类型StringGraphType。其次这里我们使用了Lambda表达式设置了实体类属性和GraphQL字段之间的映射, 这有点类似于数据库模型和ViewModel之间的转换的映射。

    下一步,我们需要在HelloWorldQuery中注册ItemType

    public HelloWorldQuery()  
    {
        ...
        ...
    
        Field<ItemType>(
            "item",
            resolve: context =>
            {
               return new Item {
                    Barcode = "123",
                    Title = "Headphone",
                    SellingPrice = 12.99M
                };
            }
        ); 
    }
    

    这里我们暂时设置了一个硬编码的返回值。所以当查询item对象的时候,这个硬编码的返回值会输出出来。

    现在我们启动项目,进入GraphiQL界面

    首先我们设置查询为

    query {
        item{
            barcode
            sellingPrice
        }
    }
    

    运行查询之后,结果是

    {
      "data": {
        "item": {
          "barcode": "123",
          "sellingPrice": 12.99
        }
      }
    }
    

    然后我们修改查询为

    query {
        item{
            barcode
            sellingPrice
            title
        }
    }
    

    运行查询之后,结果是

    {
      "data": {
        "item": {
          "barcode": "123",
          "sellingPrice": 12.99,
          "title": "Headphone"
        }
      }
    }
    

    这说明我们的GraphQL查询已经生效,api根据我们需要的字段返回了正确的返回值。

    参数

    这里我们可以使用参数去除前面的硬编码。

    为了说明如何使用参数,这里我们首先创建一个数据源类DataSource, 其代码如下

    public class DataSource  
    {
        public IList<Item> Items
        {
            get;
            set;
        }
    
        public DataSource()
        {
            Items = new List<Item>(){
                new Item { Barcode= "123", Title="Headphone", SellingPrice=50},
                new Item { Barcode= "456", Title="Keyboard", SellingPrice= 40},
                new Item { Barcode= "789", Title="Monitor", SellingPrice= 100}
            };
        }
    
        public Item GetItemByBarcode(string barcode)
        {
            return Items.First(i => i.Barcode.Equals(barcode));
        }
    }
    

    这里除了Items集合,我们还添加了一个方法GetItemByBarcode, 这个方法可以根据传递的barcode参数返回第一个匹配的Item

    然后现在我们来修改之前的item查询, 添加一个arguments参数, 其代码如下:

    Field<ItemType>(
        "item",
        arguments: new QueryArguments(new QueryArgument<StringGraphType> { Name = "barcode" }),
        resolve: context =>
        {
            var barcode = context.GetArgument<string>("barcode");
            return new DataSource().GetItemByBarcode(barcode);
        }
    );
    

    arguments是一个参数列表,里面可以包含必填参数和选填参数。针对每个参数,我们都需要指定它对应的类型,这里Name属性是设置了当前参数的名称。

    resolve参数中, 你可以使用context.GetArgument()方法获取查询中传递的参数值。

    现在我们重新启动项目,并在GraphiQL中添加如下查询

    query {  
      item (barcode: "123") {
        title
        sellingPrice
      }
    }
    

    输出的查询结果

    {
      "data": {
        "item": {
          "title": "Headphone",
          "sellingPrice": 50
        }
      }
    }
    

    这个结果与我们预想的一样。

    但是这时候如果我们不传递barcode参数

    query {  
      item {
        title
        sellingPrice
      }
    }
    

    程序就会报错

    {
      "data": {
        "item": null
      },
      "errors": [
        {
          "message": "Error trying to resolve item.",
          "locations": [
            {
              "line": 2,
              "column": 3
            }
          ],
          "path": [
            "item"
          ],
          "extensions": {
            "code": "INVALID_OPERATION"
          }
        }
      ]
    }
    

    原因是当前barcode是一个可空项,程序查询时, First方法会报错。所以这时候我们可以使用NonNullGraphType来设置barcode为一个必填项。

    QueryArgument<NonNullGraphType<StringGraphType>> { Name = "barcode" }  
    

    这样重新启动项目后,继续使用之前报错的查询,GraphiQL就会给出校验错误。

    变量

    现在是时候将参数变成动态了。 我们不希望每次在查询中写死查询条件,我们希望这个查询参数是动态的,这时候我们就需要使用到变量。

    首先,这里我们需要确保我们的GraphQL中间件可以接受参数,所以我们需要在GraphQLRequest类中添加一个参数变量

    public class GraphQLRequest
    {
        public string Query { get; set; }
        public JObject Variables { get; set; }
    }
    

    然后我们需要修改GraphQLMiddleware中间件的InvokeAsync方法, 在其中添加一行代码设置doc.Inputs

    var result = await _executor.ExecuteAsync(doc =>
    {
        doc.Schema = _schema;
        doc.Query = request.Query;
    
        doc.Inputs = request.Variables.ToInputs();
    
    }).ConfigureAwait(false);
    

    现在我们的item查询已经支持动态参数了,我们可以运行程序,在GraphiQL中设置如下查询

    query($barcode: String!){  
      item(barcode: $barcode){
        title
        sellingPrice
      }
    }
    

    查询中变量是以$开头的, 后面需要加上变量类型,因为之前我们这是了barcode参数为必填项,所以$barcode变量我们也要设置成必填。变量的必填设置是在变量类型后添加一个!号。

    最后,在GraphiQL中,你可以使用QUERY VARIABLES面板中输入参数的值。如下图所示,最终结果正确的返回了。

    本文源代码:https://github.com/lamondlu/GraphQL_Blogs/tree/master/Part%20V

  • 相关阅读:
    剑指Offer
    剑指Offer
    剑指Offer
    面积(area)
    最少步数
    细胞
    集合的前N个元素
    1~100卡特兰数(存一下hhhh)
    [Codeforces137C]History(排序,水题)
    [Codeforces676B]Pyramid of Glasses(递推,DP)
  • 原文地址:https://www.cnblogs.com/lwqlun/p/9926315.html
Copyright © 2020-2023  润新知