Trong bài viết trước của chúng tôi "Phải làm gì khi API có nhiều cấu trúc tham số", chúng tôi đã thảo luận rằng khi một API yêu cầu các cấu trúc tham số khác nhau, bạn có thể sử dụng oneOf, anyOf, hoặc allOf trong Apidog để định nghĩa chúng.
Sau khi bạn đã thiết lập các cấu trúc tham số bằng cách sử dụng các thành phần lược đồ này, và các tham số của bạn bao gồm một trường xác định kiểu dữ liệu, bạn có thể làm cho tài liệu rõ ràng hơn nữa bằng cách sử dụng tính năng discriminator từ đặc tả OpenAPI để phân biệt giữa các lược đồ một cách trực quan hơn.
Discriminator là gì?
Trong OpenAPI, vai trò của discriminator là triển khai "đa hình" từ lập trình hướng đối tượng.
Nói một cách đơn giản, nó sử dụng một thuộc tính chung và giá trị duy nhất của thuộc tính đó để chỉ rõ lược đồ cụ thể nào trong danh sách oneOf hoặc anyOf nên được sử dụng.
Ví dụ, trong một cửa hàng thú cưng, chúng ta cần sử dụng một lược đồ để mô tả thú cưng. (Và mèo và chó có các thuộc tính khác nhau.)
Lược đồ cho chó là:
{
"name": "Hotdog",
"age": 3,
"weight": 25.5,
"petType": "dog",
"breed": "Golden Retriever",
"isVaccinated": true,
"walkingSchedule": "Morning and Evening",
"favoriteToys": ["Frisbee", "Tennis Ball"],
"trainingLevel": "Intermediate"
}Lược đồ cho mèo là:
{
"name": "Meow",
"age": 2,
"weight": 4.2,
"petType": "cat",
"isVaccinated": true,
"isIndoor": true,
"litterBoxType": "Enclosed",
"scratchingPostHeight": 120,
"favoriteSleepingSpot": "Balcony"
}Như bạn có thể thấy, mặc dù cả hai lược đồ đều có các trường chung như petType, name, age, v.v., chó có các trường đặc trưng như breed, walkingSchedule, v.v., trong khi mèo có các trường đặc trưng như isIndoor, litterBoxType, v.v.
Nếu bạn chỉ sử dụng oneOf để định nghĩa các lược đồ này khi thiết kế tài liệu API của mình, tài liệu có thể về mặt kỹ thuật phân biệt giữa các lược đồ dog và cat. Tuy nhiên, sự tương ứng của từng loại không rõ ràng lắm. Người đọc phải chuyển đổi giữa hai tab lược đồ để so sánh chúng, điều này trở nên khó hiểu khi có nhiều tham số.

Bằng cách thêm một discriminator, tài liệu sẽ hiển thị một menu thả xuống, nơi người đọc có thể chọn giá trị của petType (dog hoặc cat). Sau khi được chọn, trang sẽ tự động hiển thị lược đồ chính xác với các trường liên quan. Điều này loại bỏ nhu cầu so sánh nhiều tab và làm cho sự khác biệt giữa mỗi lược đồ rõ ràng hơn nhiều.

Một discriminator thường được sử dụng cùng với oneOf hoặc anyOf. oneOf định nghĩa các lược đồ đối tượng có thể có, và discriminator cho trình phân tích biết cách xác định lược đồ nào sẽ áp dụng dựa trên loại đã chọn.
Điều này làm cho các đối tượng phức tạp rõ ràng hơn và giúp các công cụ tự động phân biệt các loại khi hiển thị tài liệu hoặc tạo mã.
Các thuộc tính cấu hình của discriminator
Một discriminator chứa hai thuộc tính chính:
- propertyName: Chỉ định tên trường được sử dụng để phân biệt các loại, chẳng hạn như
petTypetrong ví dụ trên. - mapping: Định nghĩa mối quan hệ ánh xạ giữa các giá trị trường và các lược đồ cụ thể, chẳng hạn như
"dog"tương ứng với lược đồ chó và"cat"tương ứng với lược đồ mèo.
Ví dụ cấu hình trong OpenAPI:
discriminator:
propertyName: petType
mapping:
dog: '#/components/schemas/Dog'
cat: '#/components/schemas/Cat'Cấu hình Discriminator trong Apidog
Apidog hỗ trợ cấu hình discriminator theo hai cách:
- GUI + JSON Schema
- Nhập trực tiếp các đặc tả OpenAPI
Phương pháp 1: GUI + JSON Schema
Đầu tiên, tạo các lược đồ trong Apidog, chẳng hạn như Dog và Cat:

Mở endpoint nơi bạn muốn sử dụng các lược đồ này và đi đến trình chỉnh sửa nội dung yêu cầu hoặc phản hồi.
Chọn trường bạn muốn định nghĩa là một thành phần lược đồ và chọn Cài đặt nâng cao → Thành phần lược đồ → oneOf.

Trong oneOf, tham chiếu các lược đồ bạn cần, chẳng hạn như Dog và Cat.

Bây giờ bạn đã định nghĩa nhiều lược đồ có thể thông qua oneOf. Tiếp theo, bạn cần thêm discriminator để chỉ định cách phân biệt các lược đồ này. Nhấp vào JSON Schema để chuyển sang chế độ mã:

