mcpField
S
sasjs-framework

sasjs-framework

github.com/sasjs开发31.1k 次安装2 天前更新
代码仓库

关于 sasjs-framework

sasjs-framework 是构建 SASjs 应用的静态参考技能,说明如何组织 Web 前端与 SAS 后端。内容涵盖项目布局、配置、服务契约和质量实践,适用于部署到 SAS 9、Viya 或 SASjs server 的应用。

主要功能

  1. 01标准应用布局
  2. 02按目标配置项目
  3. 03前端与 SAS 服务契约
  4. 04输入验证指引
  5. 05多目标代码组织
  6. 06测试与 lint 实践

适用场景

  1. 01规划 SASjs 应用结构
  2. 02配置面向多个目标的部署
  3. 03设计前端与 SAS 服务的数据交换
  4. 04检查输入安全和测试实践

安装

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

$npx skills add https://github.com/sasjs/skills --skill sasjs-framework

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

附带文件sasjsconfig-schema.json

详细介绍

SASjs Framework - 构建 SASjs 应用

SASjs 应用 = Web 前端(可使用任意框架:Angular、React、原生)+ 按标准布局组织的 SAS 后端代码,由 @sasjs/cli 编译并部署到 SAS 9、Viya 或 SASjs 服务。前端通过 @sasjs/adapter 与 SAS 通信;后端服务通过 _webout 返回 JSON。

标准项目布局

sasjs/
  sasjsconfig.json     # project + target configuration
  macros/              # project-specific macros (macroFolders)
  services/            # web services called from the frontend
  jobs/                # jobs (scheduled / flow / long-running)
  programs/            # plain programs (initProgram, termProgram, utilities)
  db/                  # DDL + static data per library (sasjs db)
  tests/               # tests run by `sasjs test`
  mocks/               # mock responses for offline frontend dev (syncFolder)
  doxy/                # extra doxygen content for `sasjs doc`

sasjsconfig.json

根配置文件保存默认值;targets[] 中的每个条目都可以覆盖这些默认值。主要部分:

  • macroFolders、binaryFolders - CLI 查找宏和二进制文件的位置
  • serviceConfig.serviceFolders - 服务源文件夹;initProgram 在每个服务运行前执行(用于设置库名、选项)
  • jobConfig.jobFolders - 作业源文件夹
  • programFolders - 随应用编译和部署的程序
  • streamConfig - streamWeb: true 会将构建后的前端流式传输到 SAS,使其由平台自身提供服务(无需单独的 Web 服务器);webSourcePath 指向前端构建输出目录
  • syncFolder - 同步到服务端的文件夹(例如 mocks)
  • testConfig - 用于 sasjs test 的初始化/终止程序
  • targets[] - 按环境覆盖配置:serverUrl、serverType(SAS9/SASVIYA/SASJS)、appLoc(部署根目录,例如 /Public/app/myapp)、特定目标的 macroFolders(例如用于平台适配层的 targets/viya/macros_viya)、httpsAgentOptions、deployConfig

完整的 JSON Schema 随附于此文件旁的 sasjsconfig-schema.json 中;请依据该文件验证配置变更。通过 "$schema": "https://cli.sasjs.io/sasjsconfig-schema.json" 引用它。

Viya 上的流式前端文件(MIME 类型)

当 streamWeb: true 时,CLI 会使用 %mv_createfile 宏,将前端(index.html,按 streamServiceName 重命名,另包括 css/js)上传到 Viya Files 服务。该宏会以非常特定的方式创建文件,以确保文件能够正确流式传输:

  • 向 /files/files 发送 POST 请求,内容类型根据扩展名确定(%mf_mimetype)
  • 设置 typeDefName=file_html(通过 %mv_getViyaFileExtParms),以便将文件识别为 HTML
  • 对 HTML/SVG 发送不含 attachment 的 Content-Disposition,使其能在浏览器中呈现

绝不要通过 filename filesrvc 文件引用和 data step 重写来原位更新流式前端文件——Files 服务随后会将其视为通用 blob,导致 MIME 类型丢失,应用也就无法继续流式传输(浏览器会下载文件或显示原始文本)。若要在运行时修改流式文件(例如修补 HTML 中的 compute contextname),请读取文件(filesrvc 文件引用可用于读取),将修改后的内容写入临时文件引用,然后使用 %mv_createfile(path=..., name=..., inref=...) 重新创建文件(该操作会删除旧文件,并以正确的 MIME 类型重新 POST 文件)。

服务契约(前端 <-> SAS)

  1. 适配器通过 services/<folder>/<name> 发送 POST 请求,并附带输入表(对象数组)→ 工作数据集以 JS 键命名。
  2. 服务 SAS 代码在 initProgram 之后运行;它读取输入、执行操作,并将输出 JSON 写入 _webout。
  3. 使用 @sasjs/core 宏的常见模式:
/**
  @file
  @brief Example service returning data
  <h4> SAS Macros </h4>
  @li mp_jsonout.sas
  @li mp_abort.sas
**/

/* validation / logic here */

