Phenomenon
When using the Rainbow DNS panel (dnsmgr v2.16, based on ThinkPHP 6) API to delete or modify DNS records, the response is always “Domain does not exist”, even though the same credentials work perfectly for query APIs.
API endpoint format: POST /api/record/delete/{domain_id}, with POST body containing id={record_id}.
This issue persisted across several debugging sessions. During troubleshooting, we tried:
- Verifying the signature algorithm → Correct (
md5(uid + timestamp + apikey)) - Confirming
domain_idcorrectness → Correct (same value used successfully in the list API) - Checking permissions → Query works fine, ruling out permission issues
The root cause remained elusive.
Root Cause Analysis
We finally identified the root cause after reviewing the dnsmgr source code. The key lies in ThinkPHP’s parameter parsing mechanism.
Controller Code (Domain.php)
// record_delete method
public function record_delete($id) {
// $id comes from route `/api/record/delete/:id`, representing the domain's internal ID
$domain = DomainModel::where('id', $id)->find();
// ...
$recordid = input('post.recordid'); // Note: it reads 'recordid', not 'id'
// ...
}
Route Definition (route/app.php)
Route::post('/api/record/delete/:id', 'Domain/record_delete');
The Problem
ThinkPHP’s input('param.id') resolution order is: Route parameters → GET → POST.
When issuing POST /api/record/delete/364 with a POST body containing id=2028144566343693312:
- Route parameter
:id=364(domain ID) ✓ - However, the POST body also contains an
idfield; ThinkPHP’s parameter resolution causes the POSTidto override the route:id - Consequently, the controller receives
$id = 2028144566343693312(a record ID) - Using this record ID to query the domain table → naturally fails → “Domain does not exist”
Meanwhile, the controller retrieves the record ID via input('post.recordid') — the expected parameter name is recordid, not id.
Same Issue Exists in record_update
public function record_update($id) {
$domain = DomainModel::where('id', $id)->find();
$recordid = input('post.recordid'); // Again, expects 'recordid'
// ...
}
Solution
Change all script calls for delete and update from \&id= to \&recordid=:
# Before (incorrect)
_dns_api "/api/record/delete/${domain_id}" "\&id=${record_id}"
# After (correct)
_dns_api "/api/record/delete/${domain_id}" "\&recordid=${record_id}"
# Similarly for update
_dns_api "/api/record/update/${domain_id}" "\&recordid=${record_id}\&name=..."
Verification Results
After applying the fix, full end-to-end testing passed: add test record → verify via query → delete → verify again that record is gone.
Lessons Learned
- When API documentation is incomplete, reading the source code is the most reliable debugging method. This bug would be extremely difficult to isolate via black-box testing alone, because the error message “Domain does not exist” is misleading—it suggests an issue with
domain_id, whereas the real problem is incorrect parameter naming causingdomain_idto be unexpectedly overwritten. - ThinkPHP’s parameter resolution order (Route → GET → POST) is a common pitfall. If a POST body field shares the same name as a route parameter, unexpected overwriting occurs.
- Using the same parameter name (
id) to represent different entities across multiple endpoints easily introduces ambiguity. dnsmgr’s design intentionally avoids naming conflicts by using:idin the route for domains andrecordidin POST data for records—but callers unaware of this convention will inevitably run into this trap.