[C#] Java servlet에서 C#의 RestAPI를 통해 통신하는 방법


Development note/C#  2020. 7. 17. 18:28

안녕하세요. 명월입니다.


이 글은 Java servlet에서 C#의 RestAPI를 통해 통신하는 방법에 대한 글입니다.


이전에 블로그 방명록에 하나의 요청이 있었습니다.

Jsp에서 C#으로 만든 Window Form 형식의 프로그램에 데이터를 보내고 싶은데 방법이 없어서 C#에서 Jsp API로 30초 단위로 호출하는 방법으로 데이터를 요청합니다.

그러나 이런 방식이 조금 비효율이라서 Jsp 웹페이지를 클릭하면 C# Winform 프로그램에 데이터를 보내는 방법을 알려달라고 문의하는 글이 있었습니다.


처음에는 제가 굳이 웹 API를 사용하는 이유나 소켓을 사용하면 간단하게 될 것이라고도 설명을 했습니다만 이 분께서 Web Protocal를 이용하고 싶다라고 해서 사양에 마다 이유가 있을 듯하니 거기까지 인식했습니다.

그 때 제가 조사하기 전이라 단순히 Soap를 사용하면 간단할 줄 알았는데 Soap도 IIS에서 동작하는 Web service였습니다. 목적은 WinForm에서 만드는 것이기 때문에 역시 Soap도 안되네요.


그러다 찾아보니 Console 환경에서 WebService를 하는 라이브러리에 있었습니다.

링크 - [C#] 콘솔로 RestAPI 서버를 만드는 방법


그래서 Jsp에서 버튼을 클릭하면 C#으로 메시지를 보내는 프로그램을 만들어 보겠습니다.


먼저 자바 웹서버는 구현하기 쉽게 Jsp servlet으로 구현했습니다.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
  <!-- submit 버튼을 누르면 Send 페이지로 -->
  <form method="POST" action="Send">
    <!-- Servlet에서 보낼 url 주소 -->
    Url : <input type="text" name="url" value="http://127.0.0.1:8081/data"><br />
    <!-- 메시지 내용 -->
    Message : <input type="text" name="msg" value="message"><br />
    <!-- submit 버튼 -->
    <input type="submit">
  </form>
</body>
</html>

위 index.jsp에서 submit 버튼을 누르면 Send 서블릿 페이지로 이동합니다.

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// Send 서블릿
@WebServlet("/Send")
public class Send extends HttpServlet {
  // 직렬화 키
  private static final long serialVersionUID = 1L;
  // 생성자
  public Send() {
    super();
  }
  // Web method가 Get일 경우
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    // 화면에 No page를 리턴한다.
    response.getWriter().append("No page");
  }
  // Web method가 Post일 경우
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    // form의 url 값을 받는다.
    String url = request.getParameter("url");
    // form의 msg 값을 받는다.
    String msg = request.getParameter("msg");
    // C# Window로 Request 한다.
    String ret = getRequest(url, msg);
    // 결과를 콘솔에 표시한다.
    System.out.println(ret);
    // index.jsp로 이동한다.
    response.sendRedirect("./");
  }
  // 요청 함수
  public static String getRequest(String url, String parameter) {
    try {
      // 데이터를 json 형식 값으로 보낸다.
      // 예제로 간략하게 만들었습니다만 Gson을 이용하면 변환할 수 있습니다.
      String param = "{\"param\":\"" + parameter + "\"} ";
      // url를 인스턴스를 만든다.
      URL uri = new URL(url);
      // HttpURLConnection 인스턴스를 가져온다.
      HttpURLConnection connection = (HttpURLConnection) uri.openConnection();
      // Web Method는 Post 타입
      connection.setRequestMethod("POST");
      // Json 형식으로 보내고
      connection.setRequestProperty("ContentType", "application/json");
      // Header 밑에 값을 보내기 위한 스트립을 받는다.
      connection.setDoOutput(true);
      try (DataOutputStream output = new DataOutputStream(connection.getOutputStream())) {
        output.writeBytes(param);
        output.flush();
      }
      // 요청한다. 200이면 정상이다.
      int code = connection.getResponseCode();
      if (code == 200) {
        // 응답 온 body 내용의 stream을 받는다.
        try (BufferedReader input = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
          String line;
          StringBuffer buffer = new StringBuffer();
          while ((line = input.readLine()) != null) {
            buffer.append(line);
          }
          // 리턴한다.
          return buffer.toString();
        }
      }
      // 에러 코드 값 반환
      return code + "";
    } catch (Throwable e) {
      // 에러를 RuntimeException으로 변환
      throw new RuntimeException(e);
    }
  }
}

기동을 해도 버튼을 누르면 아직 C#의 RestAPI 서버를 만들지 않았기 때문에 에러가 발생합니다.

C#은 예전에 제가 콘솔에서 RestAPI를 만드는 방법에 대해 설명한 적이 있습니다.