%mp_jsonout(OPEN)
%mp_jsonout(OBJ,results,dslabel=results)
%mp_jsonout(CLOSE)
  1. 标头中的 <h4> SAS Macros </h4> @li 条目不是文档,而是编译器的依赖项清单。服务调用的每个宏都必须列在其中,否则不会内联到构建中,部署的作业将在运行时失败,并报错 Apparent invocation of macro X not resolved。如果某个已声明宏的 @li 依赖链恰好内联了未声明的调用(传递解析),未声明的调用可能会侥幸通过测试,但当该宏的依赖项发生变化时就会出错。编辑现有服务时,请重新核对正文中的 %macro() 调用与标头条目,并声明所有缺失项,包括 mf_getuser.sas 这类小型辅助宏。

  2. 出错时,使用 %mp_abort(...)(mf_abort 已弃用)干净地中止,以便适配器在 JSON 中收到结构化错误,而不是不完整的响应。不要在 %if/%else 块内调用 %mp_abort,因为宏处理器可能会在中止后继续执行。请改用 iftrue= 参数进行条件判断,例如:

%mp_abort(iftrue= (%mf_existds(work.results)=0)
  ,mac=&_program
  ,msg=%str(No results found)
)

如果中止发生在 %include 块内,SAS 无法干净地退出到 _webout;在 include 之后调用 %mp_abort(mode=INCLUDE)(不要放在任何宏封装内),它会检查 work.mp_abort_errds 中的中止状态。

清理服务输入(SAS 宏注入)

所有来自 Web 请求的值(输入表、URL 参数)都由攻击者控制。如果这些值在生成的代码中解析为宏变量——例如 libname 路径、set/from 语句、where 子句或 %include——那么空格、引号、分号以及 &/% 宏触发字符就会成为经典的 SAS 注入攻击媒介。

规则:

  • 在任何 call symputx 之前,先在数据步中使用 @sasjs/core 的 %mp_validatecol(incol,RULE,outcol) 验证输入。仅在验证通过时执行 symput,否则中止(%mp_abort(iftrue=...))。规则包括 ISLIB(有效 libref)、ISNAME(有效 SAS 名称,例如成员/表名称)、LIBDS(LIBREF.DATASET)、FORMAT、ISINT、ISNUM。
  • 如果存在白名单,请使用白名单:语法验证之后,在控制表中确认该值存在(例如通过主键查找),再用它构建路径或代码。
  • 从控制表读取的值也可能被篡改,因此在将其传入生成代码的宏之前,同样要验证这些值。
  • 对于必须回显到代码中的自由文本值(例如用户消息),请去除宏触发字符:使用 compress(value,'&%;') 或对每个危险字符使用 tranwrd。但最好不要将自由文本放入代码中。
%let libds=0;
data _null_;
  set work.sascontroltable;
  %mp_validatecol(libds,LIBDS,is_libds)
  if is_libds=1 then call symputx('libds',libds);
  else putlog 'ERR' 'OR: invalid libds: ' libds;
run;
%mp_abort(iftrue= ("&libds"="0")
  ,mac=&_program
  ,msg=%str(Invalid libds provided)
)

多目标规范

  • 在共享文件夹中保持后端代码与平台无关;将特定于平台的垫片放入 targets/<name>/macros_* 文件夹,并仅在相应目标上注册。
  • @sasjs/core 中提供了平台能力宏(mm_* 元数据、mv_* Viya、ms_* server),不要手动按服务器类型进行分支处理。

质量门禁(遵循 Data Controller 等成熟应用的惯例)

  • 修改任何 .sas 文件后,都要运行 sasjs lint;修复所修改文件中的所有警告。
  • 代码检查器会强制要求所有位置使用 2 个空格缩进,包括 /* ... */ 块注释中的续行——注释文本绝不能使用 3 个或更多空格对齐。
  • 后端逻辑有变更时,要添加测试并运行 sasjs test。测试宏时,始终使用 %mp_assertscope(SNAPSHOT) / %mp_assertscope(COMPARE, ...) 包裹被测宏,以捕获宏变量作用域泄漏;并使用 %macro 包裹任何包含平台分支的代码(测试程序中不能出现未封装的条件宏代码)。
  • 在 sasjs/mocks 中提供模拟数据,以便无需连接实时 SAS 服务器也能开发前端。
  • 绝不自动提交或提升版本;发布由流水线驱动(遵循约定式提交)。
  • Markdown 文件:不要硬换行——每个段落占一行。
  • 应用必须支持离线和本地部署:前端包中不得包含外部 CDN 资源。

测试必须具备幂等性

测试文件必须能够重复运行并通过(包括在上次运行中途失败后)。

  • 文件开头使用 %let syscc=0;——许多 DC 宏在 &syscc>0 时会在入口处中止,而且先前测试中的任何 WARNING 都会将 syscc 增至 4。
  • 准备步骤要有防护:先删除再插入配置记录(处理上次中止运行遗留的数据),并重新创建物理表,而不要假设这些表不存在。

参考实现

查看现有应用,了解相关模式:文件夹布局、sasjsconfig.json 多目标配置、服务结构、流式构建,以及测试和模拟数据惯例,例如:

局限

此技能是用于构建 SASjs 应用的静态参考资料——提供有关项目结构、配置和服务契约的指导。它不会执行代码、运行 shell 命令、访问文件系统、连接数据库或发起网络请求。对文件路径、构建输出和部署目标的引用仅用于描述 SASjs 项目的结构——此技能本身不会创建、修改或访问这些路径。

评论

支持 Markdown