-
netty 간단한 http client, application/json 요청하기프로그래밍/buycycle 2021. 4. 8. 22:40반응형
buycycle를 활용하면 증권사 api를 http post 방식으로 요청 및 응답을 받을 수 있다.
네티를 활용하여 buycycle에 http post application/json 요청하는 예제이다. 간단한 http 요청이기에 비교적 소스는 간결하다. channelpipeline 에 등록된 핸들러는 Http 코덱을 위한 HttpClientCodec과 응답에 대한 처리를 구현한 ResponseHandler 두 종류이다. 보통 코덱은 인코더와 디코더로 구분하여 등록하나 HttpClientCodec은 CombinedChannelDuplexHandler 상속 받은 핸들러이기에 인코더와 디코더가 함께 있다.
package test; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.http.*; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; public class NettyHttpRequest { public static void main(String[] args){ EventLoopGroup group = new NioEventLoopGroup(); Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group); bootstrap.channel(NioSocketChannel.class); bootstrap.handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpClientCodec()); pipeline.addLast(new NettyHttpResponseHandler()); } }); String content = "{" + " \"body\": {" + " \"trName\": \"t1511\"," + " \"bNext\": false," + " \"query\": {" + " \"upcode\": \"001\"" + " }" + " }," + " \"header\": {" + " \"uuid\": \"7b81c375-d9b9-43c1-8449-77e561a979f2\"" + " }" + "}"; try { URI uri = new URI("http://localhost:7771/data/ebest/query"); FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uri.getRawPath()); request.headers().set(HttpHeaderNames.HOST, uri.getHost()); request.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON); request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE); ByteBuf byteBuf = Unpooled.copiedBuffer(content, StandardCharsets.UTF_8); request.headers().set(HttpHeaderNames.CONTENT_LENGTH, byteBuf.readableBytes()); request.content().writeBytes(byteBuf); Channel ch = bootstrap.connect(uri.getHost(), uri.getPort()).sync().channel(); ch.writeAndFlush(request); ch.closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } catch (URISyntaxException e) { e.printStackTrace(); } finally { group.shutdownGracefully(); } } }
DefaultFullHttpRequest 객체 생성 시 Http 버전 정보와, method, raw path 파라미터를 함께 넣을 수 있다. http body에 해당하는 content 파라미터도 넣을 수 있으나 해당 예제에선 request.content().writeBytes() 메소드를 이용하였다.
Http header 정보로 Host 값과 content type, connection 정보도 같이 넣어주었다. chunked 전송이 아니므로 content_length 정보도 꼭 넣어주어야 한다. chunked 전송시 io.netty.handler.codec.http.HttpUtil.setTransferEncodingChunked(); 이용시 setTransferEncodingChunked 메소드의 소스를 보면 content_length header 정보는 삭제하는 걸 알 수 있다.
다음은 인바운드에 대한 handler 구현체이다.
package test; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.*; import io.netty.util.CharsetUtil; public class NettyHttpResponseHandler extends SimpleChannelInboundHandler<HttpObject> { @Override protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception { if (msg instanceof HttpResponse) { HttpResponse response = (HttpResponse) msg; System.out.println("STATUS: " + response.status()); System.out.println("VERSION: " + response.protocolVersion()); System.out.println(); if (!response.headers().isEmpty()) { for (CharSequence name: response.headers().names()) { for (CharSequence value: response.headers().getAll(name)) { System.out.println("HEADER: " + name + " = " + value); } } System.out.println(); } System.out.println("CONTENT ["); } if (msg instanceof HttpContent) { HttpContent content = (HttpContent) msg; System.out.print(content.content().toString(CharsetUtil.UTF_8)); System.out.flush(); if (content instanceof LastHttpContent) { System.out.println(); System.out.println("] END OF CONTENT"); ctx.close(); } } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
해당 핸들러를 디버깅 해보면 channelRead0 메소드가 3번 호출 되는걸 알 수 있다. 처음으로 DefaultHttpReponse instance 가 호출 되고 그 다음에 DefaultHttpContent 그 다음으로 EmptyLastHttpContent이다. EmptyLastHttpContent 객체는 실제로 데이터는 없고 마지막을 알리는 플래그 값이라고 이해하면 쉽다. DefaultHttpReponse에서는 헤더에 대한 정보를 출력하고 DefaultHttpContent에서는 Body 부분에 담긴 데이터를 출력 한다.
반응형'프로그래밍 > buycycle' 카테고리의 다른 글
eBest xing api RESTful 요청 예제 (14) 2021.03.29 eBest xing api를 http json으로 요청 응답 하기 (0) 2021.03.22