AgentSkillsCN

sfcc-forms-development

在 Salesforce B2C Commerce 中构建、验证、保护并持久化 SFCC 商店前台表单(SFRA + SiteGenesis 模式)的指南。适用于创建或排查表单 XML、控制器处理、CSRF 保护与验证时使用。

SKILL.md
--- frontmatter
name: sfcc-forms-development
description: Guide for building, validating, securing, and persisting SFCC storefront forms (SFRA + SiteGenesis patterns). Use this when creating or troubleshooting form XML, controller handling, CSRF, and validation.

SFCC Forms Development Skill

This skill guides you through implementing and troubleshooting the Salesforce B2C Commerce Forms Framework.

It covers:

  • XML form definitions (schema, constraints, localization, reuse)
  • Controller patterns (SFRA middleware, request handling, persistence)
  • Secure validation (server-side first, CSRF/XSS protections)
  • Migration guidance (SiteGenesis ➜ SFRA)

When to Use

Use this skill when you need to:

  • Create or modify a form definition in cartridge/forms/**.
  • Render forms in ISML using the platform’s generated field metadata.
  • Process POST submissions safely (CSRF, server-side validation, transactions).
  • Add custom validation beyond XML constraints.
  • Migrate legacy SiteGenesis form flows to SFRA.

Quick Checklist (Production-Grade Forms)

text
[ ] Form routes are POST-only and HTTPS-only
[ ] CSRF token is generated on GET and validated on POST
[ ] Server-side validation gate exists (do not trust client validation)
[ ] All string fields have max-length (DoS & storage safety)
[ ] Regex is conservative (avoid catastrophic backtracking)
[ ] Persistence happens inside Transaction.wrap
[ ] SFRA persistence is isolated in route:BeforeComplete where appropriate
[ ] Explicit mapping for writes (avoid mass assignment)
[ ] Form state cleared when appropriate (pre-render or after success)
[ ] User-provided values are output-encoded in ISML

File Locations

Form XML

Forms are defined per cartridge:

code
/my-cartridge
  /cartridge
    /forms
      /default
        profile.xml
        contact.xml
      /de_DE
        profile.xml

Form Labels + Error Messages (Localization)

Resource bundles typically live in:

code
/my-cartridge
  /cartridge
    /templates
      /resources
        forms.properties
        forms_de_DE.properties

Architecture Notes (Know the Moving Parts)

  • Form XML is a strict server-side contract: it defines types, constraints, and errors.
  • Form instances are stored in session scope (stateful across requests) and must be cleared deliberately.
  • Client-side validation is UX; server-side validation is authoritative.

For deeper context and SFRA vs SiteGenesis architectural differences, see:

Form Definition (XML)

Minimal Example

Keep examples small, but always include:

  • strict types
  • max-length for strings
  • mandatory + missing-error where needed
  • parse/range errors for format/constraint enforcement
xml
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.demandware.com/xml/form/2008-04-19">
    <field formid="email" type="string"
           label="form.email.label"
           mandatory="true"
           max-length="254"
           regexp="^[^@\s]+@[^@\s]+\.[^@\s]+$"
           missing-error="form.email.required"
           parse-error="form.email.invalid"/>

    <action formid="submit" valid-form="true"/>
</form>

Reuse and Modularity

Prefer include to avoid duplicating complex groups (e.g., an address group reused in checkout, profile, and gift registry).

See the cheat sheet:

Controller Patterns

SFRA: Render Form (GET)

Goals:

  • clear stale state when rendering an “empty” form
  • generate a CSRF token
javascript
'use strict';

var server = require('server');
var csrfProtection = require('*/cartridge/scripts/middleware/csrf');

server.get('Show',
    server.middleware.https,
    csrfProtection.generateToken,
    function (req, res, next) {
        var form = server.forms.getForm('contact');
        form.clear();

        res.render('forms/contact', {
            contactForm: form
        });
        next();
    }
);

module.exports = server.exports();

SFRA: Handle Submission (POST)

Goals:

  • validate CSRF first
  • gate all business logic on form.valid
  • isolate DB writes inside Transaction.wrap
  • prefer route:BeforeComplete for persistence to keep transaction boundaries tight
javascript
'use strict';

var server = require('server');
var Transaction = require('dw/system/Transaction');
var csrfProtection = require('*/cartridge/scripts/middleware/csrf');

server.post('Submit',
    server.middleware.https,
    csrfProtection.validateRequest,
    function (req, res, next) {
        var form = server.forms.getForm('contact');

        if (!form.valid) {
            // SFRA base commonly provides a helper like */cartridge/scripts/formErrors.
            // Prefer using it when available.
            res.json({
                success: false,
                fieldErrors: getFormErrorsSafe(form)
            });
            return next();
        }

        var email = form.email.value;
        var message = form.message && form.message.value;

        this.on('route:BeforeComplete', function () {
            Transaction.wrap(function () {
                // Persist explicitly (no mass assignment)
                // e.g. save a Custom Object, update profile, create a case, etc.
                // Keep only assignments inside the transaction.
            });

            form.clear();
        });

        res.json({ success: true });
        next();
    }
);

function getFormErrorsSafe(form) {
    try {
        var formErrors = require('*/cartridge/scripts/formErrors');
        if (formErrors && typeof formErrors.getFormErrors === 'function') {
            return formErrors.getFormErrors(form);
        }
    } catch (e) {
        // Optional dependency; fall back if not present.
    }
    return {};
}

module.exports = server.exports();

Notes:

  • Use csrfProtection.validateAjaxRequest if your route expects AJAX conventions.
  • Keep transactions short: no remote API calls, no heavy computation inside Transaction.wrap.

SiteGenesis (SGJC): Key Differences

  • Form is typically accessed via app.getForm('id') and rendered via pdict.CurrentForms.
  • Templates must use generated htmlName (SiteGenesis uses dwfrm_ prefixes + session-specific IDs).
  • CSRF is often handled manually via dw.web.CSRFProtection.

Use this for migration and pitfalls:

ISML Rendering Rules

Always Use Generated Field Metadata

  • Prefer field.htmlName over hardcoded names.
  • Use server-provided pdict.csrf.* when using SFRA CSRF middleware.

Typical SFRA token:

html
<input type="hidden" name="${pdict.csrf.tokenName}" value="${pdict.csrf.token}" />

XSS Safety

When outputting user-provided values, ensure the context is encoded (default output encoding is usually safe, but be deliberate):

html
<isprint value="${pdict.contactForm.email.value}" encoding="htmlcontent" />

Custom Validation

Use custom validation for business rules that XML cannot express (uniqueness checks, cross-field constraints, etc.).

Patterns:

  • Field-level: invalidate a specific field.
  • Group-level: invalidate the group for cross-field rules.
  • Form-level: invalidate the form for global errors.

Keep custom validation server-side. Client-side validation is optional and must never be the security gate.

Related Skills

  • sfcc-security (CSRF, XSS, input validation)
  • sfcc-sfra-controllers (middleware patterns, response handling)
  • sfcc-isml-development (safe output encoding, template constraints)
  • sfcc-localization (resource bundles, locale fallback)

References