Shopify Node.js(Express)应用报错No Shop Provided的解决方法及店铺类型咨询
解决Shopify应用启动时的"No Shop Provided"错误
问题场景
使用Node.js(Express.js)开发Shopify应用,每次运行项目访问时,都会收到错误提示:
No Shop Provided
报错核心代码(来自node_modules/@shopify/shopify-app-express/build/cjs/middlewares/ensure-installed-on-shop.js):
function getRequestShop(api, config, req, res) { if (typeof req.query.shop !== 'string') { config.logger.error('ensureInstalledOnShop did not receive a shop query argument', { shop: req.query.shop }); res.status(422); res.send('No shop provided'); return undefined; } }
用户的index.js代码:
// @ts-check import { join } from "path"; import { readFileSync } from "fs"; import express from "express"; import serveStatic from "serve-static"; import shopify from "./shopify.js"; import GDPRWebhookHandlers from "./gdpr.js"; import apis from "./api/index.js"; const PORT = parseInt(3003, 10); const STATIC_PATH = process.env.NODE_ENV === "production" ? `${process.cwd()}/web/frontend/dist` : `${process.cwd()}/frontend/`; const app = express(); // Set up Shopify authentication and webhook handling app.get(shopify.config.auth.path, shopify.auth.begin()); app.get( shopify.config.auth.callbackPath, shopify.auth.callback(), shopify.redirectToShopifyOrAppRoot() ); app.post( shopify.config.webhooks.path, shopify.processWebhooks({ webhookHandlers: GDPRWebhookHandlers }) ); app.get('/api/auth/callback', async (req, res) => { // app stores Session in its own storage mechanism const callbackResponse = shopify.auth.callback({ rawRequest: req, rawResponse: res, }); await apis.createSession(callbackResponse.session.toObject()); }); app.get("/api/session/get", async (req, res) => { const sessionId = await shopify.api.session.getCurrentId({ isOnline: false, rawRequest: req, rawResponse: res, }); console.log(sessionId); const session = await apis.getSession(sessionId); console.log(session); res.status(200).send(session.data); }); app.get('/api/plans/check', async (req, res) => { const sessionId = await shopify.api.session.getCurrentId({ isOnline: false, rawRequest: req, rawResponse: res, }); const session = await apis.getSession(sessionId); console.log(shopify); const hasPayment = await shopify.api.billing.check({ session, plans: req.query.plans, isTest: true, }); res.status(200).send(hasPayment); }); app.post('/api/plans/create', async (req, res) => { const sessionId = await shopify.api.session.getCurrentId({ isOnline: false, rawRequest: req, rawResponse: res, }); const session = await apis.getSession(sessionId); const hasPayment = await shopify.api.billing.check({ session, plans: req.query.plans, isTest: true, }); if (!hasPayment) { const confirmationUrl = await shopify.api.billing.request({ session, plans: req.query.plans, isTest: true, }); res.redirect(confirmationUrl); } }); // All endpoints after this point will require an active session app.use("/api/*", shopify.validateAuthenticatedSession()); app.use(express.json()); app.use(serveStatic(STATIC_PATH, { index: false })); app.use("/*", shopify.ensureInstalledOnShop(), async (_req, res, _next) => { return res .status(200) .set("Content-Type", "text/html") .send(readFileSync(join(STATIC_PATH, "index.html"))); }); app.listen(PORT);
报错原因
shopify.ensureInstalledOnShop()中间件会强制检查请求的query参数中是否包含合法的shop字符串(即Shopify店铺域名)。你的根路由/*使用了这个中间件,直接访问本地服务时没有携带shop参数,因此触发错误。
解决方案
1. 从Shopify后台打开应用(推荐)
开发阶段,登录Shopify合作伙伴后台,进入你创建的测试店铺,找到你的应用并点击打开。Shopify会自动生成带shop参数的跳转链接,格式为:
http://localhost:3003?shop=你的测试店铺域名.myshopify.com
这种方式会自动完成授权流程,无需手动处理参数。
2. 手动添加shop参数访问
如果直接访问本地服务,在URL末尾手动拼接shop参数:
http://localhost:3003?shop=your-test-shop.myshopify.com
替换your-test-shop.myshopify.com为你的测试店铺域名。
3. 修正路由逻辑
你的代码中自定义了/api/auth/callback路由,这会和Shopify默认的授权回调路由冲突。建议移除这个自定义路由,在Shopify提供的默认回调流程中处理session存储,比如修改默认的auth callback配置:
app.get( shopify.config.auth.callbackPath, shopify.auth.callback(), async (req, res) => { // 在这里处理session存储 await apis.createSession(res.locals.shopify.session.toObject()); // 然后重定向到应用根目录 shopify.redirectToShopifyOrAppRoot()(req, res); } );
所需的店铺类型
必须使用Shopify官方店铺:
- 开发测试用:在Shopify合作伙伴后台创建的测试店铺,域名格式为
xxx.myshopify.com - 生产环境:已安装你的应用的正式Shopify店铺,同样需要使用
xxx.myshopify.com格式的域名(自定义域名无法直接用于授权流程)
内容的提问来源于stack exchange,提问作者Terencee
相关产品推荐
相关产品推荐

