成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

react+graphql起手和特性介紹(二)

NikoManiac / 1646人閱讀

摘要:緊接第一篇文章,起手和特性介紹一,我們接下來(lái)實(shí)現(xiàn),和自定義請(qǐng)求上下文,來(lái)完成創(chuàng)建用戶(hù),發(fā)帖,查看所有帖子的功能首先,我們進(jìn)行自定義請(qǐng)求上下文,來(lái)模擬數(shù)據(jù)庫(kù)和會(huì)話,保存我們的用戶(hù)數(shù)據(jù),帖子數(shù)據(jù),登錄狀態(tài)。

緊接第一篇文章,react+graphql起手和特性介紹(一),我們接下來(lái)實(shí)現(xiàn)resolver,和自定義請(qǐng)求上下文,來(lái)完成創(chuàng)建用戶(hù),發(fā)帖,查看所有帖子的功能
首先,我們進(jìn)行自定義請(qǐng)求上下文,來(lái)模擬數(shù)據(jù)庫(kù)和會(huì)話,保存我們的用戶(hù)數(shù)據(jù),帖子數(shù)據(jù),登錄狀態(tài)。在server目錄下創(chuàng)建context.js文件。

// server/context.js

const store = new Map(); // 模擬數(shù)據(jù)庫(kù),保存注冊(cè)用戶(hù),創(chuàng)建的帖子數(shù)據(jù)

const sessionStore = { // 模擬session會(huì)話,保存用戶(hù)登錄狀態(tài)數(shù)據(jù)
    key: 0,
};

module.exports = (context) => {
    const { ctx } = context; // 我們是將graphql與koa整合在一起,
                             // 這里的ctx就是koa提供的請(qǐng)求上下文
    
    // 我們?yōu)?graphql 的 context 添加 session 和 store                  
    context.session = {
        get() {
            const cookieValue = ctx.cookies.get("user_login");
            return sessionStore[cookieValue];
        },
        set(value) {
            const cookieValue = ++sessionStore.key;
            ctx.cookies.set("user_login", cookieValue);
            sessionStore[cookieValue] = value;
        }
    };
    context.store = store;
    return context;
}

接下來(lái)我們實(shí)現(xiàn)user的reslover和對(duì)應(yīng)的schema

// server/resolver/user.js

const idsKey = "user_ids";

let idCount = 0;

const genId = () => {
    idCount++;
    return "user_id_" + idCount;
};

module.exports = {
    Query: {
        // 查詢(xún)登錄用戶(hù)
        user(root, query, ctx) {
            const { session } = ctx;
            return session.get();
        },
        // 查詢(xún)所有用戶(hù)
        users(root, query, { store }) {
            const ids = store.get(idsKey) || [];

            const res = [];
            ids.forEach(id => {
                res.push(store.get(id));
            });

            return res;
        },
        // 用戶(hù)登錄
        login(root, { id }, { store, session }) {
            const user = store.get(id);
            if (user) session.set(user);
            return user;
        }
    },
    Gender: {
        MALE: 1,
        FEMALE: 2
    },
    // Mutation 是與Query一樣的根節(jié)點(diǎn),與Query沒(méi)有什么區(qū)別,只有語(yǔ)義上的區(qū)分,
    // 對(duì)數(shù)據(jù)進(jìn)行修改和新增的操作都放在 Mutation 中
    Mutation: {
        // 創(chuàng)建用戶(hù)
        createUser(root, { data }, { session, store }) {
            data.id = genId();

            let userIds = store.get(idsKey);
            if (!userIds) userIds = [];
            userIds.push(data.id);
            
            store.set(data.id, data);
            store.set(idsKey, userIds);

            session.set(data);

            return data;
        }
    }
}
# server/schema/user.graphql

...

extend type Query {
    user: User
    users: [User]
    login(id: ID!): User
}

# input 代表輸入type,需要輸入的類(lèi)型需要用input進(jìn)行定義。
# 比如創(chuàng)建用戶(hù)的json數(shù)據(jù),其結(jié)構(gòu)需要用input定義,才能使用
input UserInput {
    name: String
    age: Int
    available: Boolean
    money: Float 
    gender: Gender
    birthday: Date
}

extend type Mutation {
    # 使用 UserInput 作為輸入結(jié)構(gòu)類(lèi)型,! 表示不能為空
    createUser(data: UserInput!): User
}

為使我們返回的自定義類(lèi)型數(shù)據(jù)生效,修改下對(duì)mock進(jìn)行如下修改

// server/mock.js

module.exports = {
    Date(root, args, ctx, info) {
        // info代表解析信息,可以取到當(dāng)前訪問(wèn)的字段名,我們對(duì)返回?cái)?shù)據(jù)root進(jìn)行判斷,
        // 如果為null,則創(chuàng)建新的對(duì)象,否則使用返回的數(shù)據(jù)
        if (!root[info.fieldName]) return new Date();
        return root[info.fieldName];
    }
}

