재우니의 블로그

 

 

application/x-www-form-urlencoded 형태의 BODY 파라미터로 web api 호출

 

 

 

ASP.NET MVC 의 WEB API 를 활용하여 content-type 이 application/x-www-form-urlencoded 형식 즉 '키=값&키=값' 형태로 데이터를 post 형식으로 외부에서 전송 받을 경우, 키=값 다중의 데이터를 FormDataCollection 객체를 통해 쉽게 얻을 수 있습니다.

 

[AllowAnonymous]
public class ValueController : ApiController
{
    [AllowAnonymous]
    [HttpPost]
    public string Authenticate(FormDataCollection form)
    {
        var id = form.Get("id");
        var pw = form.Get("pw");

        return id + "_" + pw;
    }
}

 

 

아래 2개의 샘플 예제는 http://도메인/api/Value/Authenticate 의 web api 를 호출하기 위한 wapper 클래스로 보시면 됩니다. 

 

 

첫번째 샘플예제 

 

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;

namespace UnivDefault.Web.Helpers
{
    /// <summary>
    /// 사용법
    /// MyWebRequest myRequest = new MyWebRequest("http://도메인/api/Value/Authenticate", "POST", "id=shims&pw=pwd");
    /// var res = myRequest.GetResponse();
    /// </summary>
    public class MyWebRequest
    {
        private WebRequest request;
        private Stream dataStream;

        public string Status { get; set; }

        public MyWebRequest(string url)
        {
            // post 로 받을 수 있는 URL 을 사용하여 REQUEST 객체를 생성한다.
            request = WebRequest.Create(url);
        }

        public MyWebRequest(string url, string method)
            : this(url)
        {

            if (method.Equals("GET") || method.Equals("POST"))
            {
                // REQUEST 의 METHOD 속성을 POST 또는 GET 으로 설정한다.
                request.Method = method;
            }
            else
            {
                throw new Exception("Invalid Method Type");
            }
        }

        public MyWebRequest(string url, string method, string data)
            : this(url, method)
        {

            // POST 로 보낼 DATA 를 BYTE 배열로 변환하여 담는다.
            string postData = data;
            byte[] byteArray = Encoding.UTF8.GetBytes(postData);

            // WebRequest 의 ContentType 속성에 설정한다.
            request.ContentType = "application/x-www-form-urlencoded";

            // WebRequest 의 ContentLength 속성에 설정한다.
            request.ContentLength = byteArray.Length;

            // REQUEST 의 stream 을 가져온다.
            dataStream = request.GetRequestStream();

            // data 를 REQUEST 의 stream 에 Write 한다.
            dataStream.Write(byteArray, 0, byteArray.Length);

            // Stream 객체를 닫는다.
            dataStream.Close();

        }

        public string GetResponse()
        {
            // 요청에 대한 응답을 받는다.
            WebResponse response = request.GetResponse();

            // 응답에 대한 상태설명 값을 얻는다.
            this.Status = ((HttpWebResponse)response).StatusDescription;

            // 요청받은 서버에 의해 반환받은 모든 content 가 포함되어져 있는 stream 을 받는다.
            dataStream = response.GetResponseStream();

            // StreamReader 객체를 통해 stream 을 쉽게 접근하여 열 수 있다.
            StreamReader reader = new StreamReader(dataStream);

            // 모든 content 를 끝까지 읽는다.
            string responseFromServer = reader.ReadToEnd();

            // Stream 을 초기화한다.
            reader.Close();
            dataStream.Close();
            response.Close();

            return responseFromServer;
        }

    }
}

 

 

사용법은 간단합니다. post 로 보낼것인지 get 방식으로 보낼 것인지 지정하고, 키=값 형태로 데이터를 전송합니다.

 

MyWebRequest myRequest = new MyWebRequest("http://도메인/api/Value/Authenticate", "POST", "id=shims&pw=pwd");
var res = myRequest.GetResponse();

 


 

 

두번째 샘플예제

 

 

- 응답받을 때 euc-kr 일 경우를 대비함.

 

 

private string PostRequest(string url)
{
    HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
    httpWebRequest.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";
    httpWebRequest.Method = "POST";

    string postData = String.Format("id={0}&pw={1}", "luckshim", "XXXXXX");
    byte[] sendData = UTF8Encoding.UTF8.GetBytes(postData);
    httpWebRequest.ContentLength = sendData.Length;

    using (var stream = httpWebRequest.GetRequestStream())
    {
        stream.Write(sendData, 0, sendData.Length);
        stream.Close();
    }

    string result = "";

    try
    {
        using (var response = httpWebRequest.GetResponse() as HttpWebResponse)
        {
            if (httpWebRequest.HaveResponse && response != null)
            {
                Encoding encode; //응답받을 때 utf-8 또는 euc-kr 인지 확인 후 읽음.
                if (response.CharacterSet.ToLower() == "utf-8")
                {
                    encode = Encoding.GetEncoding("UTF-8");
                }
                else
                {
                    encode = Encoding.Default;
                }

                using (var reader = new StreamReader(response.GetResponseStream(), encode))
                {
                    result = reader.ReadToEnd();
                }
            }
        }
    }
    catch (WebException e)
    {
        if (e.Response != null)
        {
            using (var errorResponse = (HttpWebResponse)e.Response)
            {
                Encoding encode;
                if (errorResponse.CharacterSet.ToLower() == "utf-8")
                {
                    encode = Encoding.GetEncoding("UTF-8");
                }
                else
                {
                    encode = Encoding.Default;
                }

                using (var reader = new StreamReader(errorResponse.GetResponseStream(), encode))
                {
                    string error = reader.ReadToEnd();
                    result = error;
                }
            }
        }
    }

    return result;
}

