데브기어 포럼에 등록된 이슈 공유합니다.
https://welcome.devgear.co.kr/topic/227-delphi-11-rest-client-동작-오류-문의드립니다/
<질문 요약>
11.0에서 REST 클라이언트로 post로 요청시, 서버단에서 post로 못받고 get으로 인지를 하는 문제입니다.
개요 : 10.4.2 에서 잘 되던 앱이 11 버전 RestClient POST 방식에서 문제가 되어 여러가지 테스트 해본 결과 POST로 파라미터를 요청을 하면 서버쪽에서 POST로 파라미터 값을 못 받는 현상입니다.
<답변>
질문하신 내용을 요약하면 "델파이 11에서 POST로 요청 시 GET으로 메소드를 호출되는 이슈가 있다.로 이해됩니다.
질문의 답변에 앞서,
요구사항을 확인하면 "이미지 데이터를 서버로 전달"하는 것으로 보여 다음 2가지 답변을 드립니다.
1) POST 요청 시 GET으로 호출하는 원인 확인 및 조치사항(질문에 대한 답변)
2) REST 클라이언트로 이미지를 업로드하는 방안(요구사항 솔루션)
1) POST 요청 시 GET으로 호출하는 원인 확인 및 조치사항
우선 현상과 원인 파악을 위해 PHP 페이지를 다음과 같이 작성 후 다시 시도해보시기 바랍니다.(정확히 어떤 메소드로 호출하는지 확인할 수 있습니다.)
<?php
$GetId = $_GET['id'];
$PostId = $_POST['id'];
echo $_SERVER["REQUEST_METHOD"]."<br />\n"; // HTTP 메소드(GET / POST)
echo "Get : ".$GetId."<br />\n";
echo "Post : ".$PostId."<br />\n";
echo "body: ".$HTTP_RAW_POST_DATA."<br />\n";
echo "body: ".file_get_contents("php://input")."<br />\n";
?>
제가 테스트한 결과는 다음과 같습니다.
두가지 버전 모두 POST로 요청함을 확인했습니다.
(응답 Body의 첫번째 항목은 HTTP 메소드 종류이며, 둘다 POST로 출력되었습니다.)
하지만 전송한 요청 파라미터(GETorPOST 파라미터)는, 10.4.2에서는 POST($_POST) 데이터로 11.0에서는 GET($_GET)으로 전달됩니다.
결과적으로, 두 버전간 전달하는 방식에 차이가 있는 것을 확인했습니다.
두 버전이 다른 결과가 나온 내용을 소스코드에서 분석한 내용을 설명합니다.(간단히 설명하니 참고만 하고, 아래의 결론의 내용을 적용하시기 바랍니다.)
11.0에서 REST 클라이언트의 주요 변경사항 중 ContentType을 문자열로 처리하도록 개선되었습니다.(상당히 많은 양의 코드가 변경되었습니다.)
특히, 파라메터 전송 방식을 판단하는 IsQueryParam은 아래와 같이 변경되었습니다.(좌: 10.4.2, 우: 11.0)
빨간 박스를 보면 파라메터의 컨텐트타입(APram.ContentType)이 ctNone(공백)인 경우 쿼리 파라메터로 인식되어 요청시 Get 형식의 파라메터가 전송됩니다.
위 로직을 피하기 위해서는 다음과 같은 조치를 취해야 합니다.
- TRESTRequest.Params 속성 선택
- POST 파라메터로 전달하려는 파라메터의 ContentTypeStr을 "multipart/form-data" 지정
위와 같이 지정 후 REQUEST 실행하면 다음과 같이 10.4.2와 동일한 방식으로 POST 파라메터로 전송되는 것을 확인할 수 있습니다.
해당 조치는 REST Debugger에서는 진행할 수 없으니, 소스코드 상에서 조치해야 합니다.
결론:
델파이 11.0에서 POST 요청 시 파라메터를 POST 파라미터로 전달하려면, 파라메터의 ContentTypeStr을 "multipart/form-data"로 지정해야 합니다.
2) REST 클라이언트로 이미지를 업로드하는 방안
이미지를 Base64로 인코딩해 문자열로 전송하셨습니다. 멀티파트 폼데이터(multi-part/formdata)로 전송하는 방식도 검토해 보시기 바랍니다.
멀티파트 폼데이터로 전송 시 Stream을 그대로 파라미터로 설정할 수 있습니다.
다음은 클라이언트에서 멀티파트-폼데이터로 전송하는 샘플 코드입니다.
(파라미터에 파일(pkFILE)이 포함된 경우 multi-part/formdata로 전송됩니다.)
var
Stream: TMemoryStream;
Item: TRESTRequestParameter;
begin
if not OpenDialog1.Execute then
Exit;
Stream := TMemoryStream.Create;
Stream.LoadFromFile(OpenDialog1.FileName);
// 파라메터는 디자인타임에 생성되었습니다.
// Item := RESTRequest1.Params.AddItem;
// Item.Name := 'img';
// Item.Kind := pkFILE;
RESTRequest1.Params.ParameterByName('img').SetStream(Stream);
RESTRequest1.Method := rmPOST;
RESTRequest1.Execute;
end;
멀티파트 폼데이터로 전송 시 웹서버에서 파일 전송과 동일한 방식으로 받아 처리할 수 있습니다.
RAD 서버 11.0에서는 다음 코드를 이용해 멀티파트 폼데이터를 처리할 수 있습니다.
procedure TImgResource1.Post(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
var
Stream: TStream;
begin
Stream := ARequest.Body.GetPart('img', '').GetStream;
if Stream.Size <= 0 then
AResponse.RaiseBadRequest('no stream');
TMemoryStream(Stream).SaveToFile('D:\Temp\test.jpg');
end;
위 샘플코드 프로젝트입니다.
결론:
이미지 등의 파일 업로드 구현 시 멀티파티 폼데이터로 전송할 수도 있습니다.
(델파이의 일반적인 데이터구조인 Stream을 바로 전송할 수 있어 별도 인코딩 없이 간단히 구현할 수 있습니다.)
두가지 방식을 안내드렸습니다.
제가 제안하는 방식은,
업로드 방식을 변경할 수 있다면 2번항목을 참고해 업로드 방식을 변경하시길 권장드리며,
서버의 인터페이스를 변경하지 못하는 경우 1번항목을 참고해 적용하시기 바랍니다.