Thêm cấu hình discriminator vào vị trí thích hợp (cùng cấp với oneOf), ví dụ:
"discriminator": {
"propertyName": "petType",
"mapping": {
"dog": "#/definitions/190704823",
"cat": "#/definitions/190704706"
}
}Các giá trị trong mapping, chẳng hạn như #/definitions/190704823, là các ID duy nhất được Apidog tạo nội bộ cho các lược đồ. Bạn có thể tìm thấy đường dẫn definitions tương ứng cho mỗi lược đồ trong bảng cấu hình JSON Schema.

Sau khi cấu hình hoàn tất, hãy lưu lại. Trong tài liệu API, bạn có thể chuyển đổi các loại đối tượng một cách thông minh dựa trên giá trị của trường petType.

Phương pháp 2: Nhập trực tiếp các đặc tả OpenAPI
Đây là một cách tiếp cận tiêu chuẩn hơn, đặc biệt phù hợp cho các nhóm quen với quy trình làm việc "thiết kế trước".
Viết các đặc tả OpenAPI của bạn có kèm discriminator, sau đó nhập chúng vào Apidog.
Ví dụ, trong các đặc tả OpenAPI, định nghĩa của discriminator như sau:
Pet:
oneOf:
- $ref: '#/components/schemas/Dog'
- $ref: '#/components/schemas/Cat'
discriminator:
propertyName: petType
mapping:
dog: '#/components/schemas/Dog'
cat: '#/components/schemas/Cat'Định nghĩa này nêu rõ:
- Thông qua
oneOf, chỉ định rằng đối tượng có thể là loạiDoghoặcCat - Thông qua
discriminator, chỉ định việc xác định loại cụ thể dựa trên giá trị của trườngpetType - Thông qua
mapping, chỉ rõ rằng"dog"tương ứng với lược đồDogvà"cat"tương ứng với lược đồCat.
Các đặc tả OpenAPI hoàn chỉnh như sau:
openapi: 3.0.3
info:
title: Pet Shop API
version: 1.0.0
components:
schemas:
Dog:
type: object
properties:
petType:
type: string
enum: [dog]
name:
type: string
age:
type: integer
weight:
type: number
breed:
type: string
isVaccinated:
type: boolean
walkingSchedule:
type: string
favoriteToys:
type: array
items:
type: string
trainingLevel:
type: string
required:
- petType
- name
- age
Cat:
type: object
properties:
petType:
type: string
enum: [cat]
name:
type: string
age:
type: integer
weight:
type: number
isVaccinated:
type: boolean
isIndoor:
type: boolean
litterBoxType:
type: string
scratchingPostHeight:
type: integer
favoriteSleepingSpot:
type: string
required:
- petType
- name
- age
Pet:
oneOf:
- $ref: '#/components/schemas/Dog'
- $ref: '#/components/schemas/Cat'
discriminator:
propertyName: petType
mapping:
dog: '#/components/schemas/Dog'
cat: '#/components/schemas/Cat'
paths:
/pets:
get:
summary: Get pet information
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'Sau khi hoàn thành các đặc tả OpenAPI, sử dụng tính năng import trong Apidog. Apidog sẽ phân tích tất cả các đặc tả, tự động tạo các lược đồ và endpoint tương ứng, đồng thời nhận diện chính xác cấu hình discriminator (enum được thêm vào đây để khóa các giá trị tham số).

Câu hỏi thường gặp
1. Khi nào nên sử dụng Discriminator?
Bạn chỉ nên sử dụng discriminator khi endpoint của bạn đã bao gồm một trường xác định các lược đồ. Trong ví dụ trên, trường petType tồn tại trong thiết kế API và được sử dụng để phân biệt giữa các loại thú cưng.
discriminator chỉ đơn giản cho các công cụ biết cách chọn lược đồ chính xác dựa trên giá trị của trường đó.
Nếu endpoint của bạn không bao gồm một trường xác định loại như vậy, thì discriminator không phù hợp—chỉ cần sử dụng oneOf là đủ.
Ngoài ra, nếu lược đồ của bạn đơn giản hoặc chỉ có một vài biến thể, bạn có thể không cần đến discriminator.
2. Discriminator có bắt buộc phải dùng với oneOf không?
Có. Một discriminator phải được sử dụng cùng với oneOf, anyOf, hoặc allOf. Nó không thể tự định nghĩa các lược đồ—nó chỉ giải thích cách phân biệt giữa nhiều lược đồ có thể có.
Kết luận
discriminator là một tính năng tùy chọn trong đặc tả OpenAPI được sử dụng để nâng cao trải nghiệm người dùng của oneOf/anyOf/allOf.
Khi các đặc tả endpoint của bạn đã chứa các trường được sử dụng để xác định loại, bạn có thể cấu hình discriminator trong Apidog để làm cho tài liệu API rõ ràng và thông minh hơn.
Tất nhiên, nếu bạn thấy việc cấu hình phiền phức hoặc lược đồ tương đối đơn giản, bạn hoàn toàn có thể chỉ sử dụng thành phần lược đồ như oneOf.
