b2c-controllers
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseControllers Skill
控制器技能
This skill guides you through creating storefront controllers for Salesforce B2C Commerce. Controllers handle HTTP requests and render responses for the storefront.
本技能将指导你为Salesforce B2C Commerce创建店面控制器。控制器负责处理HTTP请求并为店面渲染响应内容。
Overview
概述
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控制器是用于处理店面请求的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-ShowTwo Controller Patterns
两种控制器模式
B2C Commerce supports two controller patterns:
| Pattern | When to Use | Module Style |
|---|---|---|
| SFRA | Storefront Reference Architecture sites | |
| Classic | Non-SFRA sites, simple APIs | Direct exports with |
SFRA is recommended for most storefront development. Classic controllers are useful for simple endpoints or non-SFRA projects.
B2C Commerce支持两种控制器模式:
| 模式 | 适用场景 | 模块风格 |
|---|---|---|
| SFRA | 店面参考架构站点 | |
| 经典 | 非SFRA站点、简单API | 带有 |
大多数店面开发推荐使用SFRA。经典控制器适用于简单端点或非SFRA项目。
File Location
文件位置
Controllers reside in the cartridge's directory:
controllers/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. handles requests.
Home.jsHome-*控制器存放在cartridge的目录下:
controllers/my-cartridge
/cartridge
/controllers
Home.js # URL: Home-{function}
Product.js # URL: Product-{function}
Cart.js # URL: Cart-{function}命名规则: 控制器文件名会成为URL前缀,会处理所有请求。
Home.jsHome-*SFRA Controllers (Recommended)
SFRA控制器(推荐)
SFRA controllers use the module for routing and middleware:
serverjavascript
'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();SFRA控制器使用模块实现路由和中间件功能:
serverjavascript
'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();Request Object (req)
请求对象(req)
javascript
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 objectjavascript
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 objectResponse Object (res)
响应对象(res)
javascript
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 codejavascript
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 codeMiddleware
中间件
Apply middleware to routes for cross-cutting concerns:
javascript
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();
});为路由应用中间件来处理横切关注点:
javascript
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();
});Common Middleware
常用中间件
| Middleware | Purpose |
|---|---|
| Require HTTPS connection |
| Apply default page caching |
| Validate CSRF token |
| Check tracking consent |
| Require authenticated user |
| 中间件 | 用途 |
|---|---|
| 要求HTTPS连接 |
| 应用默认页面缓存 |
| 验证CSRF令牌 |
| 检查追踪授权状态 |
| 要求用户已完成身份验证 |
Route Events
路由事件
Execute code at specific points in the request lifecycle:
javascript
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
});在请求生命周期的特定节点执行代码:
javascript
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
});Extending Controllers
扩展控制器
Extend existing controllers to add or modify functionality:
javascript
'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();扩展现有控制器以新增或修改功能:
javascript
'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();Classic Controllers (Non-SFRA)
经典控制器(非SFRA)
For non-SFRA sites or simple endpoints, use direct exports:
javascript
'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 instead of the module.
exports.FunctionName.public = trueserver对于非SFRA站点或简单端点,可使用直接导出的方式:
javascript
'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;核心区别: 经典控制器使用标记可访问方法,而非使用模块。
exports.FunctionName.public = trueserverModule Imports
模块导入
Import B2C Commerce APIs using :
require()javascript
// 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.
使用导入B2C Commerce API:
require()javascript
// 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');最佳实践: 仅在需要时导入模块,不要在文件顶部一次性导入所有模块。
Error Handling
错误处理
Wrap operations in try-catch blocks:
javascript
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();
});使用try-catch块包裹操作逻辑:
javascript
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();
});Generating URLs
生成URL
Use URLUtils to generate locale-aware URLs:
javascript
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');使用URLUtils生成适配多语言的URL:
javascript
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');Best Practices
最佳实践
- Always call in SFRA middleware chain
next() - Use ViewModels to prepare data for templates
- Keep controllers thin - move business logic to scripts/helpers
- Use hooks for functionality that works for both storefront and OCAPI
- Handle errors gracefully - never expose stack traces
- Use for portable module paths
*/cartridge/...
- 在SFRA中间件链中始终调用
next() - 使用ViewModel为模板准备数据
- 保持控制器轻量化 - 将业务逻辑移至scripts/helpers目录
- 使用钩子实现同时适用于店面和OCAPI的功能
- 优雅处理错误 - 切勿暴露堆栈跟踪信息
- 使用格式实现可移植的模块路径
*/cartridge/...
Detailed Reference
详细参考
For comprehensive patterns and examples:
- SFRA Patterns - Full SFRA patterns with middleware
- Classic Patterns - Non-SFRA controller patterns
如需了解完整模式和示例:
- SFRA模式 - 包含中间件的完整SFRA模式
- 经典模式 - 非SFRA控制器模式