NetHttpClient를 사용한 POST 요청 시 발생하는 문제 해결

최근 API 테스트를 진행하던 중 NetHttpClient를 사용하여 데이터 전송 작업을 수행했습니다. GET 방식으로 데이터를 전송하는 것은 문제없이 동작했지만, 특정 데이터를 POST 방식으로 전송할 때 오류가 발생했습니다. 패킷 캡처 도구를 사용한 결과, 데이터가 실제로 전송되지 않는 것을 확인할 수 있었습니다. 코드상의 문제점을 찾지 못해 해결 방법을 요청드립니다.

문제가 발생하는 핵심 코드는 다음과 같습니다:

procedure TTabbedwithNavigationForm.Button3Click(Sender: TObject);
var joj, jodata: TJSONObject;
    jadata: TJSONArray;
    ss: TStringStream;
    aresponse: IHttpResponse;
begin
  .........
  ss := TStringStream.Create('', TEncoding.UTF8);
  ss.WriteString(jodata.ToString);
  ShowMessage(IntToStr(length(jodata.ToString)));

  NetHttpClient1.Accept := '*/*';
  NetHttpClient1.AcceptEncoding := 'gzip,deflate';
  NetHttpClient1.AcceptLanguage := 'zh-CN';
  NetHttpClient1.UserAgent := 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko';
  NetHttpClient1.ContentType := 'application/json';
  NetHttpClient1.CustomHeaders['Authorization'] := au_edit.Text;

  aresponse := NetHttpClient1.Post(lbltitle4.Text, ss);
  memo3.Lines.Add(aresponse.ContentAsString());

  ss.Free;
end;

위 코드를 실행한 후 다음과 같은 오류 메시지가 반환되었습니다:

{"timestamp":"2019-06-14 16:53:37","status":400,"error":"Bad Request","exception":"org.springframework.http.converter.HttpMessageNotReadableException","message":"Required request body is missing: public com.qianfan123.sail.inf.service.ResponseResult<java.util.List<com.qianfan123.sail.inf.service.mbr.coupon.v2.CouponVT>> com.qianfan123.sail.inf.server.mbr.coupon.v2.CouponServiceVTImpl.issue(java.lang.String,java.lang.String,java.lang.String,com.qianfan123.sail.inf.service.mbr.coupon.v2.CouponIssueVTDTO)","path":"/api/mbr/v2/coupon/issue"}

패킷 캡처 도구를 통해 분석한 정상적으로 실행되는 세션 정보는 다음과 같습니다(별표는 생략된 부분입니다):

POST http://*************/api/mbr/v2/coupon/issue HTTP/1.1
Content-Type: application/json
Accept: */*
Authorization: *************
Referer: http://*************/swagger-ui.html
Accept-Language: zh-CN
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
Content-Length: 366
Host: ****.****.****:6020
Connection: Keep-Alive
Pragma: no-cache

{"event":"string","lines":[{"amount":"1000","beginDate":"2019-06-14 16:02:14","categoryCode":"000275","code":"20190614160214","count":"1","endDate":"2019-06-14 16:02:14"}],"memberId":"135057337785253888","occuredAt":"****","occuredTime":"2019-06-14 16:02:14","orgCode":"****","ownerActivity":"pos","summary":"테스트","terminal":"01","transId":"20190614160214"}
HTTP/1.1 200
X-Application-Context: member-inf:10000
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Fri, 14 Jun 2019 08:04:27 GMT

1d7
{"status":"0000","message":"요청 성공","data":[{"orgCode":"****","memberId":"********","code":"20190614160214","categoryId":"****4","categoryCode":"*****","name":"TIMTEST","type":"cash","facedAmt":1000,"amount":1000,"effectBegin":"2019-06-14 00:00:00","effectEnd":"2019-06-14 23:59:59","state":"사용 가능","fixed":true,"discount":null,"storeCode":null,"useLimitAmount":null,"isRegifted":-1,"limitRegiftedNum":null,"remainRegiftedNum":null}]}
0

오류가 발생한 패킷 정보는 다음과 같습니다(별표는 생략된 부분입니다):

POST http://*.*.*.*:6020/api/mbr/v2/coupon/issue HTTP/1.1
Content-Type: application/json
Accept: */*
Authorization: *************
Accept-Language: zh-CN
Accept-Encoding: gzip,deflate
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
Host: *.*.*.*:6020
Content-Length: 0----------
Connection: Keep-Alive

HTTP/1.1 400
X-Application-Context: member-inf:10000
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Fri, 14 Jun 2019 08:21:33 GMT
Connection: close

21f
{"timestamp":"2019-06-14 16:21:33","status":400,"error":"Bad Request","exception":"org.springframework.http.converter.HttpMessageNotReadableException","message":"Required request body is missing: public com.qianfan123.sail.inf.service.ResponseResult<java.util.List<com.qianfan123.sail.inf.service.mbr.coupon.v2.CouponVT>> com.qianfan123.sail.inf.server.mbr.coupon.v2.CouponServiceVTImpl.issue(java.lang.String,java.lang.String,java.lang.String,com.qianfan123.sail.inf.service.mbr.coupon.v2.CouponIssueVTDTO)","path":"/api/mbr/v2/coupon/issue"}
0

분석 결과, 오류 부분에는 다음과 같은 부분이 누락되어 있었습니다:

  • Pragma: no-cache
  • 실제 JSON 데이터

즉, 요청 본문 데이터가 전송되지 않은 것으로 확인됩니다.

동일한 문제를 겪은 다른 개발자의 해결책은 요청 본문(body)이나 헤더(header) 문제가 아니라 데이터 스트림에 데이터를 쓴 후 Position을 0으로 설정해야 한다는 것이었습니다:

ss := TStringStream.Create('', TEncoding.UTF8);
ss.WriteString(jodata.ToString);
ss.Position := 0;

이 방법은 데이터를 스트림에 쓴 후 커서(포지션)가 스트림의 끝에 위치하기 때문에 발생하는 문제입니다. Position을 0으로 설정하면 스트림의 시작점으로 이동하여 데이터가 올바르게 읽히게 됩니다.

또 다른 해결책은 TStringStream을 생성할 때 바로 문자열을 전달하는 것입니다:

ss := TStringStream.Create(jodata.ToString, TEncoding.UTF8);
// Position은 현재 0입니다.

이 방법은 더 간결하며 Position을 수동으로 설정할 필요가 없습니다.

태그: Delphi NetHttpClient POST TStringStream HTTP

6월 25일 17:57에 게시됨