링크 - [C#] 콘솔로 RestAPI 서버를 만드는 방법


위는 기본적으로 콘솔이니 이번에는 C# Winform에서 작성해 봅시다.

먼저 라이브러리를 추가하겠습니다. 필요한 라이브러리는 Newtonsoft.Json과 System.ServiceModel, System.ServiceModel.Web이 있습니다.

Newtonsoft.Json는 Nuget으로 라이브러리를 다운 받아서 연결합니다.

System.ServiceModel, System.ServiceModel.Web의 경우는 .Net Framwork 라이브러리이기 때문에 그냥 Reference 참조로 라이브러리를 참조합니다.


WindowForm 프로젝트도 App.config이 있습니다.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
  </startup>
  <system.serviceModel>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
    <!-- 바인딩 설정  -->
    <bindings>
      <webHttpBinding>
        <binding name="webHttp" openTimeout="00:10:00" closeTimeout="00:10:00" sendTimeout="00:10:00" receiveTimeout="01:00:00" maxBufferPoolSize="2147483647">
          <security mode="None">
          </security>
        </binding>
      </webHttpBinding>
    </bindings>
    <!-- 서비스 설정 -->
    <services>
      <!-- 서비스 클래스 설정 -->
      <service name="RestAPIFromJsp.Service">
        <!-- 서비스 인터페이스 설정 -->
        <endpoint address ="rest" binding="webHttpBinding" bindingConfiguration="webHttp" contract="RestAPIFromJsp.IService" behaviorConfiguration="web"></endpoint>
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8081" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="mexBehaviour">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="web">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
  </system.serviceModel>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

그리고 App.config에 지정했던 서비스 인터페이스와 클래스를 만듭니다.

using System.ServiceModel;
using System.ServiceModel.Web;
using System.IO;
namespace RestAPIFromJsp
{
  // 서비스 지정
  [ServiceContract]
  interface IService
  {
    [OperationContract]
    // 메소드 지정과 리턴 포멧 지정(리턴 포멧은 RestAPI를 만들 것이기 때문에 Json으로 한다.)
    // UriTemplate에 보간법으로 중괄호로 값을 지정할 수 있다.
    [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped, UriTemplate = "/data")]
    // 이 어트리뷰트를 설정하지 않으면 기본 함수명을 키로 리턴이 됩니다. 저는 키를 value로 지정했다.
    [return: MessageParameter(Name = "value")]
    // Form 데이터를 받아야 하기 때문에 Stream으로 받는다.
    string GetData(Stream data);
  }
}
using Newtonsoft.Json;
using System.IO;
namespace RestAPIFromJsp
{
  // 파라미터 클래스
  public class Parameter
  {
    public string Param { get; set; }
  }
  // 구현 클래스
  class Service : IService
  {
    // Stream 형식을 제네릭을 이용해 클래스로 변환한다.
    private T GetParameter<T>(Stream data)
    {
      using (StreamReader reader = new StreamReader(data))
      {
        // 스트림를 string으로 변환
        var json = reader.ReadToEnd();
        // json형식의 string를 제네릭으로 클래스로 변환
        return JsonConvert.DeserializeObject<T>(json);
      }
    }
    // /data의 post method 형식로 접속되면 호출되어 처리한다.
    public string GetData(Stream data)
    {
      // form 데이터를 Parameter 클래스로 변환
      var param = GetParameter<Parameter>(data);
      // 폼에 표시
      Form1.SetText(param.Param);
      // 결과는 OK
      return "OK";
    }
  }
}

그리고 폼을 설정하겠습니다.

using System;
using System.Windows.Forms;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace RestAPIFromJsp
{
  public partial class Form1 : Form
  {
    // 외부에서 접속하기 위한 mainForm
    private static Form1 mainForm;
    public Form1()
    {
      InitializeComponent();
      // 호출되면 설정한다.
      Form1.mainForm = this;
    }
    // Form이 Load되면 WebServer를 기동한다.
    protected override void OnLoad(EventArgs e)
    {
      base.OnLoad(e);
      // 서버 인스턴스 생성
      var server = new WebServiceHost(typeof(Service));
      // EndPoint 설정
      server.AddServiceEndpoint(typeof(IService), new WebHttpBinding(), "");
      // 서버 기동
      server.Open();
    }
    // richTextBox에 텍스트 설정
    public static void SetText(string text)
    {
      // 동기화 Invoke 함수 호출
      mainForm.InvokeControl(mainForm.richTextBox1, () =>
      {
        // 텍스트 설정
        mainForm.richTextBox1.Text = mainForm.richTextBox1.Text + text + " \r\n";
        // 가장 아래쪽의 스크롤로 이동
        mainForm.richTextBox1.SelectionStart = mainForm.richTextBox1.TextLength;
        mainForm.richTextBox1.ScrollToCaret();
      });
    }
    // Window 폼에서 동기화를 해주는 InvokeControl 함수
    public void InvokeControl(Control ctl, Action func)
    {
      if (ctl.InvokeRequired)
      {
        ctl.Invoke(func);
      }
      else
      {
        func();
      }
    }
    // Window 폼에서 동기화를 해주는 InvokeControl 함수(제네릭 타입)
    public T InvokeControl<T>(Control ctl, Func<T> func)
    {
      if (ctl.InvokeRequired)
      {
        return (T)ctl.Invoke(func);
      }
      else
      {
        return func();
      }
    }
  }
}

기동을 하겠습니다.

이제 다시 Jsp 웹 페이지로 가서 메시지 내용을 넣고 submit 버튼을 누릅니다.

Window에서 값을 받아서 값이 Window 폼에 표시되는 것을 확인할 수 있습니다.

다시 eclipse 콘솔에서도 결과가 표시되는 것을 확인할 수 있습니다.

여기까지 Java servlet에서 C#의 RestAPI를 통해 통신하는 방법에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.