好了,我們的用戶(hù)相關(guān)功能已經(jīng)實(shí)現(xiàn)完成,現(xiàn)在啟動(dòng)服務(wù),我們創(chuàng)建一個(gè)用戶(hù),登錄,并查詢(xún)

在mutation上我們先定義$user變量,語(yǔ)法規(guī)定需以$開(kāi)頭,它的類(lèi)型是UserInput!,對(duì)應(yīng)我們的schema定義,然后在createUser查詢(xún)中使用此變量$user,它對(duì)應(yīng)的schema解析變量是data,data就是我們?cè)趓eslover中訪問(wèn)請(qǐng)求參數(shù)的變量名。具體的請(qǐng)求數(shù)據(jù),我們通過(guò)query variables進(jìn)行定義,它是json格式的數(shù)據(jù),"user"對(duì)應(yīng)我們的$user變量,里面的結(jié)構(gòu)與UserInput!一一對(duì)應(yīng),并輸入值。創(chuàng)建完用戶(hù)之后會(huì)將用戶(hù)數(shù)據(jù)返回,并有對(duì)應(yīng)的id值。
登錄用戶(hù)

查詢(xún)所有用戶(hù)

根據(jù)我們的定義,查詢(xún)出來(lái)的是數(shù)組類(lèi)型
讓我們繼續(xù)完成帖子的功能

// server/resolver/post.js

const idsKey = "post_ids";

let idCount = 0;

const genId = () => {
    idCount++;
    return "post_id_" + idCount;
};

module.exports = {
    Query: {
        post(root, query, { store }) {
            return store.get(query.id)
        },
        posts(root, query, { store }) {
            const ids = store.get(idsKey) || [];
            const res = [];
            ids.forEach(id => {
                res.push(store.get(id));
            });
            return res;
        }
    },
    Post: {
        // 在返回post數(shù)據(jù)時(shí)有個(gè)user字段是User類(lèi)型,我們并不需要每次返回時(shí)都在post查詢(xún)的
        // resolver中查出對(duì)應(yīng)的user數(shù)據(jù),graphql的特性是,如果reslover返回的數(shù)據(jù)沒(méi)有某個(gè)
        // 定義了類(lèi)型的字段值,就會(huì)找類(lèi)型字段的具體定義reslover并執(zhí)行,其root值就是上次查詢(xún)
        // 出來(lái)的對(duì)應(yīng)類(lèi)型值,然后將此reslover返回值拼接到原始對(duì)象中并返回。
        // 在這里具體的執(zhí)行流程會(huì)在下面示例中說(shuō)明
        user(root, query, { store }) {
            if (root && root.userId) {
                return store.get(root.userId)
            }
        }
    },
    Mutation: {
        createPost(root, { data }, { store, session }) {
            // 如果用戶(hù)沒(méi)有登錄,將無(wú)法創(chuàng)建帖子
            if (!session.get()) throw new Error("no permission");

            data.id = genId();

            data.userId = session.get().id;

            let ids = store.get(idsKey);
            if (!ids) ids = [];
            ids.push(data.id);

            store.set(data.id, data);
            store.set(idsKey, ids);

            return data;
        }
    }
}

為了格式化錯(cuò)誤,在創(chuàng)建服務(wù)時(shí),自定義formatError

// server/index.js

...

const server = new ApolloServer({
    ...
    formatError: error => {
        // 刪除 extensions 字段,刪除異常的堆棧,不暴露服務(wù)器發(fā)生錯(cuò)誤的文件
        delete error.extensions;
        return error;
    },
 });

...

繼續(xù)完善post schema

# server/schema/post.graphql

...

extend type Query {
    post(id: ID!): Post @auth(role: ONE)
    posts: [Post] @auth(role: ALL)
}

input PostInput {
    title: String!
    content: String!
}

extend type Mutation {
    createPost(data: PostInput!): Post
}

如果會(huì)話有異常,沒(méi)有cookie信息,修改下graphql gui客戶(hù)端的配置
修改 "request.credentials": "omit" 為 "request.credentials": "include"

下面我們進(jìn)行創(chuàng)建帖子和查詢(xún)帖子的操作


可以看到,我們?cè)诖acreatePost和posts代碼中并沒(méi)有查詢(xún)user,這里也會(huì)返回user數(shù)據(jù),是因?yàn)槲覀兌x了Post的user字段對(duì)應(yīng)的reslover方法,在返回類(lèi)型為Post時(shí),posts/createPost返回的數(shù)據(jù)user字段為空,graphql就會(huì)自動(dòng)調(diào)用user的reslover方法,并且之前posts/createPost返回的數(shù)據(jù)會(huì)作為user的reslover中root參數(shù)傳入,這樣我們就可以從root數(shù)據(jù)中獲取userId,然后對(duì)user數(shù)據(jù)的查詢(xún)只用放在一個(gè)地方執(zhí)行就可以。graphql很好地分化了類(lèi)型數(shù)據(jù)的處理邏輯,使每個(gè)resolver只關(guān)注處理此層對(duì)應(yīng)的數(shù)據(jù),剩下的數(shù)據(jù)拼接graphql會(huì)幫我們處理好。

