Skip to content

如何调用 REST 终结点

数据墙DBW提供 RESTful Web API,使你能够访问已连接数据库中的表、视图和存储过程。每个公开的数据库对象都会在运行时配置中定义为一个实体

默认情况下,数据墙DBW 将 REST 终结点托管在:

text
https://{base_url}/api/{entity}

NOTE

所有路径组件和查询参数都区分大小写。

数据墙DBW 支持的关键字

概念REST用途
投影$select选择要返回的字段
筛选$filter按条件限制行
排序$orderby定义排序顺序
页大小$first限制每页项目数
延续$after从上一页继续

基本结构

要调用 REST API,请按以下模式构造请求:

http
{HTTP method} https://{base_url}/{rest-path}/{entity}

读取 book 实体中所有记录的示例:

http
GET https://localhost:5001/api/book

响应是一个包含 value 数组的 JSON 对象。分页和错误信息仅在适用时出现。

NOTE

默认情况下,除非另有配置,否则 数据墙DBW 每次查询最多返回 100 个项目(runtime.pagination.default-page-size)。

HTTP

http
GET https://localhost:5001/api/book

成功:

json
{
  "value": [
    { "id": 1, "title": "Dune", "year": 1965, "pages": 412 },
    { "id": 2, "title": "Foundation", "year": 1951, "pages": 255 }
  ]
}

带分页的成功响应:

json
{
  "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"
}

空结果(未找到项目):

json
{
  "value": []
}

NOTE

针对不存在主键的 GET 请求会返回 200 OK 和空的 value 数组,而不是 404 Not Found。请通过检查数组是否为空来判断该项是否存在。

cURL

bash
curl -X GET "https://localhost:5001/api/book"

C#

以下模型类用于反序列化 数据墙DBW 响应:

csharp
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 并反序列化响应:

csharp
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 响应:

python
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 并解析响应:

python
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:

javascript
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 ?? [];
}

使用示例:

javascript
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}按主键返回单条记录

返回单条记录的示例:

http
GET /api/book/id/1010

返回多条记录的示例:

http
GET /api/book

筛选结果

使用 $filter 查询参数限制返回哪些记录。

http
GET /api/book?$filter=title eq 'Foundation'

此查询返回所有标题等于 “Foundation” 的图书。

筛选还可以包含逻辑运算符,以构造更复杂的查询:

http
GET /api/book?$filter=year ge 1970 or title eq 'Dune'

有关详细信息,请参阅 $filter 参数参考页。该参考页可在对应专题中查看。

排序结果

$orderby 参数用于定义记录的排序方式。

http
GET /api/book?$orderby=year desc, title asc

这将返回按 year 降序、再按 title 升序排列的图书。

有关详细信息,请参阅 $orderby 参数参考页。该参考页可在对应专题中查看。

限制结果

$first 参数限制单次请求返回的记录数。

http
GET /api/book?$first=5

这会返回前五本图书,默认按主键排序。你也可以使用 $first=-1 请求配置的最大页大小,默认是 100 项。可在配置文件中通过 runtime.pagination.default-page-size 设置此限制。

有关详细信息,请参阅 $first 参数参考页。该参考页可在对应专题中查看。

继续获取结果

要获取下一页,请使用上一响应中的延续令牌配合 $after 参数。

http
GET /api/book?$first=5&$after={continuation-token}

$after 令牌标识上一次查询结束的位置。

有关详细信息,请参阅 $after 参数参考页。该参考页可在对应专题中查看。

字段选择(投影)

使用 $select 控制响应中包含哪些字段。

http
GET /api/book?$select=id,title,price

这只返回指定列。如果某个字段不存在或不可访问,数据墙DBW 会返回 400 Bad Request

有关详细信息,请参阅 $select 参数参考页。该参考页可在对应专题中查看。

修改数据

REST API 也支持创建、更新和删除操作,前提取决于实体权限配置。

方法操作
POST创建新项
PUT替换现有项(若不存在则创建)
PATCH更新现有项(若不存在则创建)
DELETE按主键删除项

IMPORTANT

PUTPATCH 的 upsert(不存在则插入)行为,仅在数据库允许显式指定主键值时才有效。对于使用自动生成键的表(例如 SQL Server 中的 IDENTITY 列或 PostgreSQL 中的 SERIAL),对不存在键执行 PUTPATCH 会返回 404 Not Found,因为数据库会拒绝向自动生成列显式插入值。对于使用自动生成键的表,请使用 POST 创建记录,或使用无键 PUT 和 PATCH让 数据墙DBW 自动分配键值。

创建新记录

使用 POST 创建新项。

HTTP

http
POST https://localhost:5001/api/book
Content-Type: application/json

{
  "id": 2000,
  "title": "Leviathan Wakes",
  "year": 2011,
  "pages": 577
}

cURL

bash
curl -X POST "https://localhost:5001/api/book" \
  -H "Content-Type: application/json" \
  -d '{"id": 2000, "title": "Leviathan Wakes", "year": 2011, "pages": 577}'

C#

csharp
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

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

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

http
PATCH https://localhost:5001/api/book/id/2000
Content-Type: application/json

{
  "id": 2000,
  "title": "Leviathan Wakes",
  "year": 2011,
  "pages": 577
}

cURL

bash
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

csharp
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

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

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

http
DELETE https://localhost:5001/api/book/id/2000

cURL

bash
curl -X DELETE "https://localhost:5001/api/book/id/2000"

C#

csharp
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

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

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 属性中配置子目录路径:

json
{
  "entities": {
    "ShoppingCartItem": {
      "source": "dbo.ShoppingCartItem",
      "rest": {
        "path": "shopping-cart/item"
      }
    }
  }
}

此配置会生成如下终结点:

http
GET /api/shopping-cart/item

数据墙DBW 使用最长前缀匹配进行路由,因此更具体的路径会优先于较短路径进行匹配。出于安全考虑,验证会阻止路径遍历模式(如 ..)、反斜杠以及经过百分号编码的分隔符。

有关详细信息,请参阅 REST 路径配置参考页;该页面可在对应专题中查看。

针对自动生成主键的无键 PUT 和 PATCH

NOTE

本节描述的 数据墙DBW 2.0 功能当前为预览版,在正式发布前可能会发生变化。

当某个实体的所有主键列都是自动生成时(例如 SQL Server 中的 IDENTITY 列),你可以在 URL 中不指定主键就发送 PUTPATCH 请求。数据墙DBW 会在插入期间自动分配键值。

http
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 部分中配置压缩:

json
{
  "runtime": {
    "compression": {
      "level": "optimal"
    }
  }
}

可用压缩级别:

级别说明
optimal平衡压缩率与速度(建议用于大多数场景)
fastest优先考虑压缩速度而非压缩率
none禁用压缩

有关如何配置压缩的详细信息,请参阅运行时压缩配置参考页;该页面可在对应专题中查看。

数据墙DBW 产品文档与开发指南。