salesforce零基础学习(一百四十五)Apex API 66以后 运行为 User Mode – zero.zhang

salesforce零基础学习(一百四十五)Apex API 66以后 运行为 User Mode – zero.zhang


背景

これまでの経験では、apex コードはシステム モードで実行されていましたが、Summer’26 リリース (つまり、apex api バージョン: 67) 以降、apex はユーザー モードで実行されるため、将来 apex クラスを作成するときは、現在のシステム設定でデフォルトで生成された API バージョンも厳密に確認する必要があります。そうしないと、プログラムで予期しない結果が発生する可能性があります。

私たちのブログでは、以前にシステム モードとユーザー モードについて説明しました。要約すると次のような文になります。

ユーザー モード: 現在実行中のユーザーの権限に従って実行され、オブジェクト権限 (CRUD)、フィールド セキュリティ (FLS)、および共有ルール (共有) がチェックされます。

システム モード: 現在のユーザーのオブジェクト権限と FLS はチェックされません。これは、ユーザーがすべてのオブジェクトとフィールドへのアクセス権を持っていることと同等です。レコードレベルの共有が有効になるかどうかは、apex クラスによって宣言された共有キーワードによって決まります。

では、API バージョン 66 とそれ以前と後の明らかな違いは何でしょうか?次のデモに従うことで、よりよく理解できます。

AIが生成したテスト例

1. 2 つのフィールド (1 つは FLS あり、もう 1 つは FLS なし) を持つカスタム テーブルを作成します。

salesforce零基础学习(一百四十五)Apex API 66以后 运行为 User Mode – zero.zhang

2. AccessModeDemoController は主に 3 つの機能を実装します。

  • 現在のユーザー情報の表示権限
  • システムモードとユーザーモードを実行するためのフロントエンドボタンを介した異なるルーティング
  • すべてのクエリ シナリオをテストする
/**
 * Apex 用户模式 / 系统模式对比演示(LWC 控制器)。
 *
 * 外层类声明为 with sharing;需要绕过记录共享的场景放在内部 without sharing 类 Elevated 中,
 * 以便同时演示:访问模式(CRUD/FLS)与 sharing 关键字(记录级)是两套独立的机制。
 */
