Các dịch vụ REST dựa trên kbmMW Smart Service hỗ trợ nhiều cách tiếp nhận dữ liệu qua phương thức POST. Bài viết này trình bày chi tiết các kiểu dữ liệu phổ biến được gửi từ client và cách khai báo, xử lý tương ứng trong mã nguồn server.
Dữ liệu được nhúng trực tiếp vào URI dưới dạng phần tử đường dẫn:
Dữ liệu được truyền qua chuỗi tham số sau dấu chấm hỏi:
Body chứa cặp khóa–giá trị được mã hóa URL:
Body chứa cấu trúc dữ liệu có định dạng chuẩn:
Body được chia thành nhiều phần riêng biệt, mỗi phần có header riêng và phân tách bởi boundary.
Sử dụng cú pháp `{tên}` trong thuộc tính `path`, kết hợp với annotation `value:"{tên}"`:
2. Lấy tham số từ query string
Dùng ký hiệu `$` để chỉ định tham số truy vấn:
3. Đọc toàn bộ body dạng form-urlencoded
Khai báo tham số nhận toàn bộ nội dung body, sau đó phân tích bằng
Triển khai hàm:
4. Xử lý JSON/XML bằng lớp dữ liệu mạnh kiểu
Định nghĩa class tương ứng với cấu trúc dữ liệu đầu vào:
kbmMW tự động deserialize JSON/XML thành thể hiện của
Sử dụng
Các dạng dữ liệu POST thường gặp
1. Giá trị đường dẫn (Path parameters)Dữ liệu được nhúng trực tiếp vào URI dưới dạng phần tử đường dẫn:
POST http://localhost/myservice/process/42/1984
2. Tham số truy vấn (Query string)Dữ liệu được truyền qua chuỗi tham số sau dấu chấm hỏi:
POST http://localhost/myservice/process?id=42&code=1984
3. Dữ liệu dạng form-urlencoded trong bodyBody chứa cặp khóa–giá trị được mã hóa URL:
key1=value1&key2=value2
4. Dữ liệu JSON hoặc XML trong bodyBody chứa cấu trúc dữ liệu có định dạng chuẩn:
{"key1":"value1","key2":"value2"}
5. Dữ liệu multipart – thường dùng cho upload fileBody được chia thành nhiều phần riêng biệt, mỗi phần có header riêng và phân tách bởi boundary.
Cách triển khai trong kbmMW
1. Xử lý giá trị từ đường dẫnSử dụng cú pháp `{tên}` trong thuộc tính `path`, kết hợp với annotation `value:"{tên}"`:
[kbmMW_Rest('method:post, path: "process/{id}/{code}"')]
function ProcessData(
[kbmMW_Rest('value:"{id}"')] AId: Integer;
[kbmMW_Rest('value:"{code}"')] ACode: Integer): string;Dùng ký hiệu `$` để chỉ định tham số truy vấn:
[kbmMW_Rest('method:post, path: "process"')]
function ProcessData(
[kbmMW_Rest('value:"$id"')] AId: Integer;
[kbmMW_Rest('value:"$code"')] ACode: Integer): string;Khai báo tham số nhận toàn bộ nội dung body, sau đó phân tích bằng
TkbmMWHTTPQueryValues:
[kbmMW_Rest('method:post, path: "process"')]
function ProcessData([kbmMW_Rest('value:body')] ARawBody: string): string;var
qv: TkbmMWHTTPQueryValues;
begin
qv := TkbmMWHTTPQueryValues.Create;
try
qv.AsEncodedString := ARawBody; // Dùng AsEncodedString nếu body đã được URL-encode
Result := Format('ID=%s, Code=%s', [
qv.ValueByName['id'],
qv.ValueByName['code']
]);
finally
qv.Free;
end;
end;Định nghĩa class tương ứng với cấu trúc dữ liệu đầu vào:
[kbmMW_Root('payload', [mwrfIncludeOnlyTagged])]
TRequestPayload = class
private
FId: Integer;
FCode: string;
public
[kbmMW_Attribute('id')]
property Id: Integer read FId write FId;
[kbmMW_Attribute('code')]
property Code: string read FCode write FCode;
end;
[kbmMW_Rest('method:post, path: "process"')]
function ProcessData([kbmMW_Rest('value:body')] const APayload: TRequestPayload): string;TRequestPayload, không cần viết logic phân tích thủ công.
5. Phân tích multipart body (upload file)Sử dụng
TkbmMWHTTPMultiParts để tách và xử lý từng phần:
[kbmMW_Rest('method:post, path: "upload"')]
function HandleUpload: string;
function TMyService.HandleUpload: string;
var
mp: TkbmMWHTTPMultiParts;
part: TkbmMWHTTPMultiPart;
headers: TkbmMWHTTPMimeHeaderValueFields;
boundary: string;
fs: TFileStream;
fileName: string;
helper: TkbmMWHTTPTransportStreamHelper;
begin
Result := 'No upload detected';
helper := TkbmMWHTTPTransportStreamHelper(RequestTransportStream.Helper);
headers := helper.Header.ValueFields['Content-Type'];
if not Assigned(headers) then Exit;
boundary := headers.ValueByName['boundary'];
if boundary = '' then Exit;
mp := TkbmMWHTTPMultiParts.Create(RequestStream, boundary);
try
for var i := 0 to mp.Count - 1 do
begin
part := mp.Parts[i];
headers := part.Headers.ValueFields['Content-Disposition'];
fileName := headers.ValueByName['filename'];
if fileName <> '' then
begin
fileName := '.\uploads\' + TPath.GetFileName(fileName);
TDirectory.CreateDirectory('.\uploads');
fs := TFileStream.Create(fileName, fmCreate);
try
part.SaveToStream(fs);
Result := Format('File saved: %s', [fileName]);
finally
fs.Free;
end;
Break;
end;
end;
finally
mp.Free;
end;
end;