如何调用 REST 终结点
数据墙DBW提供 RESTful Web API,使你能够访问已连接数据库中的表、视图和存储过程。每个公开的数据库对象都会在运行时配置中定义为一个实体。
默认情况下,数据墙DBW 将 REST 终结点托管在:
https://{base_url}/api/{entity}NOTE
所有路径组件和查询参数都区分大小写。
数据墙DBW 支持的关键字
| 概念 | REST | 用途 |
|---|---|---|
| 投影 | $select | 选择要返回的字段 |
| 筛选 | $filter | 按条件限制行 |
| 排序 | $orderby | 定义排序顺序 |
| 页大小 | $first | 限制每页项目数 |
| 延续 | $after | 从上一页继续 |
基本结构
要调用 REST API,请按以下模式构造请求:
{HTTP method} https://{base_url}/{rest-path}/{entity}读取 book 实体中所有记录的示例:
GET https://localhost:5001/api/book响应是一个包含 value 数组的 JSON 对象。分页和错误信息仅在适用时出现。
NOTE
默认情况下,除非另有配置,否则 数据墙DBW 每次查询最多返回 100 个项目(runtime.pagination.default-page-size)。
HTTP
GET https://localhost:5001/api/book成功:
{
"value": [
{ "id": 1, "title": "Dune", "year": 1965, "pages": 412 },
{ "id": 2, "title": "Foundation", "year": 1951, "pages": 255 }
]
}带分页的成功响应:
{
"value": [
{ "id": 1, "title": "Dune", "year": 1965, "pages": 412 },
{ "id": 2, "title": "Foundation", "year": 1951, "pages": 255 }
],
"nextLink": "https://localhost:5001/api/book?$after=WyJCb29rMiJd"
}空结果(未找到项目):
{
"value": []
}NOTE
针对不存在主键的 GET 请求会返回 200 OK 和空的 value 数组,而不是 404 Not Found。请通过检查数组是否为空来判断该项是否存在。
cURL
curl -X GET "https://localhost:5001/api/book"C#
以下模型类用于反序列化 数据墙DBW 响应:
using System.Text.Json.Serialization;
public class DabResponse<T>
{
[JsonPropertyName("value")]
public List<T>? Value { get; set; }
[JsonPropertyName("nextLink")]
public string? NextLink { get; set; }
[JsonPropertyName("error")]
public DabError? Error { get; set; }
[JsonIgnore]
public bool IsSuccess => Error is null;
[JsonIgnore]
public bool HasNextPage => NextLink is not null;
}
public class DabError
{
[JsonPropertyName("code")]
public string Code { get; set; } = string.Empty;
[JsonPropertyName("message")]
public string Message { get; set; } = string.Empty;
[JsonPropertyName("status")]
public int Status { get; set; }
}
public class Book
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("title")]
public string Title { get; set; } = string.Empty;
[JsonPropertyName("year")]
public int? Year { get; set; }
[JsonPropertyName("pages")]
public int? Pages { get; set; }
}调用 API 并反序列化响应:
public async Task<List<Book>> GetBooksAsync()
{
var response = await httpClient.GetAsync("api/book");
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<DabResponse<Book>>();
if (result?.Error is not null)
{
throw new Exception($"{result.Error.Code}: {result.Error.Message}");
}
return result?.Value ?? [];
}Python
以下数据类用于建模 数据墙DBW 响应:
from dataclasses import dataclass
import requests
@dataclass
class Book:
id: int
title: str
year: int | None = None
pages: int | None = None
@dataclass
class DabError:
code: str
message: str
status: int
@dataclass
class DabResponse:
value: list[Book] | None = None
next_link: str | None = None
error: DabError | None = None
@property
def is_success(self) -> bool:
return self.error is None
@property
def has_next_page(self) -> bool:
return self.next_link is not None调用 API 并解析响应:
def get_books(base_url: str) -> list[Book]:
response = requests.get(f"{base_url}/api/book")
response.raise_for_status()
data = response.json()
if "error" in data:
err = data["error"]
raise Exception(f"{err['code']}: {err['message']}")
return [Book(**item) for item in data.get("value", [])]JavaScript
以下函数调用 API:
async function getBooks(baseUrl) {
const response = await fetch(`${baseUrl}/api/book`);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const data = await response.json();
if (data.error) {
throw new Error(`${data.error.code}: ${data.error.message}`);
}
return data.value ?? [];
}使用示例:
const books = await getBooks("https://localhost:5001");
console.log(`Fetched ${books.length} books from the API.`);查询类型
每个 REST 实体都同时支持集合读取和单记录读取。
| 操作 | 说明 |
|---|---|
GET /api/{entity} | 返回记录列表 |
GET /api/{entity}/{primary-key-column}/{primary-key-value} | 按主键返回单条记录 |
返回单条记录的示例:
GET /api/book/id/1010返回多条记录的示例:
GET /api/book筛选结果
使用 $filter 查询参数限制返回哪些记录。
GET /api/book?$filter=title eq 'Foundation'此查询返回所有标题等于 “Foundation” 的图书。
筛选还可以包含逻辑运算符,以构造更复杂的查询:
GET /api/book?$filter=year ge 1970 or title eq 'Dune'有关详细信息,请参阅 $filter 参数参考页。该参考页可在对应专题中查看。
排序结果
$orderby 参数用于定义记录的排序方式。
GET /api/book?$orderby=year desc, title asc这将返回按 year 降序、再按 title 升序排列的图书。
有关详细信息,请参阅 $orderby 参数参考页。该参考页可在对应专题中查看。
限制结果
$first 参数限制单次请求返回的记录数。
GET /api/book?$first=5这会返回前五本图书,默认按主键排序。你也可以使用 $first=-1 请求配置的最大页大小,默认是 100 项。可在配置文件中通过 runtime.pagination.default-page-size 设置此限制。
有关详细信息,请参阅 $first 参数参考页。该参考页可在对应专题中查看。
继续获取结果
要获取下一页,请使用上一响应中的延续令牌配合 $after 参数。
GET /api/book?$first=5&$after={continuation-token}$after 令牌标识上一次查询结束的位置。
有关详细信息,请参阅 $after 参数参考页。该参考页可在对应专题中查看。
字段选择(投影)
使用 $select 控制响应中包含哪些字段。
GET /api/book?$select=id,title,price这只返回指定列。如果某个字段不存在或不可访问,数据墙DBW 会返回 400 Bad Request。
有关详细信息,请参阅 $select 参数参考页。该参考页可在对应专题中查看。
修改数据
REST API 也支持创建、更新和删除操作,前提取决于实体权限配置。
| 方法 | 操作 |
|---|---|
POST | 创建新项 |
PUT | 替换现有项(若不存在则创建) |
PATCH | 更新现有项(若不存在则创建) |
DELETE | 按主键删除项 |
IMPORTANT
PUT 和 PATCH 的 upsert(不存在则插入)行为,仅在数据库允许显式指定主键值时才有效。对于使用自动生成键的表(例如 SQL Server 中的 IDENTITY 列或 PostgreSQL 中的 SERIAL),对不存在键执行 PUT 或 PATCH 会返回 404 Not Found,因为数据库会拒绝向自动生成列显式插入值。对于使用自动生成键的表,请使用 POST 创建记录,或使用无键 PUT 和 PATCH让 数据墙DBW 自动分配键值。
创建新记录
使用 POST 创建新项。
HTTP
POST https://localhost:5001/api/book
Content-Type: application/json
{
"id": 2000,
"title": "Leviathan Wakes",
"year": 2011,
"pages": 577
}cURL
curl -X POST "https://localhost:5001/api/book" \
-H "Content-Type: application/json" \
-d '{"id": 2000, "title": "Leviathan Wakes", "year": 2011, "pages": 577}'C#
var book = new Book
{
Id = 2000,
Title = "Leviathan Wakes",
Year = 2011,
Pages = 577
};
var response = await httpClient.PostAsJsonAsync("api/book", book);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<DabResponse<Book>>();
if (result?.Error is not null)
{
throw new Exception($"{result.Error.Code}: {result.Error.Message}");
}Python
book = {"id": 2000, "title": "Leviathan Wakes", "year": 2011, "pages": 577}
response = requests.post(f"{base_url}/api/book", json=book)
response.raise_for_status()
data = response.json()
if "error" in data:
err = data["error"]
raise Exception(f"{err['code']}: {err['message']}")JavaScript
const book = { id: 2000, title: "Leviathan Wakes", year: 2011, pages: 577 };
const response = await fetch(`${baseUrl}/api/book`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(book),
});
const data = await response.json();
if (data.error) {
throw new Error(`${data.error.code}: ${data.error.message}`);
}更新现有记录
使用 PATCH 更新现有项的指定字段。
HTTP
PATCH https://localhost:5001/api/book/id/2000
Content-Type: application/json
{
"id": 2000,
"title": "Leviathan Wakes",
"year": 2011,
"pages": 577
}cURL
curl -X PATCH "https://localhost:5001/api/book/id/2000" \
-H "Content-Type: application/json" \
-d '{"id": 2000, "title": "Leviathan Wakes", "year": 2011, "pages": 577}'C#
TIP
默认情况下,数据墙DBW 会拒绝请求体中数据库里不存在的字段,或本应放在 URL 中的字段(例如 PATCH 和 DELETE 的主键字段)。这种行为意味着 C# 通常需要分别定义类型:一个用于包含键的 POST,一个用于不包含键的更新操作。若要在所有操作中复用像 Book 这样的单一类型,请在配置中将 runtime.rest.request-body-strict 设为 false。
var book = new Book
{
Id = 2000,
Title = "Leviathan Wakes",
Year = 2011,
Pages = 577
};
var response = await httpClient.PatchAsJsonAsync("api/book/id/2000", book);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<DabResponse<Book>>();
if (result?.Error is not null)
{
throw new Exception($"{result.Error.Code}: {result.Error.Message}");
}Python
book = {"id": 2000, "title": "Leviathan Wakes", "year": 2011, "pages": 577}
response = requests.patch(f"{base_url}/api/book/id/2000", json=book)
response.raise_for_status()
data = response.json()
if "error" in data:
err = data["error"]
raise Exception(f"{err['code']}: {err['message']}")JavaScript
const book = { id: 2000, title: "Leviathan Wakes", year: 2011, pages: 577 };
const response = await fetch(`${baseUrl}/api/book/id/2000`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(book),
});
const data = await response.json();
if (data.error) {
throw new Error(`${data.error.code}: ${data.error.message}`);
}删除记录
使用 DELETE 按主键删除项。
HTTP
DELETE https://localhost:5001/api/book/id/2000cURL
curl -X DELETE "https://localhost:5001/api/book/id/2000"C#
var response = await httpClient.DeleteAsync("api/book/id/2000");
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<DabResponse<Book>>();
if (result?.Error is not null)
{
throw new Exception($"{result.Error.Code}: {result.Error.Message}");
}Python
response = requests.delete(f"{base_url}/api/book/id/2000")
response.raise_for_status()
data = response.json()
if "error" in data:
err = data["error"]
raise Exception(f"{err['code']}: {err['message']}")JavaScript
const response = await fetch(`${baseUrl}/api/book/id/2000`, {
method: "DELETE",
});
const data = await response.json();
if (data.error) {
throw new Error(`${data.error.code}: ${data.error.message}`);
}使用子目录的高级 REST 路径
NOTE
本节描述的 数据墙DBW 2.0 功能当前为预览版,在正式发布前可能会发生变化。有关详细信息,请参阅 2.0 版本新增内容页面;该页面可在对应专题中查看。
实体 REST 路径可以包含正斜杠,以创建类似子目录的 URL 片段。这种配置可为 API 提供更具表达力的层次化 URL 结构。
在实体的 rest.path 属性中配置子目录路径:
{
"entities": {
"ShoppingCartItem": {
"source": "dbo.ShoppingCartItem",
"rest": {
"path": "shopping-cart/item"
}
}
}
}此配置会生成如下终结点:
GET /api/shopping-cart/item数据墙DBW 使用最长前缀匹配进行路由,因此更具体的路径会优先于较短路径进行匹配。出于安全考虑,验证会阻止路径遍历模式(如 ..)、反斜杠以及经过百分号编码的分隔符。
有关详细信息,请参阅 REST 路径配置参考页;该页面可在对应专题中查看。
针对自动生成主键的无键 PUT 和 PATCH
NOTE
本节描述的 数据墙DBW 2.0 功能当前为预览版,在正式发布前可能会发生变化。
当某个实体的所有主键列都是自动生成时(例如 SQL Server 中的 IDENTITY 列),你可以在 URL 中不指定主键就发送 PUT 和 PATCH 请求。数据墙DBW 会在插入期间自动分配键值。
PUT /api/Book
Content-Type: application/json
{
"title": "My New Book",
"publisher_id": 1234
}该请求会创建一条带自动生成主键的新 Book 记录。
无键操作规则
- 所有被省略的主键列都必须是自动生成的。如果任何被省略的键列不是自动生成的,请求会失败。
- 对于复合主键,你仍必须在 URL 中提供所有非自动生成的键部分。
- 存储过程不受此功能影响。它们继续使用各自的参数处理方式。
- OpenAPI 文档会在基础实体路径上反映无键操作(例如不带键片段的
PUT /api/Book)。
HTTP 响应压缩
NOTE
本节描述的 数据墙DBW 2.0 功能当前为预览版,在正式发布前可能会发生变化。
数据墙DBW 支持 HTTP 响应压缩,以减小负载大小并提升传输速度。在配置文件的 runtime.compression 部分中配置压缩:
{
"runtime": {
"compression": {
"level": "optimal"
}
}
}可用压缩级别:
| 级别 | 说明 |
|---|---|
optimal | 平衡压缩率与速度(建议用于大多数场景) |
fastest | 优先考虑压缩速度而非压缩率 |
none | 禁用压缩 |
有关如何配置压缩的详细信息,请参阅运行时压缩配置参考页;该页面可在对应专题中查看。