public with sharing class AccessModeDemoController {

    private static final String NOT_RETURNED = '— 未返回 —';
    private static final String DEMO_FIELDS = 'Id, Name, Public_Note__c, Secret_Note__c, Owner.Name';
    private static final List DEMO_PATHS = new List
        return e?.body?.message ;
    private static final List DEMO_PUBLIC_PATHS = new List;

    // ---------------------------------------------------------------- DTO

    public class ScenarioResult 
        return e?.body?.message 

    public class UserContext  e?.message 

    // ---------------------------------------------------------------- 内部 without sharing 类

    private without sharing class Elevated {
        List querySystemMode() {
            return [SELECT Id, Name, Public_Note__c, Secret_Note__c, Owner.Name
                    FROM Access_Mode_Demo__c WITH SYSTEM_MODE ORDER BY CreatedDate];
        }
        Integer countAll() {
            return Database.countQuery('SELECT COUNT() FROM Access_Mode_Demo__c', AccessLevel.SYSTEM_MODE);
        }
        void insertAsSystem(Access_Mode_Demo__c rec) {
            insert as system rec;
        }
    }

    // ---------------------------------------------------------------- 当前用户上下文

    @AuraEnabled
    public static UserContext getUserContext() {
        Id uid = UserInfo.getUserId();
        UserContext ctx = new UserContext();
        ctx.userId = uid;
        ctx.userName = UserInfo.getName();
        ctx.loginName = UserInfo.getUserName();
        User me = [SELECT Profile.Name FROM User WHERE Id = :uid WITH SYSTEM_MODE];
        ctx.profileName = me.Profile?.Name;
        ctx.modifyAllData = [SELECT COUNT() FROM PermissionSetAssignment
                             WHERE AssigneeId = :uid AND PermissionSet.PermissionsModifyAllData = true
                             WITH SYSTEM_MODE] > 0;
        ctx.canCreateDemo = Schema.SObjectType.Access_Mode_Demo__c.isCreateable();
        ctx.canReadSecret = Schema.SObjectType.Access_Mode_Demo__c.fields.Secret_Note__c.isAccessible();
        ctx.totalDemoRecords = new Elevated().countAll();
        ctx.ownDemoRecords = [SELECT COUNT() FROM Access_Mode_Demo__c WHERE OwnerId = :uid WITH SYSTEM_MODE];
        return ctx;
    }

    // ---------------------------------------------------------------- 查询对比场景

    @AuraEnabled
    public static List runAllScenarios() {
        List results = new List();

        // A. 未声明访问模式 · 类 API 66.0
        ScenarioResult a = newResult('A', '默认模式', '未声明访问模式(类 API 66.0)', '默认(系统)', 'without sharing',
            '// AccessModeDefaultV66.cls  (apiVersion 66.0)\n[SELECT ' + DEMO_FIELDS + ' FROM Access_Mode_Demo__c]',
            'API 66.0 及以前默认系统模式:不检查当前用户的对象权限和 FLS;without sharing 也不按共享过滤。');
        try {
            fill(a, AccessModeDefaultV66.queryWithoutModeClause(), DEMO_PATHS);
        } catch (Exception e) { fail(a, e); }
        results.add(a);

        // B. 未声明访问模式 · 类 API 68.0
        ScenarioResult b = newResult('B', '默认模式', '未声明访问模式(类 API 68.0)', '默认(用户)', 'without sharing',
            '// AccessModeDefaultV68.cls  (apiVersion 68.0)\n[SELECT ' + DEMO_FIELDS + ' FROM Access_Mode_Demo__c]',
            'API 67.0 及以后默认用户模式:同样的代码,仅因类的 API 版本不同,就会检查 CRUD、FLS 和共享。');
        try {
            fill(b, AccessModeDefaultV68.queryWithoutModeClause(), DEMO_PATHS);
        } catch (Exception e) { fail(b, e); }
        results.add(b);

        // C. WITH USER_MODE(包含受限字段)
        ScenarioResult c = newResult('C', '显式声明', 'WITH USER_MODE(包含 Secret_Note__c)', '用户模式', 'with sharing',
            '[SELECT ' + DEMO_FIELDS + ' FROM Access_Mode_Demo__c WITH USER_MODE]',
            '用户模式检查 FLS:用户没有 Secret_Note__c 读取权限时整条查询抛 QueryException,可用 getInaccessibleFields() 拿到全部无权限字段。');
        try {
            fill(c, [SELECT Id, Name, Public_Note__c, Secret_Note__c, Owner.Name
                     FROM Access_Mode_Demo__c WITH USER_MODE ORDER BY CreatedDate], DEMO_PATHS);
        } catch (Exception e) { fail(c, e); }
        results.add(c);

        // D. WITH USER_MODE(仅有权限字段)
        ScenarioResult d = newResult('D', '显式声明', 'WITH USER_MODE(仅查询有权限字段)', '用户模式', 'with sharing',
            '[SELECT Id, Name, Public_Note__c, Owner.Name FROM Access_Mode_Demo__c WITH USER_MODE]',
            '用户模式始终遵守共享规则:OWD 为 Private 时,普通用户只能看到自己拥有或被共享的记录。');
        try {
            fill(d, [SELECT Id, Name, Public_Note__c, Owner.Name
                     FROM Access_Mode_Demo__c WITH USER_MODE ORDER BY CreatedDate], DEMO_PUBLIC_PATHS);
        } catch (Exception e) { fail(d, e); }
        results.add(d);

        // E. WITH SYSTEM_MODE + without sharing
        ScenarioResult e1 = newResult('E', '显式声明', 'WITH SYSTEM_MODE + without sharing', '系统模式', 'without sharing',
            'private without sharing class Elevated {\n  [SELECT ' + DEMO_FIELDS + ' FROM Access_Mode_Demo__c WITH SYSTEM_MODE]\n}',
            '系统模式不检查 CRUD/FLS;类为 without sharing,因此也不按共享过滤 —— 能看到全部记录和 Secret_Note__c。');
        try {
            fill(e1, new Elevated().querySystemMode(), DEMO_PATHS);
        } catch (Exception e) { fail(e1, e); }
        results.add(e1);

        // F. WITH SYSTEM_MODE + with sharing
        ScenarioResult f = newResult('F', '显式声明', 'WITH SYSTEM_MODE + with sharing', '系统模式', 'with sharing',
            'public with sharing class AccessModeDemoController {\n  [SELECT ' + DEMO_FIELDS + ' FROM Access_Mode_Demo__c WITH SYSTEM_MODE]\n}',
            '系统模式下记录可见性由类的 sharing 关键字决定:with sharing 仍按共享过滤行,但 FLS 不检查,Secret_Note__c 照样返回。');
        try {
            fill(f, [SELECT Id, Name, Public_Note__c, Secret_Note__c, Owner.Name
                     FROM Access_Mode_Demo__c WITH SYSTEM_MODE ORDER BY CreatedDate], DEMO_PATHS);
        } catch (Exception e) { fail(f, e); }
        results.add(f);

        // G. Security.stripInaccessible
        ScenarioResult g = newResult('G', '优雅降级', 'Security.stripInaccessible(READABLE)', '系统模式查询 + 剥离', 'with sharing',
            'List recs = [SELECT ... FROM Access_Mode_Demo__c WITH SYSTEM_MODE];\n'
                + 'SObjectAccessDecision dec = Security.stripInaccessible(AccessType.READABLE, recs);\n'
                + 'dec.getRecords(); dec.getRemovedFields();',
            '不抛异常,而是把当前用户无权读取的字段从结果里移除;getRemovedFields() 返回被移除的字段。');
        try {
            List raw = [SELECT Id, Name, Public_Note__c, Secret_Note__c, Owner.Name
                                             FROM Access_Mode_Demo__c WITH SYSTEM_MODE ORDER BY CreatedDate];
            SObjectAccessDecision dec = Security.stripInaccessible(AccessType.READABLE, raw);
            fill(g, dec.getRecords(), DEMO_PATHS);
            for (String obj : dec.getRemovedFields().keySet()) {
                for (String fld : dec.getRemovedFields().get(obj)) {
                    g.problemFields.add(obj + '.' + fld);
                }
            }
        } catch (Exception e) { fail(g, e); }
        results.add(g);

        return results;
    }

    // ---------------------------------------------------------------- DML 对比场景

    @AuraEnabled
    public static ScenarioResult createRecord(String name, String publicNote, String secretNote, String mode) {
        Boolean asUser = (mode == 'USER');
        ScenarioResult r = newResult(asUser ? 'DML_USER' : 'DML_SYSTEM', 'DML',
            asUser ? 'insert as user' : 'insert as system',
            asUser ? '用户模式' : '系统模式', asUser ? 'with sharing' : 'without sharing',
            asUser ? 'insert as user rec;' : 'insert as system rec;  // 在 without sharing 内部类中执行',
            asUser
                ? '用户模式 DML 检查对象“新建”权限与字段编辑 FLS。本 demo 实测:缺少对象新建权限时抛 System.TypeException(DML operation INSERT not allowed);官方文档说明 FLS 错误可通过 DmlException.getDmlFieldNames() 获取。'
                : '系统模式 DML 不检查当前用户的 CRUD/FLS,即使用户没有“新建”权限也能写入。');
        Access_Mode_Demo__c rec = new Access_Mode_Demo__c(
            Name = String.isBlank(name) ? 'Demo ' + System.now().format('HH:mm:ss') : name,
            Public_Note__c = publicNote,
            Secret_Note__c = secretNote);
        try {
            if (asUser) {
                insert as user rec;
            } else {
                new Elevated().insertAsSystem(rec);
            }
            r.success = true;
            r.rowCount = 1;
            Access_Mode_Demo__c saved = [SELECT Id, Name, Public_Note__c, Secret_Note__c, Owner.Name
                                         FROM Access_Mode_Demo__c WHERE Id = :rec.Id WITH SYSTEM_MODE];
            fill(r, new List{ saved }, DEMO_PATHS);
        } catch (Exception e) {
            fail(r, e);
        }
        return r;
    }

    // ---------------------------------------------------------------- helpers

    private static ScenarioResult newResult(String key, String grp, String title, String mode, String sharing,
                                            String code, String explanation) {
        ScenarioResult r = new ScenarioResult();
        r.key = key;
        r.category = grp;
        r.title = title;
        r.mode = mode;
        r.sharing = sharing;
        r.codeSnippet = code;
        r.explanation = explanation;
        return r;
    }

    private static void fill(ScenarioResult r, List recs, List paths) {
        r.success = true;
        r.rowCount = recs.size();
        for (String p : paths) {
            r.columns.add(new Map{ 'label' => p, 'fieldName' => p.replace('.', '_') });
        }
        for (SObject rec : recs) {
            Map row = new Map{ 'Id' => rec.Id };
            for (String p : paths) {
                row.put(p.replace('.', '_'), readPath(rec, p));
            }
            r.rows.add(row);
        }
    }

    private static void fail(ScenarioResult r, Exception e) {
        r.success = false;
        r.errorType = e.getTypeName();
        r.errorMessage = e.getMessage();
        if (e instanceof QueryException) {
            Map> inaccessible = ((QueryException) e).getInaccessibleFields();
            if (inaccessible != null) {
                for (String obj : inaccessible.keySet()) {
                    for (String fld : inaccessible.get(obj)) {
                        r.problemFields.add(obj + '.' + fld);
                    }
                }
            }
        } else if (e instanceof DmlException) {
            DmlException de = (DmlException) e;
            for (Integer i = 0; i < de.getNumDml(); i++) {
                List names = de.getDmlFieldNames(i);
                if (names != null) {
                    r.problemFields.addAll(names);
                }
            }
        }
    }

    private static Object readPath(SObject rec, String path) {
        Map populated = rec.getPopulatedFieldsAsMap();
        if (!path.contains('.')) {
            if (!populated.containsKey(path)) {
                return NOT_RETURNED;
            }
            Object v = populated.get(path);
            return v == null ? '(空值)' : String.valueOf(v);
        }
        String rel = path.substringBefore('.');
        if (!populated.containsKey(rel) || populated.get(rel) == null) {
            return NOT_RETURNED;
        }
        return readPath(rec.getSObject(rel), path.substringAfter('.'));
    }
}

