如何通过 ASP.NET 网页获取并更新 Plesk10 数据?
结论
可以通过 ASP.NET 在服务器端调用 Plesk 10 的 XML API RPC,读取订阅、邮箱、FTP 账户及资源使用量,也可以将允许的修改提交给 Plesk。
实施前需要注意以下两点:
- 不要把 Plesk 管理接口直接暴露给浏览器。ASP.NET 应作为中间层,处理身份验证、权限校验和 API 调用。
BILLING、INVOICE和付款记录通常不是 Plesk Panel 的核心数据。如果服务商使用 Customer & Business Manager、旧版 Plesk Billing 或第三方财务系统,还需要接入相应系统的 API 或数据库接口。
另外,拥有 reseller hosting plan 并不代表一定拥有远程 API 权限。主机服务商可能禁止 reseller 使用 XML API,也可能只允许其操作名下的订阅。开始开发前,应先向服务商确认权限范围。
推荐架构
可以采用下面的调用结构:
用户浏览器
↓ HTTPS
ASP.NET 网站
├── 验证当前用户
├── 查找用户对应的 Plesk subscription/webspace ID
├── 检查邮箱、FTP 等资源是否属于该用户
└── 调用 Plesk XML API
↓ HTTPS 8443
Plesk 10
不要让前端 JavaScript 直接连接以下地址:
https://plesk.example.com:8443/enterprise/control/agent.php
否则,Plesk 管理员或 reseller 的凭据会暴露给终端用户。
调用 Plesk XML API
Plesk 10 的 XML API RPC 通常位于:
https://plesk.example.com:8443/enterprise/control/agent.php
客户端通过 POST 发送 XML,并在 HTTP 请求头中提供 Plesk 登录凭据:
HTTP_AUTH_LOGIN: reseller-name
HTTP_AUTH_PASSWD: reseller-password
Content-Type: text/xml
部分安装环境也支持使用 Plesk 生成的 secret key,并通过 KEY 请求头完成认证。该方式能否使用,取决于服务器配置和当前账户的权限。
ASP.NET 调用封装
下面的示例适用于现代 ASP.NET 和 ASP.NET Core。旧版 ASP.NET Framework 也可以沿用相同的 HttpClient 调用思路。
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
public sealed class PleskApiClient
{
private readonly HttpClient _httpClient;
private readonly string _login;
private readonly string _password;
public PleskApiClient(
HttpClient httpClient,
string endpoint,
string login,
string password)
{
_httpClient = httpClient;
_httpClient.BaseAddress = new Uri(endpoint);
_login = login;
_password = password;
}
public async Task<XDocument> SendAsync(
XDocument packet,
CancellationToken cancellationToken = default)
{
using var request = new HttpRequestMessage(HttpMethod.Post, "");
request.Headers.TryAddWithoutValidation("HTTP_AUTH_LOGIN", _login);
request.Headers.TryAddWithoutValidation("HTTP_AUTH_PASSWD", _password);
request.Headers.TryAddWithoutValidation("HTTP_PRETTY_PRINT", "TRUE");
request.Content = new StringContent(
packet.ToString(SaveOptions.DisableFormatting),
Encoding.UTF8,
"text/xml");
using var response = await _httpClient.SendAsync(
request,
cancellationToken);
string responseXml = await response.Content.ReadAsStringAsync(
cancellationToken);
response.EnsureSuccessStatusCode();
var document = XDocument.Parse(responseXml);
// Plesk 有时会在 HTTP 200 响应中返回业务级错误,
// 因而不能只检查 HTTP 状态码。
var failedResult = document
.Descendants("result")
.FirstOrDefault(result =>
string.Equals(
(string?)result.Element("status"),
"error",
StringComparison.OrdinalIgnoreCase));
if (failedResult != null)
{
string code = (string?)failedResult.Element("errcode") ?? "unknown";
string message =
(string?)failedResult.Element("errtext") ??
"Unknown Plesk API error";
throw new InvalidOperationException(
$"Plesk API error {code}: {message}");
}
return document;
}
}
注册客户端时,应从安全配置中读取凭据,不要将密码直接写入源代码:
builder.Services.AddHttpClient<PleskApiClient>();
实际项目还需要设置连接超时,例如:
builder.Services.AddHttpClient("Plesk", client =>
{
client.Timeout = TimeSpan.FromSeconds(30);
});
查询订阅和空间使用量
在 Plesk 10 中,首先要确认当前网站用户对应的 Plesk 客户、订阅或 webspace。不要根据用户提交的域名直接执行操作,而应在自己的数据库中保存明确的映射关系,例如:
ApplicationUserId → PleskCustomerId → PleskWebspaceId
获取 webspace 信息和统计数据时,可以使用类似下面的 XML packet:
<?xml version="1.0" encoding="UTF-8"?>
<packet version="1.6.3.0">
<webspace>
<get>
<filter>
<id>123</id>
</filter>
<dataset>
<gen_info />
<hosting />
<stat />
</dataset>
</get>
</webspace>
</packet>
在 ASP.NET 中,可以用以下方式安全地构造 XML:
var packet = new XDocument(
new XElement("packet",
new XAttribute("version", "1.6.3.0"),
new XElement("webspace",
new XElement("get",
new XElement("filter",
new XElement("id", webspaceId)),
new XElement("dataset",
new XElement("gen_info"),
new XElement("hosting"),
new XElement("stat"))))));
XDocument response = await pleskClient.SendAsync(packet);
统计响应可能包含磁盘、流量和邮件等数据,具体字段会受到 Plesk 10 小版本、操作系统及 XML API 协议版本的影响。磁盘统计也未必是实时计算结果,有可能来自 Plesk 最近一次运行的统计任务。
部分 Plesk 10 环境使用 domain operator,较新的协议则使用 webspace operator。应先查询服务器支持的协议版本,再参照与该服务器匹配的 API Reference。不能假定示例中的 1.6.3.0 适用于所有 Plesk 10 安装。
创建邮箱
创建邮箱的请求通常采用以下结构:
<?xml version="1.0" encoding="UTF-8"?>
<packet version="1.6.3.0">
<mail>
<create>
<filter>
<site-id>123</site-id>
<mailname>
<name>sales</name>
<mailbox>
<enabled>true</enabled>
<quota>104857600</quota>
</mailbox>
<password type="plain">A-Strong-Password</password>
</mailname>
</filter>
</create>
</mail>
</packet>
site-id 必须由服务器端根据当前登录用户查询,不能直接接受浏览器传入的值。quota 通常以字节为单位,但仍需核对相应协议版本中的字段定义。
ASP.NET 构造示例:
var packet = new XDocument(
new XElement("packet",
new XAttribute("version", "1.6.3.0"),
new XElement("mail",
new XElement("create",
new XElement("filter",
new XElement("site-id", webspaceId),
new XElement("mailname",
new XElement("name", localPart),
new XElement("mailbox",
new XElement("enabled", "true"),
new XElement("quota", quotaBytes)),
new XElement("password",
new XAttribute("type", "plain"),
password)))))));
await pleskClient.SendAsync(packet);
创建邮箱前还要检查:
localPart是否符合邮箱名称规则;- 用户是否已经达到 hosting plan 允许的邮箱数量上限;
- 配额是否超出订阅允许值;
- 密码是否符合服务器的密码强度策略;
- 是否已经存在同名邮箱。
删除邮箱或修改密码
删除邮箱一般通过 mail/remove 完成:
<packet version="1.6.3.0">
<mail>
<remove>
<filter>
<site-id>123</site-id>
<name>sales</name>
</filter>
</remove>
</mail>
</packet>
修改邮箱密码通常使用 mail/set,结构可以参考下面的示例:
<packet version="1.6.3.0">
<mail>
<set>
<filter>
<site-id>123</site-id>
<mailname>
<name>sales</name>
<password type="plain">New-Strong-Password</password>
</mailname>
</filter>
</set>
</mail>
</packet>
不同 Plesk 10 小版本对 set packet 的元素结构可能有不同要求,需要按照服务器实际支持的 XML API schema 进行调整。
执行删除操作前,必须再次确认该邮箱属于当前用户的 webspace。可以增加二次确认,并记录操作者、邮箱地址、操作时间及 Plesk 返回结果。
创建或修改 FTP 账户
如果协议版本支持 ftp-user operator,可以使用类似下面的请求:
<packet version="1.6.3.0">
<ftp-user>
<add>
<name>customer_ftp</name>
<password>Strong-FTP-Password</password>
<home>/httpdocs/customer</home>
<webspace-id>123</webspace-id>
</add>
</ftp-user>
</packet>
修改一般使用 ftp-user/set,删除使用 ftp-user/remove。过滤器和属性的具体名称,应以目标 Plesk 服务器对应的 API Reference 为准。
实现 FTP 管理时,需要严格限制以下内容:
- FTP 主目录必须位于该订阅允许的目录内;
- 不能让用户通过
..或路径编码跳出指定目录; - 用户不能修改其他订阅的 FTP 账户;
- 登录名可能受到服务器前缀、长度和字符规则的限制;
- 部分 hosting plan 不允许创建额外的 FTP 用户;
- 能否修改主系统 FTP 用户,取决于账户权限和服务器配置。
不要通过字符串拼接生成 XML。使用 XElement 可以避免 XML 特殊字符引起格式错误或注入问题。
Billing、Invoice 和付款数据
Plesk Panel 中的 hosting subscription 与财务系统里的收费订阅不是同一类数据。Plesk 核心 XML API 主要管理服务器对象,未必能提供完整的发票、付款及交易记录。
需要先确认服务商使用的是哪套系统:
- 使用 Plesk Customer & Business Manager 或旧版 Plesk Billing 时,需要查询该组件对应版本的 API。
- 使用 WHMCS、HostBill、ClientExec 等第三方系统时,应调用相应的第三方 API。
- 如果发票由主机服务商统一管理,而 reseller 账户没有财务 API 权限,则需要向服务商申请接口。
- 如果财务数据来自自建系统,应由自建数据库提供。Plesk 只负责邮件、网站和 FTP 等资源配置。
不建议直接读取 Plesk 内部数据库。内部表结构不是稳定的公共接口,升级后可能发生变化。直接写入数据库还可能绕过 Plesk 自身的校验、事件处理和配置同步机制。
权限设计
安全上的基本原则是,终端用户只能操作与自己绑定的 Plesk 对象。
例如,网页收到下面的请求:
{
"webspaceId": 123,
"mailbox": "sales",
"newPassword": "..."
}
服务器不能因为请求中包含 webspaceId 就直接执行操作。应先检查该 ID 是否属于当前用户:
var ownedWebspaceId = await ownershipRepository
.GetWebspaceIdForUserAsync(currentUser.Id);
if (ownedWebspaceId != request.WebspaceId)
{
return Results.Forbid();
}
更稳妥的方式是,不允许前端传递 webspaceId,而是由服务器根据当前用户查询:
var webspaceId = await ownershipRepository
.GetWebspaceIdForUserAsync(currentUser.Id);
await mailService.ChangePasswordAsync(
webspaceId,
request.Mailbox,
request.NewPassword);
此外,还应落实以下措施:
- 将 Plesk 凭据保存在服务器端的 secret store 或受保护配置中;
- ASP.NET 页面必须使用 HTTPS;
- API endpoint 应使用有效的 TLS 证书;
- 不要为了连接旧服务器而全局关闭证书验证;
- 不要把密码写入日志、异常页面或审计详情;
- 对创建、修改密码和删除操作设置速率限制;
- 为所有修改操作保留审计记录;
- 删除邮箱等不可逆操作应增加确认步骤;
- 单独处理 Plesk 请求超时、重复请求和部分成功等情况。
建议的实施顺序
可以先开发一个只读原型,再逐步加入修改功能:
- 验证 reseller 账户能否连接 XML API。
- 查询该 reseller 可以看到的客户和订阅。
- 建立网站用户与 Plesk webspace ID 的映射。
- 读取邮箱列表和空间统计信息。
- 加入邮箱创建、密码修改和删除功能。
- 加入 FTP 管理,并严格限制主目录。
- 最后接入独立的 billing 系统。
Plesk API 经常在正常的 HTTP 响应中返回业务错误,因此还必须检查:
<status>error</status>
<errcode>...</errcode>
<errtext>...</errtext>
不能仅凭 200 OK 判断操作已经成功。
参考资料
可以查阅以下 Plesk XML API RPC 文档:
当前在线文档主要针对较新的 Plesk 版本。集成 Plesk 10 时,应优先使用与服务器具体小版本对应的归档文档。XML packet 需要先在测试订阅中验证,再开放给终端用户。
备注:内容仅供参考。
