【问题标题】:Cannot set Koajs' ctx.status and ctx.body when using request-promise使用 request-promise 时无法设置 Koajs 的 ctx.status 和 ctx.body
【发布时间】:2020-01-26 02:37:35
【问题描述】:

我目前正在编写验证 Shopify 应用程序的手动设置,类似于他们在 NodeJS and Express 中设置的方式。对于应用程序,我使用的是 NodeJS 和 Koa 框架。我还使用 @koa/router 进行路由并尝试使用 request-promises 进行外部 HTTP 请求。我不能使用 Shopify 的 Koa Authentication 中间件,因为它不适用于路由器,并且我需要让代码库在未来支持其他电子商务平台(BigCommerce、Magento 等)。

当我获得访问令牌或错误消息时,我无法在 promise 的 .then().catch() 方法中更改 Koa 的 ctx.status 和 ctx.body。就好像它被忽略了一样。

我有以下设置来获取访问令牌:

const KoaRouter = require('@koa/router'),
const nonce = require('nonce')();
const querystring = require('querystring');
const crypto = require('crypto');
const dotenv = require('dotenv').config();
const request = require('request-promise');
const koaRequest = require('koa-http-request');

// Get Shopify information from the .env file
const apiKey = process.env.SHOPIFY_API_KEY;
const apiSecret = process.env.SHOPIFY_API_SECRET;
const scopes = process.env.SHOPIFY_SCOPE;

const router = new KoaRouter();

const getInstallURL = (shop) => {
  const state = nonce();
  const forwardingAddress = 'https://aecd64c2.ngrok.io'
  const redirectUri = forwardingAddress + '/shopify/auth/callback';
  return {
    "url": 'https://' + shop +
    '/admin/oauth/authorize?client_id=' + apiKey +
    '&scope=' + scopes +
    '&state=' + state +
    '&redirect_uri=' + redirectUri,
    "state": state
  };
}

const verifyHMAC = (query, hmac) => {
  const map = Object.assign({}, query);
  delete map['hmac'];
  const message = querystring.stringify(map);
  const providedHmac = Buffer.from(hmac, 'utf-8');
  const generatedHash = Buffer.from(
    crypto
      .createHmac('sha256', apiSecret)
      .update(message)
      .digest('hex'),
      'utf-8'
    );
  let hashEquals = false;
  // timingSafeEqual will prevent any timing attacks. Arguments must be buffers
  try {
    hashEquals = crypto.timingSafeEqual(generatedHash, providedHmac)
  // timingSafeEqual will return an error if the input buffers are not the same length.
  } catch (e) {
    hashEquals = false;
  };

  return hashEquals;
};

const requestAccessToken = (shop, code) => {
  const accessTokenRequestUrl = 'https://' + shop + '/admin/oauth/access_token';
  const accessTokenPayload = {
    client_id: apiKey,
    client_secret: apiSecret,
    code,
  };

  console.log('Requesting accessToken for ' + shop + "!");
  return request.post(accessTokenRequestUrl, { json: accessTokenPayload });
};

router.get('/shopify/auth', async (ctx, next) => {
  const shop = ctx.request.query.shop;
  console.log(shop);
  if (shop) {
    const ret = getInstallURL(shop);
    console.log(ret)
    ctx.cookies.set('state', ret['state']);
    ctx.redirect(ret['url']);
  } else {
    ctx.status=400;
    ctx.body='Missing shop parameter. Please add ?shop=your-development-shop.myshopify.com to your request';
  }
});

router.get('/shopify/auth/callback', async (ctx, next) => {
  const { shop, hmac, code, state } = ctx.request.query;
  const stateCookie = ctx.cookies.get('state');

  console.log(ctx.request.query);
  console.log(stateCookie);

  if (state !== stateCookie) {
    ctx.status = 403;
    ctx.body='Request origin cannot be verified';
  }

  if (shop && hmac && code) {
    if (!verifyHMAC(ctx.request.query, hmac)) {
      ctx.status = 400;
      ctx.body='HMAC validation failed';
    }

    console.log('Verified HMAC.  Now need to get access token');

    requestAccessToken(shop, code).then(
      (accessTokenResponse) => {
        const accessToken = accessTokenResponse.access_token;
        console.log('Access Token = ' + accessToken);

        ctx.status= 200;
        ctx.body= "Got an access token, let's do something with it";
      }
    ).catch(
      (error) => {
        console.log('An error has occurred!');
        console.log(error.statusCode);
        console.log(error.error.error_description);
        console.log(ctx.status);
        ctx.status = error.statusCode;
        ctx.body= error.error.error_description;
        console.log(ctx.status);
      }
    )

  } else {
    ctx.status = 400;
    ctx.body='Required parameters missing';
  }
});

module.exports = router;

如何让 Koa 的上下文变量在 Promise 中工作?

【问题讨论】:

  • ctx未定义
  • 我添加了针对 Shopify 商店对 Shopify 应用进行身份验证的完整代码示例。
  • 你能从 shopify 获得访问令牌吗?
  • 我可以获得访问令牌。但是,Koa 返回 404 错误。这告诉我,出于某种原因,对 ctx 的修改在 promise .then() 方法中被忽略了。
  • 我发布了一个答案,请检查。如果不起作用,我可以通过 VS LiveShare 或 TeamViewer 为您提供帮助

标签: node.js shopify koa request-promise koa-router


【解决方案1】:

您可以编辑您的requestAccessToken 功能如下。如果函数未定义为异步,则不能使用.then() 方法。

const requestAccessToken = async (shop, code) => {
  const accessTokenRequestUrl = 'https://' + shop + '/admin/oauth/access_token';
  const accessTokenPayload = {
    client_id: apiKey,
    client_secret: apiSecret,
    code,
  };

  console.log('Requesting accessToken for ' + shop + "!");
  return await request.post(accessTokenRequestUrl, { json: accessTokenPayload });
};

【讨论】:

  • 使函数异步工作。然而,对返回的承诺的等待没有奏效。将 await 放在线上 requestAccessToken(shop, code).then(...) 确实有效,并允许我修改 ctx
  • 是的,我不需要写,因为我想你可能会猜到
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-25
  • 1970-01-01
  • 2016-02-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多