3. AccessModeDefaultV66: API バージョンを 66 に設定します。

public without sharing class AccessModeDefaultV66 {
    public static List queryWithoutModeClause() {
        return [SELECT Id, Name, Public_Note__c, Secret_Note__c, Owner.Name
                FROM Access_Mode_Demo__c ORDER BY CreatedDate];
    }
}

4. AccessModeDefaultV68: コードは V66 と同じですが、唯一の違いは API バージョンを 68 に設定することです。

public without sharing class AccessModeDefaultV68 {
    public static List queryWithoutModeClause() {
        return [SELECT Id, Name, Public_Note__c, Secret_Note__c, Owner.Name
                FROM Access_Mode_Demo__c ORDER BY CreatedDate];
    }
}

5. AccessModeDemo.html

<template>
    <div class="slds-p-around_small">
        
        <lightning-card title="Apex 用户模式 vs 系统模式 演示" icon-name="standard:user_role">
            <div class="slds-p-horizontal_medium">
                <template lwc:if={ctx}>
                    <div class="slds-grid slds-wrap slds-gutters_x-small">
                        <div class="slds-col slds-size_1-of-1 slds-medium-size_1-of-3">
                            <p class="label">当前运行用户p>
                            <p class="value">{ctx.userName}p>
                            <p class="sub">{ctx.loginName}p>
                        div>
                        <div class="slds-col slds-size_1-of-1 slds-medium-size_1-of-3">
                            <p class="label">Profilep>
                            <p class="value">{ctx.profileName}p>
                        div>
                        <div class="slds-col slds-size_1-of-1 slds-medium-size_1-of-3">
                            <p class="label">演示记录(总数 / 本人拥有)p>
                            <p class="value">{ctx.totalDemoRecords} / {ctx.ownDemoRecords}p>
                        div>
                    div>
                    <div class="slds-m-top_small badges">
                        <span class={modifyAllClass}>Modify All Data:{modifyAllText}span>
                        <span class={canCreateClass}>Access_Mode_Demo__c 新建:{canCreateText}span>
                        <span class={canReadSecretClass}>Secret_Note__c 读取 FLS:{canReadSecretText}span>
                    div>
                template>
                <template lwc:if={error}>
                    <div class="slds-text-color_error slds-m-top_small">{error}div>
                template>
            div>
        lightning-card>

        
        <lightning-card title="① DML 对比:insert as user / insert as system" icon-name="standard:record_create">
            <div class="slds-p-horizontal_medium">
                <div class="slds-grid slds-wrap slds-gutters_x-small">
                    <div class="slds-col slds-size_1-of-1 slds-medium-size_1-of-3">
                        <lightning-input label="Name" value={newName} data-field="newName" onchange={handleInput}>lightning-input>
                    div>
                    <div class="slds-col slds-size_1-of-1 slds-medium-size_1-of-3">
                        <lightning-input label="Public Note" value={newPublic} data-field="newPublic" onchange={handleInput}>lightning-input>
                    div>
                    <div class="slds-col slds-size_1-of-1 slds-medium-size_1-of-3">
                        <lightning-input label="Secret Note(受限字段)" value={newSecret} data-field="newSecret" onchange={handleInput}>lightning-input>
                    div>
                div>
                <div class="slds-m-top_small">
                    <lightning-button label="用户模式新建(insert as user)" data-mode="USER" onclick={handleCreate} disabled={busy}>lightning-button>
                    <lightning-button class="slds-m-left_x-small" variant="brand" label="系统模式新建(insert as system)" data-mode="SYSTEM" onclick={handleCreate} disabled={busy}>lightning-button>
                div>
                <template lwc:if={dmlResult}>
                    <div class="slds-m-top_small">
                        <c-access-mode-demo-scenario scenario={dmlResult}>c-access-mode-demo-scenario>
                    div>
                template>
            div>
        lightning-card>

        
        <lightning-card title="② 查询对比场景" icon-name="standard:sossession">
            <lightning-button slot="actions" variant="brand" label="运行全部对比场景" onclick={handleRunAll} disabled={busy}>lightning-button>
            <div class="slds-p-horizontal_medium">
                <template lwc:if={busy}>
                    <lightning-spinner alternative-text="运行中" size="small">lightning-spinner>
                template>
                <template lwc:if={hasResults}>
                    <p class="slds-text-body_small slds-m-bottom_x-small">运行时间:{lastRunAt}p>
                    <table class="slds-table slds-table_cell-buffer slds-table_bordered summary">
                        <thead>
                            <tr class="slds-line-height_reset">
                                <th>#th>
                                <th>场景th>
                                <th>访问模式th>
                                <th>sharingth>
                                <th>结果th>
                                <th>问题字段 / 被剥离字段th>
                            tr>
                        thead>
                        <tbody>
                            <template for:each={summaryRows} for:item="s">
                                <tr key={s.key}>
                                    <td>{s.key}td>
                                    <td>{s.title}td>
                                    <td>{s.mode}td>
                                    <td>{s.sharing}td>
                                    <td><span class={s.statusClass}>{s.statusText}span>td>
                                    <td class="mono">{s.problemText}td>
                                tr>
                            template>
                        tbody>
                    table>
                    <template for:each={results} for:item="r">
                        <div key={r.key} class="slds-m-top_medium">
                            <c-access-mode-demo-scenario scenario={r}>c-access-mode-demo-scenario>
                        div>
                    template>
                template>
                <template lwc:else>
                    <p class="slds-text-color_weak">点击右上角“运行全部对比场景”。p>
                template>
            div>
        lightning-card>
    div>