最后我們將用自定義指令,來(lái)實(shí)現(xiàn)服務(wù)端鑒權(quán)操作
創(chuàng)建文件directive.js

// server/directive.js

const { SchemaDirectiveVisitor } = require("apollo-server-koa");

class AuthDirective extends SchemaDirectiveVisitor {
    visitFieldDefinition(field) {
        // 對(duì)用戶(hù)年齡進(jìn)行校驗(yàn)
        const age = this.args.age;
        // 指令的實(shí)現(xiàn)方式是將resolvoer進(jìn)行hack,因此指令本質(zhì)也是resolver
        const realResolve = field.resolve;

        field.resolve = async function (root, query, context, info) {
            const user = context.session.get();
            if (user && user.age >= age) {
                return await realResolve.call(this, root, query, context, info);
            } else {
                throw Error("no permission");
            }
        };
    }
}

module.exports = {
    auth: AuthDirective
}

在schema中定義指令

# server/schema/schema.graphql

...

# 使用directive關(guān)鍵子定義指令, auth 指令名,age為此指令接收的參數(shù)
directive @auth(
  age: Int,
) on FIELD_DEFINITION
# FIELD_DEFINITION 表示此指令應(yīng)用于字段定義

使用指令

# server/schema/post.graphql

...

extend type Query {
    post(id: ID!): Post @auth(age: 18)
    posts: [Post] @auth(age: 20)
}

...

現(xiàn)在我們?cè)龠M(jìn)行查詢(xún),發(fā)現(xiàn)指令已經(jīng)生效,用戶(hù)age小于20是不能查出posts數(shù)據(jù)的,而post是可以查出數(shù)據(jù)的

到此,我們graphql后端服務(wù)的搭建和特性就介紹完了,后面我們會(huì)介紹前端react如何整合graphql

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/99637.html

相關(guān)文章

  • react+graphql手和特性介紹(三)

    摘要:如果你對(duì)這系列文章有疑問(wèn)或發(fā)現(xiàn)有錯(cuò)誤的地方,歡迎在下方留言討論。 緊接上篇react+graphql起手和特性介紹(二),介紹完graphql與koa的服務(wù)搭建和graphql的一些常用特性,接下來(lái)我們介紹下在react中如何使用graphql我們使用create-react-app創(chuàng)建react應(yīng)用: npm i -g create-react-app mkdir react-gra...

    soasme 評(píng)論0 收藏0
  • 王下邀月熊_Chevalier的前端每周清單系列文章索引

    摘要:感謝王下邀月熊分享的前端每周清單,為方便大家閱讀,特整理一份索引。王下邀月熊大大也于年月日整理了自己的前端每周清單系列,并以年月為單位進(jìn)行分類(lèi),具體內(nèi)容看這里前端每周清單年度總結(jié)與盤(pán)點(diǎn)。 感謝 王下邀月熊_Chevalier 分享的前端每周清單,為方便大家閱讀,特整理一份索引。 王下邀月熊大大也于 2018 年 3 月 31 日整理了自己的前端每周清單系列,并以年/月為單位進(jìn)行分類(lèi),具...

    2501207950 評(píng)論0 收藏0
  • 前端每周清單年度總結(jié)與盤(pán)點(diǎn)

    摘要:前端每周清單年度總結(jié)與盤(pán)點(diǎn)在過(guò)去的八個(gè)月中,我?guī)缀踔蛔隽藘杉?,工作與整理前端每周清單。本文末尾我會(huì)附上清單線索來(lái)源與目前共期清單的地址,感謝每一位閱讀鼓勵(lì)過(guò)的朋友,希望你們能夠繼續(xù)支持未來(lái)的每周清單。 showImg(https://segmentfault.com/img/remote/1460000010890043); 前端每周清單年度總結(jié)與盤(pán)點(diǎn) 在過(guò)去的八個(gè)月中,我?guī)缀踔蛔隽?..

    jackwang 評(píng)論0 收藏0
  • 前端每周清單第 10 期:Firefox53、React VR發(fā)布、Microsoft Edge現(xiàn)代

    摘要:新聞熱點(diǎn)國(guó)內(nèi)國(guó)外,前端最新動(dòng)態(tài)發(fā)布近日,正式發(fā)布新版本中提供了一系列的特性與問(wèn)題修復(fù)。而近日正式發(fā)布,其能夠幫助開(kāi)發(fā)者快速構(gòu)建應(yīng)用。 前端每周清單第 10 期:Firefox53、React VR發(fā)布、JS測(cè)試技術(shù)概述、Microsoft Edge現(xiàn)代DOM樹(shù)構(gòu)建及性能之道 為InfoQ中文站特供稿件,首發(fā)地址為這里;如需轉(zhuǎn)載,請(qǐng)與InfoQ中文站聯(lián)系。從屬于筆者的 Web 前端入門(mén)...

    MingjunYang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<