b2c-controllers by salesforcecommercecloud/b2c-developer-tooling
npx skills add https://github.com/salesforcecommercecloud/b2c-developer-tooling --skill b2c-controllers本技能将指导您为 Salesforce B2C Commerce 创建店面控制器。控制器负责处理 HTTP 请求并渲染店面的响应。
控制器是处理店面请求的 JavaScript 模块。控制器 URL 具有以下结构:
https://{domain}/on/demandware.store/Sites-{SiteName}-Site/{locale}/{ControllerName}-{FunctionName}
示例: https://example.com/on/demandware.store/Sites-RefArch-Site/en_US/Home-Show
B2C Commerce 支持两种控制器模式:
| 模式 | 何时使用 | 模块风格 |
|---|---|---|
| SFRA | Storefront Reference Architecture 站点 | 使用中间件的 server 模块 |
| Classic | 非 SFRA 站点,简单 API | 使用 的直接导出 |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
.public = true对于大多数店面开发,推荐使用 SFRA。Classic 控制器适用于简单的端点或非 SFRA 项目。
控制器位于 cartridge 的 controllers 目录中:
/my-cartridge
/cartridge
/controllers
Home.js # URL: Home-{function}
Product.js # URL: Product-{function}
Cart.js # URL: Cart-{function}
命名规则: 控制器文件名将成为 URL 前缀。Home.js 处理 Home-* 请求。
SFRA 控制器使用 server 模块进行路由和中间件处理:
'use strict';
var server = require('server');
// 处理 GET 请求
server.get('Show', function (req, res, next) {
res.render('home/homepage');
next();
});
// 处理 POST 请求
server.post('Subscribe', function (req, res, next) {
var email = req.form.email;
// 处理订阅...
res.json({ success: true });
next();
});
module.exports = server.exports();
req.querystring // 查询参数:?q=shoes -> req.querystring.q
req.form // 表单 POST 数据:req.form.email
req.httpMethod // HTTP 方法:'GET'、'POST' 等
req.httpHeaders // 请求头
req.currentCustomer // 当前客户对象
req.locale // 当前区域设置
req.session // 会话对象
res.render('template', model) // 使用数据渲染 ISML 模板
res.json(object) // 返回 JSON 响应
res.redirect(url) // 重定向到 URL
res.setViewData(data) // 向视图模型添加数据
res.getViewData() // 获取当前视图模型
res.setStatusCode(code) // 设置 HTTP 状态码
将中间件应用于路由以处理横切关注点:
var server = require('server');
var cache = require('*/cartridge/scripts/middleware/cache');
var consentTracking = require('*/cartridge/scripts/middleware/consentTracking');
var csrfProtection = require('*/cartridge/scripts/middleware/csrf');
// 应用缓存
server.get('Show', cache.applyDefaultCache, function (req, res, next) {
res.render('home/homepage');
next();
});
// 要求 HTTPS
server.post('Login', server.middleware.https, function (req, res, next) {
// 处理登录...
next();
});
// 表单的 CSRF 保护
server.post('Submit', csrfProtection.validateAjaxRequest, function (req, res, next) {
// 处理表单提交...
next();
});
| 中间件 | 用途 |
|---|---|
server.middleware.https | 要求 HTTPS 连接 |
cache.applyDefaultCache | 应用默认页面缓存 |
csrfProtection.validateAjaxRequest | 验证 CSRF 令牌 |
consentTracking.consent | 检查跟踪许可 |
userLoggedIn.validateLoggedIn | 要求已认证用户 |
在请求生命周期的特定点执行代码:
server.post('Submit', function (req, res, next) {
var form = req.form;
res.setViewData({ email: form.email });
next();
}, function (req, res, next) {
// 附加中间件
next();
});
// 在所有中间件之后、渲染之前执行
this.on('route:BeforeComplete', function (req, res) {
var viewData = res.getViewData();
// 如果需要,修改视图数据
});
扩展现有控制器以添加或修改功能:
'use strict';
var server = require('server');
var page = module.superModule; // 获取父控制器
server.extend(page);
// 添加新路由
server.get('NewRoute', function (req, res, next) {
res.render('newtemplate');
next();
});
// 覆盖现有路由
server.replace('Show', function (req, res, next) {
// 自定义实现
res.render('custom/homepage');
next();
});
// 前置到现有路由
server.prepend('Show', function (req, res, next) {
// 在原处理程序之前运行
next();
});
// 追加到现有路由
server.append('Show', function (req, res, next) {
// 在原处理程序之后运行
var viewData = res.getViewData();
viewData.customData = 'value';
res.setViewData(viewData);
next();
});
module.exports = server.exports();
对于非 SFRA 站点或简单端点,使用直接导出:
'use strict';
var ISML = require('dw/template/ISML');
exports.Show = function () {
var params = request.httpParameterMap;
var productId = params.pid.stringValue;
ISML.renderTemplate('product/detail', {
productId: productId
});
};
exports.Show.public = true; // 必需:将函数标记为可访问
exports.GetData = function () {
var result = { status: 'ok', data: [] };
response.setContentType('application/json');
response.writer.print(JSON.stringify(result));
};
exports.GetData.public = true;
关键区别: Classic 控制器使用 exports.FunctionName.public = true 而不是 server 模块。
使用 require() 导入 B2C Commerce API:
// B2C Commerce API
var ProductMgr = require('dw/catalog/ProductMgr');
var Transaction = require('dw/system/Transaction');
var Logger = require('dw/system/Logger');
var URLUtils = require('dw/web/URLUtils');
var Resource = require('dw/web/Resource');
// Cartridge 模块(使用 */ 进行 cartridge 路径解析)
var collections = require('*/cartridge/scripts/util/collections');
var productHelper = require('*/cartridge/scripts/helpers/productHelpers');
最佳实践: 仅在需要时导入模块,而不是在文件顶部全部导入。
在 try-catch 块中包装操作:
server.get('Show', function (req, res, next) {
try {
var product = ProductMgr.getProduct(req.querystring.pid);
if (!product) {
res.setStatusCode(404);
res.render('error/notfound');
return next();
}
res.render('product/detail', { product: product });
} catch (e) {
Logger.error('Product error: ' + e.message);
res.setStatusCode(500);
res.render('error/general');
}
next();
});
使用 URLUtils 生成支持区域设置的 URL:
var URLUtils = require('dw/web/URLUtils');
// 控制器 URL
var productUrl = URLUtils.url('Product-Show', 'pid', 'ABC123');
// 结果:/on/demandware.store/Sites-RefArch-Site/en_US/Product-Show?pid=ABC123
// HTTPS URL
var loginUrl = URLUtils.https('Login-Show');
// 静态资源 URL
var imageUrl = URLUtils.staticURL('/images/logo.png');
next()*/cartridge/... 实现可移植的模块路径有关全面的模式和示例:
每周安装次数
76
代码仓库
GitHub 星标数
34
首次出现
2026年2月16日
安全审计
安装于
github-copilot70
codex67
cursor67
opencode66
gemini-cli65
amp65
This skill guides you through creating storefront controllers for Salesforce B2C Commerce. Controllers handle HTTP requests and render responses for the storefront.
Controllers are JavaScript modules that handle storefront requests. A controller URL has this structure:
https://{domain}/on/demandware.store/Sites-{SiteName}-Site/{locale}/{ControllerName}-{FunctionName}
Example: https://example.com/on/demandware.store/Sites-RefArch-Site/en_US/Home-Show
B2C Commerce supports two controller patterns:
| Pattern | When to Use | Module Style |
|---|---|---|
| SFRA | Storefront Reference Architecture sites | server module with middleware |
| Classic | Non-SFRA sites, simple APIs | Direct exports with .public = true |
SFRA is recommended for most storefront development. Classic controllers are useful for simple endpoints or non-SFRA projects.
Controllers reside in the cartridge's controllers directory:
/my-cartridge
/cartridge
/controllers
Home.js # URL: Home-{function}
Product.js # URL: Product-{function}
Cart.js # URL: Cart-{function}
Naming: Controller filename becomes the URL prefix. Home.js handles Home-* requests.
SFRA controllers use the server module for routing and middleware:
'use strict';
var server = require('server');
// Handle GET request
server.get('Show', function (req, res, next) {
res.render('home/homepage');
next();
});
// Handle POST request
server.post('Subscribe', function (req, res, next) {
var email = req.form.email;
// Process subscription...
res.json({ success: true });
next();
});
module.exports = server.exports();
req.querystring // Query parameters: ?q=shoes -> req.querystring.q
req.form // Form POST data: req.form.email
req.httpMethod // HTTP method: 'GET', 'POST', etc.
req.httpHeaders // Request headers
req.currentCustomer // Current customer object
req.locale // Current locale
req.session // Session object
res.render('template', model) // Render ISML template with data
res.json(object) // Return JSON response
res.redirect(url) // Redirect to URL
res.setViewData(data) // Add data to view model
res.getViewData() // Get current view model
res.setStatusCode(code) // Set HTTP status code
Apply middleware to routes for cross-cutting concerns:
var server = require('server');
var cache = require('*/cartridge/scripts/middleware/cache');
var consentTracking = require('*/cartridge/scripts/middleware/consentTracking');
var csrfProtection = require('*/cartridge/scripts/middleware/csrf');
// Apply caching
server.get('Show', cache.applyDefaultCache, function (req, res, next) {
res.render('home/homepage');
next();
});
// Require HTTPS
server.post('Login', server.middleware.https, function (req, res, next) {
// Handle login...
next();
});
// CSRF protection for forms
server.post('Submit', csrfProtection.validateAjaxRequest, function (req, res, next) {
// Handle form submission...
next();
});
| Middleware | Purpose |
|---|---|
server.middleware.https | Require HTTPS connection |
cache.applyDefaultCache | Apply default page caching |
csrfProtection.validateAjaxRequest | Validate CSRF token |
consentTracking.consent | Check tracking consent |
userLoggedIn.validateLoggedIn | Require authenticated user |
Execute code at specific points in the request lifecycle:
server.post('Submit', function (req, res, next) {
var form = req.form;
res.setViewData({ email: form.email });
next();
}, function (req, res, next) {
// Additional middleware
next();
});
// Execute after all middleware, before render
this.on('route:BeforeComplete', function (req, res) {
var viewData = res.getViewData();
// Modify view data if needed
});
Extend existing controllers to add or modify functionality:
'use strict';
var server = require('server');
var page = module.superModule; // Get parent controller
server.extend(page);
// Add new route
server.get('NewRoute', function (req, res, next) {
res.render('newtemplate');
next();
});
// Override existing route
server.replace('Show', function (req, res, next) {
// Custom implementation
res.render('custom/homepage');
next();
});
// Prepend to existing route
server.prepend('Show', function (req, res, next) {
// Runs before original handler
next();
});
// Append to existing route
server.append('Show', function (req, res, next) {
// Runs after original handler
var viewData = res.getViewData();
viewData.customData = 'value';
res.setViewData(viewData);
next();
});
module.exports = server.exports();
For non-SFRA sites or simple endpoints, use direct exports:
'use strict';
var ISML = require('dw/template/ISML');
exports.Show = function () {
var params = request.httpParameterMap;
var productId = params.pid.stringValue;
ISML.renderTemplate('product/detail', {
productId: productId
});
};
exports.Show.public = true; // Required: marks function as accessible
exports.GetData = function () {
var result = { status: 'ok', data: [] };
response.setContentType('application/json');
response.writer.print(JSON.stringify(result));
};
exports.GetData.public = true;
Key difference: Classic controllers use exports.FunctionName.public = true instead of the server module.
Import B2C Commerce APIs using require():
// B2C Commerce APIs
var ProductMgr = require('dw/catalog/ProductMgr');
var Transaction = require('dw/system/Transaction');
var Logger = require('dw/system/Logger');
var URLUtils = require('dw/web/URLUtils');
var Resource = require('dw/web/Resource');
// Cartridge modules (use */ for cartridge path resolution)
var collections = require('*/cartridge/scripts/util/collections');
var productHelper = require('*/cartridge/scripts/helpers/productHelpers');
Best Practice: Only require modules when needed, not all at the top of the file.
Wrap operations in try-catch blocks:
server.get('Show', function (req, res, next) {
try {
var product = ProductMgr.getProduct(req.querystring.pid);
if (!product) {
res.setStatusCode(404);
res.render('error/notfound');
return next();
}
res.render('product/detail', { product: product });
} catch (e) {
Logger.error('Product error: ' + e.message);
res.setStatusCode(500);
res.render('error/general');
}
next();
});
Use URLUtils to generate locale-aware URLs:
var URLUtils = require('dw/web/URLUtils');
// Controller URL
var productUrl = URLUtils.url('Product-Show', 'pid', 'ABC123');
// Result: /on/demandware.store/Sites-RefArch-Site/en_US/Product-Show?pid=ABC123
// HTTPS URL
var loginUrl = URLUtils.https('Login-Show');
// Static resource URL
var imageUrl = URLUtils.staticURL('/images/logo.png');
next() in SFRA middleware chain*/cartridge/... for portable module pathsFor comprehensive patterns and examples:
Weekly Installs
76
Repository
GitHub Stars
34
First Seen
Feb 16, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
github-copilot70
codex67
cursor67
opencode66
gemini-cli65
amp65
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
120,000 周安装