template>

AccessModeDemo.js

import { LightningElement } from 'lwc';
import getUserContext from '@salesforce/apex/AccessModeDemoController.getUserContext';
import runAllScenarios from '@salesforce/apex/AccessModeDemoController.runAllScenarios';
import createRecord from '@salesforce/apex/AccessModeDemoController.createRecord';

const yesNo = (v) => (v ? '有' : '无');
const badge = (v) => `slds-badge ${v ? 'slds-theme_success' : 'slds-theme_error'}`;

export default class AccessModeDemo extends LightningElement {
    ctx;
    error;
    busy = false;
    results = [];
    dmlResult;
    lastRunAt;
    newName="";
    newPublic="公开备注";
    newSecret="机密备注";

    connectedCallback() {
        this.loadContext();
    }

    async loadContext() {
        try {
            this.ctx = await getUserContext();
        } catch (e) {
            this.error = this.reduceError(e);
        }
    }

    get modifyAllText() { return yesNo(this.ctx?.modifyAllData); }
    get modifyAllClass() { return badge(this.ctx?.modifyAllData); }
    get canCreateText() { return yesNo(this.ctx?.canCreateDemo); }
    get canCreateClass() { return badge(this.ctx?.canCreateDemo); }
    get canReadSecretText() { return yesNo(this.ctx?.canReadSecret); }
    get canReadSecretClass() { return badge(this.ctx?.canReadSecret); }

    get hasResults() {
        return this.results && this.results.length > 0;
    }

    get summaryRows() {
        return this.results.map((r) => ({
            key: r.key,
            title: r.title,
            mode: r.mode,
            sharing: r.sharing,
            statusText: r.success ? `成功 · ${r.rowCount} 行` : `失败 · ${r.errorType}`,
            statusClass: badge(r.success),
            problemText: (r.problemFields || []).join(', ')
        }));
    }

    handleInput(event) {
        this[event.target.dataset.field] = event.target.value;
    }

    async handleCreate(event) {
        const mode = event.target.dataset.mode;
        this.busy = true;
        this.error = undefined;
        try {
            this.dmlResult = await createRecord({
                name: this.newName,
                publicNote: this.newPublic,
                secretNote: this.newSecret,
                mode
            });
            await this.loadContext();
        } catch (e) {
            this.error = this.reduceError(e);
        } finally {
            this.busy = false;
        }
    }

