DEX455

Apex Web 服务完全指南:SOAP Web 服务与 Apex REST

课程介绍

Apex Web服务完全指南:SOAP(webservice关键字/始终系统上下文不执行FLS和共享规则/global static/Generate WSDL/不能用于接口触发器内部类/Map/Set/Pattern/Exception不能作为参数和返回值/托管包不可弃用/禁止重载)→Apex REST(@RestResource urlMapping/@HttpGet/@HttpPost/@HttpPut/@HttpDelete/@HttpPatch/RestContext.request+response/JSON和XML双格式/OAuth2.0+SessionID认证/参数序列化规则:无参数→requestBody,有参数→反序列化/null字段不序列化/@HttpGet/@HttpDelete必须无参数/每类每种HTTP方法只能一个/允许的参数类型:原始类型+sObject+List/Map+用户定义类型/transient不序列化/循环引用HTTP400/Boolean值规则/XML不支持List等集合/HTTP状态码200/204/400-500完整对照/堆溢出部分JSON错误)、安全模型(API67.0+默认用户模式vs SOAP系统上下文/WITH SYSTEM_MODE绕过/without sharing绕过)、CRUD完整代码示例(含cURL命令)和RestRequest文件上传示例。

课程章节

本课程共有 12 个章节

  • 1

    Exposing Apex as SOAP and REST Web Services

    第 251 页

    同学们,今天我们来聊一个特别实用的主题——怎么把你的Apex代码变成对外的Web服务,让外部的应用程序也能调用你Salesforce里的逻辑和数据。 想象一下,你的Salesforce里有一个很强大的业务功能,比如计算折扣、查询库存,或者处理订单。现在,公司另一个系统,比如一个电商网站或者手机App,也想用这个功能。怎么办呢?我们就可以把Apex方法包装成Web服务,让外部系统通过标准的网络调用来访问。 这里有两种主流的方式:SOAP Web服务和REST Web服务。听起来有点专业,但其实很好理解。我们先说SOAP方式。 SOAP,你可以把它想象成一种非常正式的、带信封的邮寄方式。你要发一封信,必须有固定的格式:信封上要写清楚寄件人、收件人,信纸里的内容也要按照规定的XML结构来写。在Salesforce里,你只需要在一个Apex方法前面加上一个特殊的注解,就像贴了一张“SOAP服务”标签。这样,Salesforce就会自动帮你生成一个WSDL文件——这相当于服务的说明书,告诉外部系统怎么调用,需要传什么参数,返回什么结构。外部系统拿着这个说明书,就可以用SOAP协议来调用了。优点是结构严谨,支持强类型,适合企业级系统的对接。 再来看REST方式。REST更像是一种随性的明信片。不需要复杂的信封,你可以用简单的HTTP请求,比如GET、POST这些我们平时上网就在用的方法,再加上一个URL,就能访问服务。在Apex里,我们用Apex REST注解,把一个类标记为REST资源,然后里面的方法对应不同的HTTP动作,比如@HttpGet表示获取数据,@HttpPost表示创建数据。这种方式更轻量,适合移动端、网页前端这些场景,数据格式通常用JSON,更容易阅读和处理。 这两者都是我们Salesforce平台开放出去的能力。所以当有人问:“我的外部系统能不能用你Salesforce里的逻辑?”你就可以自信地回答:当然可以,我们有APEX Web服务,SOAP和REST随你选。 简单总结一下:SOAP像正式公文,严谨但笨重;REST像聊天消息,灵活又轻便。具体用哪个,看对方系统的习惯和你的场景需求。下一节课我们会深入每个方式的代码写法,现在你只需要记住这张Slide的核心思想:Apex代码可以通过两种方式暴露成Web服务,让你的Salesforce能力走出平台,连接万物。 好了,这就是今天的内容,我们下次见。

    查看详情
  • 2

    SOAP — Webservice Methods & WSDL Generation

    第 252 页

    同学们,咱们今天来讲讲,怎么把Apex类里的方法,变成一个可供外部调用的Web服务。这就要用到,webservice,这个关键字了。 你可以这样想:你写了一个很有用的方法,比如自动给客户发邮件,但外面的系统也想用这个功能,怎么办?就在这个方法前面加上,webservice,,它就成了一种叫“SOAP”的Web服务接口。外部的应用程序,不管是用Java、.NET还是别的语言写的,都能通过标准的SOAP协议来调用它,在Salesforce里帮你做事儿,比如创建个联系人、查一笔订单啥的。 那怎么对接呢?特别简单。你写好这个带webservice方法的类之后,进到Salesforce里这个类的详情页面,可以直接生成一个,WSDL文件,,也就是“Web服务描述语言”文件。你把这个文件丢给外部的开发小伙伴,他们用工具一导入,就能自动生成调用代码,基本不用手写,省时又省力。 不过这里有几个硬规矩,一定要记住: - 首先,包含webservice方法的这个类,必须声明为 ,global,,也就是全局的。要不外边根本看不见它。 - 其次,这些方法本身,必须是,静态的,,用`static`修饰。换句话说,调用时不用创建对象,直接类名.方法名就行。 - 再说个细节,,webservice,这个修饰符,它的可见性天生就是全局的。在Apex代码内部,任何能访问到这个类的代码,也都能直接调用这些方法,相当于自动拥有了“全局”权限。 最后,还有一个限制很重要——,webservice不能乱用,。你不能把它贴在类定义上,不能给内部类(也就是在一个类里面定义的类)的方法用,接口里不行,触发器里更不行。它就一个用途:,最外层类的静态方法,,仅此而已。 简单总结一下:想对外开放能力,就给外层类的静态方法加上webservice,类声明成global,然后生成WSDL文件交给外部系统,就搞定了。 是不是挺清晰的?

    查看详情
  • 3

    SOAP — Security & Data Exposure

    第 253 页

    同学们,今天这节课我们来聊聊自定义Web服务方法的安全问题。这个内容非常重要,因为稍不注意,就可能把敏感数据暴露出去。 首先你要记住一个核心点:你写的Apex Web服务方法,不管是老式的`webservice`关键字,还是现在的`@RestResource`注解,它们运行时默认是“系统上下文”。这是什么意思呢?就是说,代码运行时不使用当前调用用户的身份和权限,而是拥有整个组织的最高数据访问权限,就像超级管理员一样。不管这个用户本身有多受限,字段级安全、对象权限、共享规则,对它全都没用。所以,只要他能调用这个方法,方法里面就能查到所有他能“触达”不了的记录和字段。这非常的危险。 所以,作为开发者,我们必须主动负起责任,千万不要让敏感数据不小心流出去。那该怎么做呢?最直接的,就是在代码里手动检查权限。Salesforce提供了很便捷的方式来检查当前用户对对象和字段的访问级别。你可以拿到对象的描述结果,比如用`sObjectType.getDescribe()`,然后去看`isAccessible()`方法。对于字段,也可以通过字段的描述结果调用`isAccessible()`、`isUpdateable()`等等。更推荐的办法是使用`Security.stripInaccessible()`,它可以直接把用户没权限看的字段从查询结果里剔除掉。这样一来,你就能确保,即使方法运行在系统模式下,返回给用户的数据也不会超出他本来该看到的范围。 接下来,还有一个容易被误解的点,就是共享规则。有些同学可能会想:“我明明在类上加了`with sharing`呀,为什么共享规则好像没生效?”其实,默认情况下,自定义Web服务类如果没有声明,等同于`without sharing`,所以共享规则完全被

    查看详情
  • 4

    SOAP — webservice Keyword Rules & Restrictions

    第 254 页

    同学们,我们来看这页内容。 在Apex里写Web服务方法时,有一个重要的限制:参数和返回值不能用Maps、Sets,也不能用Pattern对象、Matcher对象,还有Exception这类的异常对象。为什么?因为SOAP协议里没有和它们对应的数据类型,就好像找不到匹配的“肥皂”(其实就是SOAP啦),所以传不了。 那需要这些数据时怎么办?可以把它们设计成类的成员变量,并用webservice关键字修饰这个类,这样成员变量就会暴露在服务定义文件里,也就是常说的WSDL。注意,这些成员变量一定要是实例变量,不要设成静态的,因为每次调用Web服务都是用的实例状态。 说到这,Salesforce提供了一个很好的示例:SpecialAccounts类。这个类里有一个专门用作Web服务成员变量的类,叫AccountInfo;还有一个DeliverCal方法会用到它;另外还有DeliverAccounts方法,厉害的是,这个方法可以在一次调用里就创建出父子记录和孙辈记录,效率很高。当然,它还包含了对应的测试类,用来验证整个创建逻辑是不是正确。 这样,通过类的实例成员变量,我们就绕开了SOAP不支持复杂类型的问题。好了,这部分内容就先到这里。

    查看详情
  • 5

    SOAP — Overloading Web Service Methods

    第 255 页

    我们来看一下关于Web服务方法重载的一个小知识点。 在讲Apex开发的时候,大家可能会想到,面向对象编程里,方法重载是很常见的做法,对吧?就是一个类里头,可以有多个名字相同但参数列表不同的方法。但是在Apex里,如果你要把一个方法暴露成Web服务,也就是加上`webservice`关键字,那可就不能这么干了。 为什么呢?这背后其实和SOAP以及WSDL的技术特性有关。SOAP协议和WSDL描述文件,对于方法重载的支持本来就不是很好。所以Apex就干脆做了一个限制:,在同一个类里,所有标记了`webservice`关键字的方法,名字不能重复,。如果你硬要写两个同名的webservice方法,哪怕参数不同,编译器也会直接报错,不让你通过。 那万一咱们的业务里真的需要根据不同的参数来做不同的事,该怎么办呢?很简单,给这些方法取不同的名字就行了。比如原来的`updateRecord(String id)`和`updateRecord(String id, Map fields)`,你可以改成`updateRecordById`和`updateRecordWithFields`。虽然方法名不一样了,但逻辑还是可以复用的。 最后记住,这个限制只针对`webservice`方法哦。普通Apex方法,就是没有加`webservice`关键字的那些,依然可以正常重载,不受任何影响。这样一来,在内部代码里该怎么写还怎么写,只是对外暴露的接口需要多花点心思给方法起个清晰的名字。 这样解释是不是就很好理解啦?

    查看详情
  • 6

    Apex REST — Introduction & RestContext

    第 256 页

    各位同学,咱们今天来看看怎么用Apex写一个REST风格的Web服务,也就是让外部系统能够通过标准的HTTP方式来调用我们Salesforce里的逻辑。 首先,你需要把一个Apex类变成一个REST接口,这很简单,只要在类的声明前面加一个注解:`@RestResource`,然后在括号里写上`urlMapping`,也就是这个接口的访问路径。比如我写`@RestResource(urlMapping='/myApi/*')`,那别人就能通过末尾是 `/myApi/` 的地址来调用这个类。路径里这个星号就是通配符,可以匹配后面任意层级的子路径。 光有类还不够,还得指定它可以接收哪些HTTP方法。我们可以在类里的方法上继续加注解,比如 `@HttpGet`、`@HttpPost`、`@HttpPut`、`@HttpDelete`,分别对应查询、创建、更新和删除操作。一个类里可以有多个这样的方法,只要HTTP注解不同就行。 那在方法内部怎么拿到客户端发来的请求,又怎么返回结果呢?系统提供了一个叫 `RestContext` 的“工具箱”,它有两个静态成员:`request` 和 `response`。`RestContext.request` 这个对象里装满了请求的所有信息:你可以读取URL里带的参数、请求头、请求体(也就是JSON或XML内容),还有HTTP方法本身。而 `RestContext.response` 这个对象则让你来组装要返回给客户端的东西,比如设置响应状态码,比如200成功或404找不到,也可以往响应体里写字符串,还可以添加自定义的响应头。这就像你接待一位访客,request是访客递来的信件,response是你写好要交给对方的回信。 使用的时候有两个容量上的限制要特别注意:单个请求体不能超过6 MB,这是Salesforce为了保护系统定的;而一次响应的内容最大可以有12 MB。如果你传进来的数据太大或者想返回的数据超了限制,调用就会失败,所以设计接口时一定要控制好数据量。 关于安全认证,Apex REST服务支持标准OAuth 2.0认证,这也是推荐的现代方式。当然,你也依然可以使用传统的会话ID(Session ID)来验证身份,只要在请求头里带上就行。 最后要记住URL映射是区分大小写的,`/Account` 和 `/account` 是两个完全不同的地址。如果你在映射里定义了精确的路径,同时又有一个带星号的模糊匹配,那系统会优先匹配精确的那个。比如你同时定义了`/myApi/hello`和`/myApi/*`,那请求`/myApi/hello`时,会进精确的那个方法,而不是走到通配符里。 这一页的内容就这些,大家可以动手试试,写一个简单的查询接口出来,很快就能感受到Apex REST有多么简洁直接了。

    查看详情
  • 7

    Apex REST — Annotations & Methods

    第 257 页

    好,同学们,咱们今天来聊聊 Apex REST 这块儿,它怎么让你自己写的 Apex 类变成一个 RESTful 的 Web 服务。听起来有点陌生?没关系,我一步一步给你讲透。 首先,你想让外部系统能通过 HTTP 调到你 Salesforce 里的代码,你就得在类上面加一个注解,叫 `@RestResource`。这个注解得放在类的第一行,里面还要配一个 `urlMapping`,比如 `'/myService/*'`,这意思就是:外面的人访问 “你的实例名/services/apexrest/myService/...” 这个路径时,请求就会被交给你这个类来处理。注意哦,类名可以随便起,但映射的路径是你自己定义的。 然后,类里面你要写方法,不同 HTTP 动词对应不同的处理逻辑。怎么做呢?很简单,在每个方法头上再分别加上对应的注解: - 查数据用 `@HttpGet` - 新增数据用 `@HttpPost` - 更新数据用 `@HttpPut` - 删除数据用 `@HttpDelete` - 部分更新用 `@HttpPatch` 这几个注解就是告诉 Salesforce:当外面用 GET 请求过来,就找带 `@HttpGet` 的方法;POST 请求找 `@HttpPost` 的,以此类推。 注意,这里面有几条硬规矩你必须遵守: - 所有这些方法必须声明成 `global static`,也就是全局静态的。因为平台要直接调用它们,没有实例化这一步。 - 对于 `@HttpGet` 和 `@HttpDelete` 方法,它们不能接收任何参数,参数是通过 URL 里带过来的,我们一般用 `RestContext.request.params` 去取。 - 那 POST、PUT、PATCH 这些方法,想拿到请求体里的数据怎么办?你可以在方法里定义参数,参数名要跟请求体里的 JSON 字段名对上,Salesforce 会自动把请求体反序列化给你填进去。如果你不定义参数,也可以通过 `RestContext.request.requestBody` 拿到原始 Blob,自己解析。 说到格式,Apex REST 默认用 JSON,外面发来的 JSON 会自动映射到你的 Apex 对象,你返回的 Apex 对象也会自动变成 JSON 响应体。但是有一点你得留神:返回对象的时候,只有公开的、被标注为 `webservice` 的成员变量才会被序列化到响应里,私有的、受保护的统统不会出现。所以如果你想某个字段暴露出去,记得把它声明成 `global` 或 `public`。 有个小坑提醒一下:XML 格式你也能用,但它对集合(列表、集合等)的支持不太好,限制比较多,所以咱们一般都推荐用 JSON 就对了。 另外,如果你的类是在一个托管包里,那 URL 映射就会自动带上包命名空间,比如 “/包命名空间/myService/*”,这个是为了避免不同的包路径冲突,你调用的时候注意路径里多了一层。 好,总结一下:类上加 `@RestResource` 配好路径,方法上贴对应动词注解,方法都 global static,GET/DELETE 不带参数,POST/PUT/PATCH 可以用参数接收请求体,返回值自动变 JSON,私有字段不会暴露。是不是一下子清晰了?有疑问咱们随时停下来讨论。

    查看详情
  • 8

    Apex REST — User-Defined Types

    第 258 页

    同学们,我们来看一下,在 Apex REST 服务里,我们自己的自定义类型是怎么作为参数来用的。 你可以把你自己定义的类,比如一个包含姓名和年龄的类,直接当作 REST 方法的参数。Apex REST 会把你发过来的请求数据,自动填到这个类的成员变量里,不管是公共的、私有的还是全局的,都可以。不过要注意,如果这个成员变量是 static 的,或者是被标记为 transient 的,那它就不会被填值,会被跳过去。 还有一点很重要,就是不要让你的成员变量互相循环依赖。比如 A 里面有个 B 类型的字段,B 里面又有个 A 类型的字段,这样在运行的时候系统会发现,然后直接返回一个 400 错误给你。 说到 JSON 里的参数名,名字必须跟类里的变量名一致,但是这些参数在 JSON 里出现的先后顺序是无所谓的,随便排。 最后,URL 模式结尾加不加那个斜杠,像 “/myservice” 和 “/myservice/”,其实是匹配同一个 URL 的。不过如果两个类都匹配上了,系统会选用先保存的那个类。 很简单吧?好,我们继续看后面的。

    查看详情
  • 9

    Apex REST — Request & Response Data Rules

    第 259 页

    好,同学们,我们来看这一页。它讲的是在使用 Salesforce REST API 的时候,发送请求要遵守的一些数据规则,还有服务端会自动返回哪些状态码。 先说请求数据里面的几个注意点。 第一个,对于布尔值,也就是真或假,它只接受 true、false,或者数字 1 和 0。其他写法都会报错。 第二,如果你在请求里不小心把同一个参数传了多次,比如网址里同一个字段写了两次,那服务器会直接给你回一个 400 错误,意思是你的请求格式不对。 第三,关于空值的处理。如果你想表示一个字段没有值,可以直接把它从请求体里省略掉;如果用 JSON,也可以写成 null;如果用的是 XML 格式,就需要用 xsi:nil 这个属性来表示空。 第四,名称空间的要求。涉及到元数据(Metadata)的时候,必须带上正确的 XML 命名空间,不能乱来。而且,元数据类型的字段也有限制,不是所有标准字段都能直接用,调用前最好查一下文档。 接下来,我们再看响应状态码。这些都是服务端处理完请求后自动设置的,不用你管。你要知道的是,不同码的意思: - 200 表示一切正常,请求成功。 - 如果你发的是 PATCH 更新请求,但改完之后没发现数据变化,会返回 204,代表成功但没内容返回。 - 400 就是我们刚才说的,请求格式有问题,或者参数不合法。 - 403 是你没有权限访问这个资源。 - 404 就是 URL 路径写得不对,没找到对应的 API 映射。 - 405 是你用了不支持的 HTTP 方法,比如这个接口只接受 GET,你偏偏用了 POST。 - 406 和 415 都是跟内容类型有关的问题,比如你要求的响应格式不支持,或者你发送的数据格式不对。 - 最后,500 是通用的服务器内部错误,如果你的 Apex 代码里有没处理到的异常,就可能看到这个。 好,这一页就讲这些。记住核心:布尔值别乱写、参数别重复、空值按规则来,再对照状态码就能快速定位问题。有什么疑问可以随时问。

    查看详情
  • 10

    Apex REST — Data Exposure & Security

    第 260 页

    同学们,今天咱们来聊聊一个比较重要的更新——就是关于自定义Apex REST方法运行模式的变化。 从API 67.0版本开始,你写好的REST服务,默认就会在用户模式下运行了。那用户模式是什么意思呢?简单说,它会严格贯彻执行你这边的三项安全设置:对象权限、字段级安全,还有共享规则。 也就是说,如果当前用户没有某个对象的访问权限,或者看不到某个敏感字段,又或者共享规则不让他看到某条记录,那你的代码就会自动替他挡住,不会因为疏忽而泄露数据。这其实是个更安全的设计。 不过呢,有时候你可能确实需要在代码里临时绕开字段级安全的限制,那该怎么办?可以用一个叫做 `WHITE_MED` 的注解来声明。如果需要显式地在运行中检查权限,就可以用模式描述结果的相关方法。 再说到共享规则,默认也是强制执行的。如果你想绕过它,让代码能访问所有记录,那就把类声明为 `without sharing` 就可以啦。 这里要特别注意,API 67.0 的改变其实挺大的。以前,REST方法默认是在系统模式下运行的,很多类即使没有特别声明,也好像自带“无视共享规则”的效果。但现在反过来,默认就是最严格的用户模式,除非你明确声明成无共享类,否则代码就会像带着共享规则一样运行。 那么最佳实践是什么呢?很简单,三点: 第一,尽量利用好默认的用户模式安全机制,别主动降低安全门槛; 第二,如果代码要处理敏感数据,记得显式检查权限; 第三,永远别忘记验证所有来自外部的输入,防止注入和非法访问。 这样讲应该清晰了吧?记住,安全无小事,现在平台帮我们把默认值弄得更安全了,咱们写代码的时候也要把这些好习惯用起来。

    查看详情
  • 11

    Apex REST — Basic CRUD Code Sample

    第 261 页

    好,我们来看一下这一页幻灯片的内容,它讲的是一个在Salesforce里用Apex写的REST API的完整例子。 首先,这个API有三个最常用的HTTP方法:一个是GET,用来查询一条客户记录;一个是POST,用来新建一个客户;还有一个是DELETE,用来删除客户。 那它是怎么知道要操作哪个客户的呢?在这段代码里,我们用了一个叫@RestResource的注解,后面跟着URL映射。这个映射里有一个特别的地方——用花括号包起来的“AccountId”,这种写法就像一个收件箱,能从请求网址里把客户的ID号给抓出来。比如,如果请求的网址是/Account/001xx000003GsAi,花括号里的部分就能自动拿到“001xx000003GsAi”这个ID。 幻灯片里还给了cURL的命令行例子,方便我们知道怎么在实际中调用这几个接口。在用的时候,你得替换掉两个东西:一个是sessionId,一个是实例的域名,这些都可以从你登录Salesforce后的响应里面找到。只要带上正确的身份验证头,就能顺利调用。 最后还展示了调用GET方法后会返回的JSON响应长什么样。它里面会包含客户ID、名称、电话和网站这几个字段,一目了然。 那么总的来说,这一页就是教你如何用Apex快速搭建一个标准的REST服务,对外提供增删查的功能,并且通过简单的地址映射来指定要操作的数据。

    查看详情
  • 12

    Apex REST — File Upload with RestRequest

    第 262 页

    咱们今天这一张幻灯片讲的是怎么用 Apex REST 这个接口来上传一个二进制文件,比如给一个案例(Case)添加附件。 你会看到这个例子里,重点就是当你的 Apex REST 方法,不声明任何参数,的时候,那整个 HTTP 请求的“身体”会被 Salesforce 自动当作一个 Blob(二进制大对象),直接放到 `RestContext.request.requestBody` 里面,非常方便。 咱们来看实现思路: 首先,这个 REST 方法是从 URL 里把案例的 ID 提取出来。然后我们就用请求体里的那个 Blob 数据,创建一个 Attachment 对象的 `Body` 字段。其他像文件名这些信息,我们可能会从请求头或者固定规则里取。最后把这个附件插入到数据库里,跟对应的案例关联起来。 你可以想象,不管客户端发来的是图片、PDF 还是什么其他文件,Salesforce 这边不用做多余的处理,直接拿到二进制内容就能保存。 那外部怎么调用呢?幻灯片里提到了 ,cURL, 命令,用的是 `--data-binary` 这个选项。`--data-binary` 的好处是它会原封不动地把文件内容发出去,不会因为编码或者换行符丢失数据,而且我们还能通过 `-H` 设置合适的 `Content-Type` 请求头,告诉 Salesforce 你传的是个 JPEG 还是 PNG。 成功之后,这个 REST 方法会返回一个 JSON,里面包含新创建的附件 ID,这样调用方就知道上传成功了。 你要在系统里验证也很简单:打开那个案例记录,往下翻到“附件”相关列表,就能看到刚才上传的文件,可以点开预览一下。 所以总结起来,这个模式就是 REST 方法无参数 + 直接读取 `requestBody` + `--data-binary` 上传,一条龙搞定文件接收。对于需要让外部系统给 Salesforce 案例补充材料、上传凭证的场景特别实用。

    查看详情