Nop 平台的 GraphQL 和 REST API
Nop 平台的 API 层有独特的设计:同一个引擎同时提供 GraphQL 和 REST 两种协议,不需要额外适配。
为什么需要两套协议
GraphQL 适合复杂查询场景——客户端可以精确指定需要哪些字段,避免过度获取。REST 适合简单调用场景——用 curl 就能测试,调试方便。
传统做法是维护两套端点,各有各的路由、序列化、权限控制。Nop 用一套模型同时暴露两种协议。
基础概念:BizModel
Nop 中每个业务对象对应一个 BizModel(业务模型)。它定义了该对象的 CRUD 操作以及自定义方法。
自动生成的 BizModel(以 Article 为例)包含以下内建操作:
save — 新增/修改 get — 按 ID 查询 delete — 删除 findPage — 分页查询 findList — 列表查询
这些操作同时注册为 GraphQL Mutation/Query 和 REST 端点。
REST 端点
每个 BizModel 自动注册以下 REST 端点:
POST /ArticleBizModel__save 保存 GET /ArticleBizModel__get?id=xxx 查询单个 POST /ArticleBizModel__findPage 分页查询 POST /ArticleBizModel__batchModify 批量修改
URL 格式为 /{BizModel}__{method}。请求体统一为 JSON。
查询示例:
curl "http://localhost:8080/ArticleBizModel__get?id=abc123" \ -H "nop-tenant: 0"
分页查询:
curl -X POST "http://localhost:8080/ArticleBizModel__findPage" \ -H "Content-Type: application/json" \ -H "nop-tenant: 0" \ -d '{ "query": { "title__contains": "Nop", "orderBy": [{"field": "createTime", "desc": true}] }, "limit": 20 }'
支持丰富的筛选条件:
| 后缀 | 含义 |
|---|---|
| __contains | 包含 |
| __gt | 大于 |
| __ge | 大于等于 |
| __lt | 小于 |
| __le | 小于等于 |
| __in | 在列表中 |
| __exists | 子查询存在 |
| __isNull | 为空 |
GraphQL 端点
GraphQL 端点在http://localhost:8080/graphql。支持 introspection,可以用 GraphiQL 探索。
查询单个文章:
query {
ArticleBizModel__get(id: "abc123") {
title
content
author
createTime
}
}
创建文章:
mutation {
ArticleBizModel__save(data: {
title: "Hello Nop",
content: "文章内容",
author: "test"
}) {
articleId
title
}
}
GraphQL 的优势在于可以跨对象查询:
query {
ArticleBizModel__findPage(query: {author__contains: "test"}) {
items {
title
createTime
# 关联对象自动可查
tags {
name
}
}
total
}
}
关联对象不需要手写 join——ORM 层自动处理。
自定义业务方法
通过继承 BizModel 添加自定义操作:
@BizModel("Article") public class ArticleBizModelEx extends _ArticleBizModel { @BizQuery public List<Article> getTopArticles( @Name("limit") int limit ) { return dao().findTopByViews(limit); } @BizAction public void publishArticle( @Name("id") String id ) { Article article = dao().get(id); article.setStatus("published"); dao().update(article); } }
@BizQuery 标记的方法注册为 GraphQL Query + REST GET。 @BizAction 标记的方法注册为 GraphQL Mutation + REST POST。
自定义方法自动获得端点:
REST: GET /ArticleBizModel__getTopArticles?limit=10
REST: POST /ArticleBizModel__publishArticle
GraphQL: query { ArticleBizModel__getTopArticles(limit: 10) { ... } }
GraphQL: mutation { ArticleBizModel__publishArticle(id: "xxx") { ... } }
权限控制
每个方法都可以通过 acl.xml 配置权限:
<!-- my-app.api.xml --> <beans> <bean id="ArticleBizModel" class="..."> <actions> <action name="save" roles="admin" /> <action name="getTopArticles" roles="user" /> <action name="publishArticle" roles="editor" /> </actions> </bean> </beans>
未授权的调用返回 403。
协议对比
| 特性 | REST | GraphQL |
|---|---|---|
| 端点 URL | /{BizModel}__{method} | /graphql |
| 客户端 | curl、浏览器 | GraphiQL、Apollo |
| 字段选择 | 固定返回 | 客户端指定 |
| 关联对象 | 单独请求 | 同一请求中 |
| 批量操作 | 逐个请求 | 同一请求中 |
| 自省 | 无 | 有(Introspection) |
两套协议由同一套 BizModel 定义驱动,不需要各自维护一份代码。