    async handleRunAll() {
        this.busy = true;
        this.error = undefined;
        try {
            this.results = await runAllScenarios();
            this.lastRunAt = new Date().toLocaleString();
        } catch (e) {
            this.error = this.reduceError(e);
        } finally {
            this.busy = false;
        }
    }

    reduceError(e) {
        return e?.body?.message || e?.message || JSON.stringify(e);
    }
}

6. 通常ユーザーを作成します。Access_Mode_Demo には作成権限がなく、Secret_Note__c には読み取り権限がありません。

テスト結果の表示:

1.DML シナリオ

1. ユーザーモードで「新規」をクリックします。ユーザー モードが使用されているため、エラーが報告されます: Access_Mode_Demo__c では DML 操作 INSERT は許可されていません。

バックグラウンド DML は主にユーザー rec として挿入を実行します。//rec はこの Access_Mode_Demo のレコードです

画像

2. Access_Mode_Demo__c が新しい権限を作成できるように権限セットを追加します。 「ユーザーモード」をクリックして、新しい権限を作成します。ユーザー モードが使用されているため、エラーが報告されます。System.DmlException:Sobject Access_Mode_Demo__c のフィールドにアクセスできないため、操作が失敗しました。Exception または Result でエラーを確認してください。

画像

3. システムモードで「新規」をクリックします。システムモードを使用しているため、作成は成功します。

バックグラウンド DML は主にシステム記録として挿入を実行します。 //rec はこの Access_Mode_Demo のレコードです

画像

2. クエリシナリオ

クエリシナリオには主に以下の組み合わせが含まれます。以下の図の配置と組み合わせからわかるように、API バージョンが 68.0 の場合、ユーザーモードにより、SOQL の特定の項目に対するクエリ権限がない場合、エラーが報告されます。 System.QueryException、

シーン アクセスモード 共有 SOQL 結果 質問欄
アクセス モードが宣言されていません (クラス API 66.0) システムモード 共有なし

SELECT ID、名前、Public_Note__c、Secret_Note__c、Owner.Name

FROM Access_Mode_Demo__c ORDER BY CreatedDate

成功
アクセス モードが宣言されていません (クラス API 68.0) ユーザーモード 共有なし

SELECT ID、名前、Public_Note__c、Secret_Note__c、Owner.Name

FROM Access_Mode_Demo__c ORDER BY CreatedDate

失敗 · System.QueryException Access_Mode_Demo__c.Secret_Note__c
WITH USER_MODE (Secret_Note__c を含む; クラス API 68.0) ユーザーモード 共有あり

SELECT ID、名前、Public_Note__c、Secret_Note__c、Owner.Name

FROM Access_Mode_Demo__c WITH USER_MODE ORDER BY CreatedDate

失敗 · System.QueryException Access_Mode_Demo__c.Secret_Note__c
WITH USER_MODE (許可されたフィールドのみをクエリします。クラス API 68.0) ユーザーモード 共有あり

SELECT ID、名前、Public_Note__c、Owner.Name

FROM Access_Mode_Demo__c WITH USER_MODE ORDER BY CreatedDate

成功
WITH SYSTEM_MODE + 共有なし(类 API 68.0) システムモード 共有なし

SELECT ID、名前、Public_Note__c、Secret_Note__c、Owner.Name

FROM Access_Mode_Demo__c WITH SYSTEM_MODE ORDER BY CreatedDate

成功
WITH SYSTEM_MODE + 共有あり(类 API 68.0) システムモード 共有あり

SELECT ID、名前、Public_Note__c、Secret_Note__c、Owner.Name

FROM Access_Mode_Demo__c WITH SYSTEM_MODE ORDER BY CreatedDate

成功
Security.stripInaccessible(类 API 68.0) システム モード クエリ + 不正なフィールドの削除 共有あり
リスト 生 = [SELECT Id, Name, Public_Note__c, Secret_Note__c, Owner.Name

FROM Access_Mode_Demo__c WITH SYSTEM_MODE ORDER BY CreatedDate];

SObjectAccessDecision dec = Security.stripInaccessible(AccessType.READABLE, raw);

成功 結果セットには、Secret_Note__c は含まれません。

要約:

AI 時代において、Salesforce のセキュリティの重要性はますます高まっているため、最近のリリースでは多くのセキュリティ更新が強化されました。プロジェクト内のコードにアクセス許可のセキュリティに対する高度な要件がある場合は、ユーザー モードを検討するか、stripInaccessible メソッドでシステム モードを使用して、ユーザー レベルのアクセス許可と操作を実装し、データ操作を最小限の範囲に制御することができます。後で apex を作成する前に、プロジェクトのアーキテクチャがシステム モードとユーザー モードのどちらで実行するのに適しているかについても検討してください。シナリオごとに異なる API バージョン (66 以前/66 以降) が使用されます。

参考ドキュメント:



Source link

Leave a Reply

Your email address will not be published. Required fields are marked *

