mcpField
B
b2c-controllers

b2c-controllers

github.com/salesforcecommercecloud开发57102 次安装今天更新
代码仓库

关于 b2c-controllers

为 Salesforce B2C Commerce 开发者提供店面控制器编写指南,涵盖 SFRA 与 Classic 两种模式及其路由、请求处理和扩展方式。

主要功能

  1. 01SFRA 路由与中间件
  2. 02Classic 控制器导出
  3. 03处理请求与响应
  4. 04扩展现有控制器
  5. 05生成本地化 URL

适用场景

  1. 01为店面页面添加路由
  2. 02为简单 API 编写控制器
  3. 03向现有控制器添加功能

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/salesforcecommercecloud/b2c-developer-tooling --skill b2c-controllers

安装后在 Claude Code 中输入/b2c-controllers确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

evals/
trigger-evals.json
references/
CLASSIC-PATTERNS.md
SFRA-PATTERNS.md

详细介绍

控制器技能

本技能将指导你创建 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 支持两种控制器模式:

模式适用场景模块风格
SFRAStorefront Reference Architecture 站点带中间件的 server 模块
Classic非 SFRA 站点、简单 API直接导出并设置 .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 控制器(推荐)

SFRA 控制器使用 server 模块进行路由和中间件处理:

'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)
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)
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
中间件

为路由应用中间件,以处理横切关注点:

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();
});
常用中间件
中间件用途
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) {
    // 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
});
扩展控制器

扩展现有控制器以添加或修改功能:

'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 控制器(非 SFRA)

对于非 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;  // 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;

主要区别: Classic 控制器使用 exports.FunctionName.public = true,而不是 server 模块。

模块导入

使用 require() 导入 B2C Commerce API:

// 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');

最佳实践: 仅在需要时引入模块,不要在文件顶部引入所有模块。

错误处理

使用 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();
});

生成 URL

使用 URLUtils 生成适配区域设置的 URL:

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');

最佳实践

  1. 在 SFRA 中间件链中始终调用 next()
  2. 使用 ViewModels 为模板准备数据
  3. 保持控制器精简——将业务逻辑移至脚本/辅助程序中
  4. 使用 hooks 实现同时适用于店面和 OCAPI 的功能
  5. 妥善处理错误——绝不暴露堆栈跟踪
  6. 使用 */cartridge/... 编写可移植的模块路径

详细参考

如需全面了解模式和示例:

评论

支持 Markdown