ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • netty 간단한 http client, application/json 요청하기
    프로그래밍/buycycle 2021. 4. 8. 22:40
    반응형

      buycycle를 활용하면 증권사 api를 http post 방식으로 요청 및 응답을 받을 수 있다.

     

    buycycle.name

    'Buycycle'은 증권사 API를 HTTP Json으로 요청 및 응답 받을 수 있습니다. 요청 받은 Json 메시지를 증권사 API 양식에 맞게 변환해 주는 자바 기반의 오픈 소스 입니다. HTTP RESTful을 제공함으로써 사용자

    buycycle.name

     

      네티를 활용하여 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 부분에 담긴 데이터를 출력 한다.

    반응형

    댓글

Designed by Tistory.