var res1 = PostRequest("http://도메인/api/Value/Authenticate");

 


 

WebClient 클래스 활용하기

 

WebClient 클래스는 .NET Framework 2.0부터 사용이 가능합니다. 따라서 C# 언어의 버전과는 무관하게 .NET Framework 2.0 이상을 사용하는 환경에서는 WebClient 클래스를 사용할 수 있습니다.

 

using System.Net;
using System.Text;
using System.Collections.Specialized;

private string PostRequest(string url)
{
    using (WebClient client = new WebClient())
    {
        client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded; charset=UTF-8";

        var postData = new NameValueCollection()
        {
            { "id", "luckshim" },
            { "pw", "XXXXXX" }
        };

        string result = "";

        try
        {
            byte[] responseBytes = client.UploadValues(url, "POST", postData);
            string response = Encoding.UTF8.GetString(responseBytes); // 가정: 서버에서 utf-8 로 응답
            result = response;
        }
        catch (WebException e)
        {
            if (e.Response != null)
            {
                using (var errorResponse = (HttpWebResponse)e.Response)
                {
                    Encoding encode;
                    if (errorResponse.CharacterSet.ToLower() == "utf-8")
                    {
                        encode = Encoding.UTF8;
                    }
                    else
                    {
                        encode = Encoding.Default;
                    }

                    using (var reader = new StreamReader(errorResponse.GetResponseStream(), encode))
                    {
                        string error = reader.ReadToEnd();
                        result = error;
                    }
                }
            }
        }

        return result;
    }
}

var res1 = PostRequest("http://도메인/api/Value/Authenticate");

 

 

WebClient 클래스의 UploadValues 메소드를 사용하여 POST 요청을 수행하고 있습니다. 이 메소드는 POST 요청의 본문에 포함할 데이터를 NameValueCollection 형태로 전달받습니다. 이후에 응답을 바이트 배열로 받아 문자열로 변환하고 있습니다. 이러한 방식은 HttpWebRequest를 사용하는 것보다 코드가 간결하며, WebClient 클래스가 내부적으로 요청 및 응답의 생성과 처리를 모두 수행하기 때문에 사용하기가 더욱 편리합니다

 

WebClient의 UploadValues 메소드를 사용하여 POST 요청을 보내는 것을 보여줍니다. 그러나 원래 코드와 달리 WebClient는 응답의 캐릭터셋을 자동으로 확인하지 않으므로, 별도의 처리가 없을 경우 utf-8로 응답을 해석하게 됩니다. 이는 대부분의 웹 서비스에서 utf-8을 사용하므로 문제가 되지 않지만, 서버가 euc-kr 등 다른 인코딩을 사용하는 경우에는 문제가 될 수 있습니다. 따라서 정확한 인코딩을 확인하기 위해서는 HttpClient를 사용하는 것이 좋습니다. HttpClient를 사용하면 응답 헤더를 직접 확인하여 적절한 인코딩을 선택할 수 있습니다.


 

HttpClient 클래스

 

.NET Core 3.0 이후와 .NET 5 이후에서는 WebClient 클래스 대신 HttpClient 클래스 사용을 권장하고 있습니다. HttpClient 클래스는 WebClient 클래스보다 더 세밀한 네트워크 설정이 가능하고, 비동기 처리를 지원하기 때문에 현대의 애플리케이션 개발에 더 적합합니다.

 

using System.Net.Http;
using System.Threading.Tasks;
using System.Collections.Generic;
using System;
using System.Text;

private async Task<string> PostRequest(string url)
{
    using (HttpClient client = new HttpClient())
    {
        var postData = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("id", "luckshim"),
            new KeyValuePair<string, string>("pw", "XXXXXX")
        });

        var response = await client.PostAsync(url, postData);

        if (response.IsSuccessStatusCode)
        {
            var charset = response.Content.Headers.ContentType.CharSet;
            Encoding encoding;
            if (charset.ToLower() == "euc-kr")
            {
                encoding = Encoding.GetEncoding("euc-kr");
            }
            else
            {
                encoding = Encoding.UTF8;
            }

            var responseBytes = await response.Content.ReadAsByteArrayAsync();
            var result = encoding.GetString(responseBytes);

            return result;
        }
        else
        {
            // 에러 처리
            throw new Exception($"Server error (HTTP {response.StatusCode}: {response.ReasonPhrase}).");
        }
    }
}

var res1 = await PostRequest("http://도메인/api/Value/Authenticate");

 

HttpClient 클래스의 PostAsync 메소드를 사용하여 POST 요청을 수행하고 있습니다. 요청의 본문에 포함할 데이터는 FormUrlEncodedContent를 사용하여 전달하고 있습니다. 이후에 응답을 문자열로 변환하고 있습니다. 주의할 점은 HttpClient 클래스의 메소드들이 비동기로 동작하기 때문에, async와 await 키워드를 사용해야 합니다. 따라서 메소드 선언부에 async 키워드를 추가하고, Task<string>으로 반환 타입을 변경하였습니다. 또한, 메소드를 호출하는 부분에도 await 키워드를 추가하였습니다.