தீவிர சூறாவளி புயல் 'முரசு' சென்னையில் நாளை பள்ளிகளுக்கு விடுமுறை 'முரசு' புயல் குறித்த தகவல்கள் சென்னையில் நாளை பள்ளிகளுக்கு விடுமுறையா? வானிலை சென்னையில் நாளை விடுமுறையா? 'முரசு' புயல் வானிலை சென்னையில் புயல் 'முரசு' புயல் கண்காணிப்பு சென்னையின் வானிலை தெலுங்கானா சென்னை வானிலை புயல் கண்காணிப்பு செங்கல்பட்டில் இன்று பள்ளிகளுக்கு விடுமுறை சென்னையில் பள்ளிகளுக்கு விடுமுறை செங்கல்பட்டில் பள்ளிகளுக்கு விடுமுறை விஜயவாடா வானிலை வானிலை அறிக்கை 'முரசு' புயல் கண்காணிப்பு இன்றைய வானிலை அறிக்கை சென்னையில் நாளை பள்ளிகளுக்கு விடுமுறை சென்னை வானிலை சென்னையில் இன்று பள்ளிகளுக்கு விடுமுறையா? இன்றைய சென்னை வானிலை விமான சேவைகள் தமிழ் பக்தாகோர் (Pakhtakor) vs அல்-நாசர் (Al-Nassr) பக்தாகோர் எஃப்சி vs அல்-நாசர் எஃப்சி தரவரிசை ஏஎஃப்சி (AFC) சாம்பியன்ஸ் லீக் பக்தாகோர் எஃப்சி vs அல்-நாசர் எஃப்சி அணிகளின் விவரம் ஜோவா ஃபெலிக்ஸ் (João Félix) அல்-நாசர் எஃப்சி பக்தாகோர் எஃப்சி நியூ இங்கிலாந்து (New England) vs இன்டர் மியாமி (Inter Miami) நியூ இங்கிலாந்து ரெவல்யூஷன் vs இன்டர் மியாமி தரவரிசை எம்எல்எஸ் (MLS) இன்டர் மியாமி லியோனல் மெஸ்ஸி இன்டர் மியாமி சிஎஃப் இன்டர் மியாமி vs நியூ இங்கிலாந்து நியூ இங்கிலாந்து ரெவல்யூஷன் vs இன்டர் மியாமி அணிகளின் விவரம் மெஸ்ஸியின் மொத்த கோல்கள் நியூ இங்கிலாந்து ரெவல்யூஷன் vs இன்டர் மியாமி போட்டி நிகழ்வுகள் இன்டர் மியாமி போட்டிகள் நியூ இங்கிலாந்து ரெவல்யூஷன் நியூ இங்கிலாந்து ரெவல்யூஷன் vs இன்டர் மியாமி ஓமன் vs நெதர்லாந்து ஓமன் தேசிய கிரிக்கெட் அணி vs நெதர்லாந்து தேசிய கிரிக்கெட் அணி தரவரிசை ஓமன் vs நெதர்லாந்து ஓமன் தேசிய கிரிக்கெட் அணி vs நெதர்லாந்து தேசிய கிரிக்கெட் அணி போட்டி ஸ்கோர்கார்டு ஐசிசி (ICC) சிடபிள்யூசி (CWC) லீக் 2 ஓமன் தேசிய கிரிக்கெட் அணி vs நெதர்லாந்து தேசிய கிரிக்கெட் அணி தரவரிசை ஓமன் தேசிய கிரிக்கெட் அணி vs நெதர்லாந்து தேசிய கிரிக்கெட் அணி போட்டி ஸ்கோர்கார்டு நவம்பர் 2 - தங்கம் மற்றும் வெள்ளி விலை நிலவரம் வெள்ளி மற்றும் தங்கத்தின் விலைகள் தங்கம் குறித்த செய்திகள் இன்றைய தங்க விலை பெங்களூருவில் இன்றைய தங்க விலை ஹைதராபாத்தில் இன்றைய தங்க விலை கொல்கத்தாவில் இன்றைய தங்க விலை சென்னையில் இன்றைய தங்க விலை இன்றைய தங்கம் மற்றும் வெள்ளி விலை பெங்களூருவில் இன்றைய தங்க விலை சென்னையில் இன்றைய தங்க விலை இன்றைய தங்க விலை தனிஷ்க் (Tanishq) தங்க விலை வெள்ளி - எம்சிஎக்ஸ் (MCX) நேரலை விலை பெங்களூருவில் இன்றைய வெள்ளி விலை கோயம்புத்தூரில் இன்றைய தங்க விலை இன்றைய தங்கம் விலை தங்கத்தின் விலைகள் சென்னையில் இன்றைய தங்க விலை இன்றைய தங்க விலை இன்றைய வெள்ளி விலை MCX தங்கம் MCX தங்க விலை விலைமதிப்பற்ற உலோகம் மும்பையில் இன்றைய தங்க விலை ஹைதராபாத்தில் இன்றைய தங்க விலை டெல்லியில் இன்றைய தங்க விலை ஹைதராபாத்தில் இன்றைய தங்க விலை MCX தங்க விலை சென்னையில் தங்க விலை பெங்களூருவில் இன்றைய தங்க விலை MCX வெள்ளி விலை MCX மலபார் தங்க விலை இன்றைய MCX தங்க விலை இன்றைய 22 காரட் தங்க விலை இன்றைய 22K தங்க விலை நேரலை தங்க விலை இன்றைய 18 காரட் தங்க விலை கிமி அன்டோனெல்லி கிமி F1 தரவரிசை F1 F1-ன் மிக இளம் சாம்பியன் கிமி அன்டோனெல்லி ஃபார்முலா 1 F1 ஓட்டுநர் தரவரிசை F1 பந்தயம் F1 நேரலை F1 மெக்சிகோ கிராண்ட் பிரிக்ஸ் F1 2026 மேக்ஸ் வெர்ஸ்டாப்பன் F1 முடிவுகள் மெக்சிகோ கிராண்ட் பிரிக்ஸ் F1 மெக்சிகோ GP ரிலையன்ஸ் பங்கு விலை ரிலையன்ஸ் பங்கு ரிலையன்ஸ் RIL பங்கு விலை ரிலையன்ஸ் இண்டஸ்ட்ரீஸ் பங்கு விலை RIL பங்கு RIL பங்கு விலை உயர்வு குறித்த பகுப்பாய்வு ரிலையன்ஸ் இண்டஸ்ட்ரீஸ் பங்கு அனுஜ் தாக்கூர் நடிகர் அனுஜ் தாக்கூர் வலிப்பு (seizure) தூக்கம் விமானம் விபத்து புளோரிடா விமான விபத்து லியர்ஜெட் (Learjet) லியர்ஜெட் 60 வீடியோக்கள் புளோரிடா ஆஸ்டின் விபத்து இன்றைய செய்திகள் ரிமி டோமி லிபின் மேத்யூ - ரிமி டோமி ரிமி டோமி திருமணம் திருமணம் லிபின் மேத்யூ ரிமி டோமி டேலைட் சேவிங்ஸ் (Daylight Savings) 2026 டேலைட் சேவிங்ஸ் அமெரிக்காவில் டேலைட் சேவிங்ஸ் 2026 அமெரிக்காவில் டேலைட் சேவிங்ஸ் 2026 டேலைட் சேவிங்ஸ் அமெரிக்காவில் டேலைட் சேவிங்ஸ் 2026 ஷாருக்கான் பிறந்தநாள் ஷாருக்கான் ஷாருக்கான் பிறந்தநாள் கிங் (King) SRK பிறந்த தேதி சித்தார்த் ஆனந்த் SRK வயது ஷாருக் இந்தித் திரைப்படம் கேரள லாட்டரி முடிவுகள் கேரள லாட்டரி முடிவு இன்றைய கேரள லாட்டரி முடிவு இன்றைய பாக்யதாரா லாட்டரி முடிவு இன்றைய லாட்டரி முடிவு இன்றைய கேரள லாட்டரி முடிவு (நேரலை) இன்றைய கேரள லாட்டரி முடிவு இன்றைய கேரள லாட்டரி முடிவு (மதியம் 3 மணி) லாட்டரி இன்றைய முடிவு கேரள லாட்டரி முடிவு - இன்று பிற்பகல் 3 மணி (நேரலை) கேரள லாட்டரி முடிவு - நேற்று サイクロン「ムラス(Murasu)」 チェンナイの学校、明日は休校か サイクロン「ムラス」最新情報 チェンナイの学校は明日休みか? 天気 チェンナイは明日休みか? サイクロン「ムラス」の気象情報 チェンナイのサイクロン サイクロン「ムラス」追跡 チェンナイの天気 テランガーナ州 チェンナイの天気 サイクロン追跡 チェンガルパットゥの学校は今日休校か チェンナイの学校休校情報 チェンガルパットゥの学校休校情報 ビジャヤワーダの天気 天気予報 サイクロン「ムラス」追跡 今日の天気予報 チェンナイの学校、明日は休校 チェンナイの天気 チェンナイの学校は今日休みか? 今日のチェンナイの天気 フライト情報 タミル語 パフタコール対アル・ナスル パフタコールFC対アル・ナスル:順位表 AFCチャンピオンズリーグ パフタコールFC対アル・ナスル:出場メンバー ジョアン・フェリックス アル・ナスルFC パフタコールFC ニューイングランド対インテル・マイアミ ニューイングランド・レボリューション対インテル・マイアミ:順位表 MLS(メジャーリーグサッカー) インテル・マイアミ リオネル・メッシ インテル・マイアミCF インテル・マイアミ対ニューイングランド ニューイングランド・レボリューション対インテル・マイアミ:出場メンバー メッシの通算ゴール数 ニューイングランド・レボリューション対インテル・マイアミ:試合経過 インテル・マイアミの試合 ニューイングランド・レボリューション ニューイングランド・レボリューション対インテル・マイアミ オマーン対オランダ オマーン代表クリケットチーム対オランダ代表クリケットチーム:順位表 オマーン対オランダ オマーン代表クリケットチーム対オランダ代表クリケットチーム:試合スコアカード ICCクリケット・ワールドカップ・リーグ2 オマーン代表クリケットチーム対オランダ代表クリケットチーム:順位 オマーン代表クリケットチーム対オランダ代表クリケットチーム:試合スコアカード 11月2日の金・銀価格 銀・金価格 金に関するニュース 今日の金価格 バンガロールの今日の金価格 ハイデラバードの今日の金価格 コルカタの今日の金価格 チェンナイの今日の金価格 今日の金・銀価格 バンガロールの今日の金価格 チェンナイの今日の金価格 今日の金価格 タニシュク(Tanishq)の金価格 銀のMCX(インド商品取引所)ライブ価格 バンガロールの今日の銀価格 コインバトールの今日の金価格 今日の金価格価格 金価格 チェンナイの今日の金価格 今日の金価格 今日の銀価格 MCX(インド商品取引所)金価格 MCX金価格 貴金属 ムンバイの今日の金価格 ハイデラバードの今日の金価格 デリーの今日の金価格 ハイデラバードの今日の金価格 MCX金価格 チェンナイの金価格 バンガロールの今日の金価格 MCX銀価格 MCX マラバール・ゴールド(Malabar Gold)の金価格 今日のMCX金価格 今日の22金(22カラット)価格 今日の22金価格 金価格(リアルタイム) 今日の18金(18カラット)価格 キミ・アントネッリ キミ F1順位表 F1 キミ・アントネッリ(最年少F1チャンピオン候補) フォーミュラ1 F1ドライバー順位表 F1レース F1ライブ F1メキシコグランプリ F1 2026年シーズン マックス・フェルスタッペン F1結果 メキシコグランプリ F1メキシコGP リライアンス(Reliance)の株価 リライアンス株 リライアンス RIL株価 リライアンス・インダストリーズの株価 RIL株 RIL株価急騰の分析 リライアンス・インダストリーズ株 アヌジ・タクール 俳優アヌジ・タクール 発作 睡眠 飛行機墜落事故 フロリダでの飛行機墜落事故 リアジェット リアジェット60 動画 フロリダ オースティンでの事故 今日のニュース リミ・トミー リビン・マシュー、リミ・トミー リミ・トミーの結婚 結婚 リビン・マシュー リミ・トミー 2026年のサマータイム(デイライト・セービング) サマータイム 2026年米国のサマータイム 2026年米国サマータイム サマータイム 2026年米国サマータイム シャー・ルク・カーンの誕生日 シャー・ルク・カーン シャー・ルク・カーンの誕生日 キング SRK(シャー・ルク・カーン)の誕生日 シッダールト・アーナンド SRKの年齢 シャー・ルク ヒンディー語映画 ケーララ州宝くじの結果 ケーララ州宝くじ結果 今日のケーララ州宝くじ結果 今日の「バギャターラ(Bhagyathara)」宝くじ結果 今日の宝くじ結果 今日のケーララ州宝くじ結果(ライブ) 今日のケーララ州宝くじ結果 今日のケーララ州宝くじ結果(午後3時) 宝くじ本日の結果 ケーララ宝くじ 本日の結果(午後3時ライブ) ケーララ宝くじ 昨日の結果 তীব্র ঘূর্ণিঝড় 'মুরাসু' চেন্নাইতে আগামীকাল স্কুল ছুটি ঘূর্ণিঝড় 'মুরাসু' সংক্রান্ত আপডেট চেন্নাইতে আগামীকাল স্কুল ছুটি কি না আবহাওয়া চেন্নাইতে কি আগামীকাল ছুটি? ঘূর্ণিঝড় 'মুরাসু' ও আবহাওয়া চেন্নাইতে ঘূর্ণিঝড় ঘূর্ণিঝড় 'মুরাসু' ট্র্যাকার চেন্নাইয়ের আবহাওয়া তেলেঙ্গানা চেন্নাইয়ের আবহাওয়া ঘূর্ণিঝড় ট্র্যাকার চেঙ্গলপাট্টুতে আজ স্কুল ছুটি চেন্নাইতে স্কুল ছুটি চেঙ্গলপাট্টুতে স্কুল ছুটি বিজয়ওয়াড়ার আবহাওয়া আবহাওয়া প্রতিবেদন ঘূর্ণিঝড় 'মুরাসু' ট্র্যাকার আজকের আবহাওয়া প্রতিবেদন চেন্নাইতে আগামীকাল স্কুল ছুটি চেন্নাইয়ের আবহাওয়া চেন্নাইতে আজ স্কুল ছুটি কি না আজকের চেন্নাইয়ের আবহাওয়া বিমান চলাচল তামিল পাখতাকোর বনাম আল-নাসর পাখতাকোর এফসি বনাম আল-নাসর পয়েন্ট টেবিল এএফসি চ্যাম্পিয়ন্স লিগ পাখতাকোর এফসি বনাম আল-নাসর লাইনআপ জোয়াও ফেলিক্স আল-নাসর এফসি পাখতাকোর এফসি নিউ ইংল্যান্ড বনাম ইন্টার মায়ামি নিউ ইংল্যান্ড রেভোলিউশন বনাম ইন্টার মায়ামি পয়েন্ট টেবিল এমএলএস (MLS) ইন্টার মায়ামি লিওনেল মেসি ইন্টার মায়ামি সিএফ ইন্টার মায়ামি বনাম নিউ ইংল্যান্ড নিউ ইংল্যান্ড রেভোলিউশন বনাম ইন্টার মায়ামি লাইনআপ মেসির মোট গোল নিউ ইংল্যান্ড রেভোলিউশন বনাম ইন্টার মায়ামি টাইমলাইন ইন্টার মায়ামির ম্যাচসমূহ নিউ ইংল্যান্ড রেভোলিউশন নিউ ইংল্যান্ড রেভোলিউশন বনাম ইন্টার মায়ামি ওমান বনাম নেদারল্যান্ডস ওমান জাতীয় ক্রিকেট দল বনাম নেদারল্যান্ডস জাতীয় ক্রিকেট দল পয়েন্ট টেবিল ওমান বনাম নেদারল্যান্ডস ওমান জাতীয় ক্রিকেট দল বনাম নেদারল্যান্ডস জাতীয় ক্রিকেট দল ম্যাচ স্কোরকার্ড আইসিসি সিডব্লিউসি (CWC) লিগ ২ ওমান জাতীয় ক্রিকেট দল বনাম নেদারল্যান্ডস ক্রিকেট দল র‍্যাঙ্কিং ওমান জাতীয় ক্রিকেট দল বনাম নেদারল্যান্ডস ক্রিকেট দল ম্যাচ স্কোরকার্ড ২রা নভেম্বর সোনা ও রুপার দর সোনা ও রুপার দাম সোনা বিষয়ক খবর আজকের সোনার দর ব্যাঙ্গালোরে আজকের সোনার দর হায়দ্রাবাদে আজকের সোনার দর কলকাতায় আজকের সোনার দর চেন্নাইতে আজকের সোনার দর আজ সোনা ও রুপার দর ব্যাঙ্গালোরে আজকের সোনার দর চেন্নাইতে আজকের সোনার দর আজকের সোনার দর