ASP.NET WebService로 .asmx 서비스를 하나 만들면 기본적으로 SOAP 호출을 받는 서비스가 된다.


그런데 여기에 AjaxControlToolkit을 설치하거나 Ajax를 통해 JSON 호출도 받는 서비스를 만들려면

[ScriptService] 라는 속성 선언을 더해주고 web.config에도 아래 부분을 추가해야 한다.


<system.web.extensions>

<scripting>

<webServices>

<jsonSerialization maxJsonLength="5000000" />

</webServices>

</scripting>

</system.web.extensions>


숫자는 적당히.


>> 참조: http://msdn.microsoft.com/ko-kr/library/bb398998(v=vs.90).aspx



그러면 AjaxControlToolkit뿐만 아니라 jQuery의 $.ajax() 명령을 이용해서 JSON 개체를 사용할 수 있다.


>> 참조: http://www.codeproject.com/Articles/521723/Web-Service-and-Script-Service




그런데, 문제는 이런 것들을 쓰려면 뭔가를 설치하거나 참조 또는 스크립트 링크를 넣어야 한다는 점이다.

제 아무리 minifier 등을 써서 줄여도 최소 수십 KB 이상이 추가되기 때문에 웹 페이지가 갑작스레 무거워진다는 점이 영 별로다. 정작 실제로 사용하는 건 겨우 함수 한두 개, 길어야 코드 수십 줄 뿐인데 말이다.


그래서 이것저것 다 빼고 Javascript로 직접 XmlHttpRequest 코딩을 하려고 할 때가 있다, 오직 가벼움을 위해.

JSON도 아니고 SOAP도 아닌 그냥 HttpPost 방식으로.



예를 들면 이런 거.


[Service.asmx.cs]

[ScriptService]

public class Service : WebService

{

[WebMethod]

public string GetString(bool isOk)

{

return isOK ? "OK" : "NO";

}

}



이런 간단한 웹 서비스를 하나 만들고 Javascript에서 호출하려고 하면,


[test.js]

function getXmlHttpRequest() {

    if (!window.XMLHttpRequest) {

        window.XMLHttpRequest = function window$XMLHttpRequest() {

            var progIDs = ['Msxml2.XMLHTTP.3.0', 'Msxml2.XMLHTTP'];

            for (var i = 0, l = progIDs.length; i < l; i++) {

                try {

                    return new ActiveXObject(progIDs[i]);

                } catch (ex) {

                }

            }

            return null;

        };

    }

    return new XMLHttpRequest();

}


function send() {
...
    var xhr = getXmlHttpRequest();

    xhr.open("POST", "/Service.asmx/GetString", false);

    xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");

    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');

    xhr.send("isOK=true");

    if (xhr.readyState == 4 && xhr.status == 200) {

        console.log("서버 호출 결과: " + xhr.responseText);

        var doc = xhr.responseXML;

        var xmldoc;

        if (doc && doc.documentElement) // Chrome, IE 최신 버전 등...

            xmldoc = doc;

        else { // IE 낮은 버전

            doc = xhr.responseText;

            if (window.DOMParser) {

                var parser = new DOMParser();

                xmldoc = parser.parseFromString(doc, "text/xml");

            } else if (window.ActiveXObject || "ActiveXObject" in window) { // Internet Explorer

                xmldoc = new ActiveXObject("Microsoft.XMLDOM");

                xmldoc.async = false;

                xmldoc.loadXML(doc);

            }

        }


        if (xmldoc != null && xmldoc.documentElement != null && xmldoc.documentElement.firstChild != null) {

            var val = xmldoc.documentElement.firstChild.nodeValue;

        }

...

}


뭐 이런 식의 코드가 만들어져야 한다. (녹색 부분은 크로스 브라우저 호환 코드라 괜히 길어졌다.)


호출은 Form POST 방식(x-www-form-urlencoded)으로 "이름=값&이름=값" 이런 식으로 단순하게 들어가지만, 결과는 안타깝게도 XML로 리턴되므로 위와 같이 XML 파싱 부분이 들어가야 하는데, 코드 길이나 참조 링크 등 여러 측면에서 JSON 파싱보다는 낫다고 생각한다. (무한 단순 반복 연산이나 과부하 상태일 때는 JSON이 더 나은 성능을 보인다는 얘기도 있는데, 그런 건 난 잘 모르겠다. 난 XML 파싱이 더 좋다. 보기에도 좋고 쓰기에도 좋고.)




그런데, 문제가 있다!



코드를 잘 완성해서 로컬 PC나 서버 로컬에서 테스트해보면 매우 정상 작동하다가도

클라이언트-서버 분리된 환경으로 테스트를 해 보면 에러가 발생하고 제대로 호출이 되지 않는 상황이 생긴다.


그 때 서버 이벤트를 보면,


Event code: 3005 

Event message: 처리되지 않은 예외가 발생했습니다. 

Event time: 2014-0x-xx 오후 x:xx:xx 

Event time (UTC): 2014-0x-xx 오전 x:xx:xx 

Event ID: 08ee8dbd5a334c16876c30272c2549c5 

Event sequence: 2506 

Event occurrence: 196 

Event detail code: 0 

 

Application information: 

    Application domain: /LM/W3SVC/1/ROOT-6-130416651036127238 

    Trust level: Full 

    Application Virtual Path: / 

    Application Path: D:\TestWeb\ 

    Machine name: TESTCOMPUTER

 

Process information: 

    Process ID: 5792 

    Process name: w3wp.exe 

    Account name: IIS APPPOOL\TestWeb 

 

Exception information: 

    Exception type: InvalidOperationException 

    Exception message: URL이 예기치 않게 '/GetString'(으)로 끝나 요청 형식을 인식할 수 없습니다.

   위치: System.Web.Services.Protocols.WebServiceHandlerFactory.CoreGetHandler(Type type, HttpContext context, HttpRequest request, HttpResponse response)

   위치: System.Web.Services.Protocols.WebServiceHandlerFactory.GetHandler(HttpContext context, String verb, String url, String filePath)

   위치: System.Web.Script.Services.ScriptHandlerFactory.GetHandler(HttpContext context, String requestType, String url, String pathTranslated)

   위치: System.Web.HttpApplication.MaterializeHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()

   위치: System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)



이런 오류를 기록하고 있다.


요청 형식을 인식할 수 없다고?

(영어로는 "Request format is unrecognized for URL unexpectedly ending in '/GetString'" 쯤 되겠다.)


이게 왜 이러는 걸까? 엄연히 존재하는 URL이 맞고 정상적으로 요청한 것도 맞는데?

개발 컴퓨터 로컬컴퓨터에서 하면 잘만 되는데?


이건 뭔가 다른 문제가 있다는 거다. 예를 들면 호출 방식이나 서버의 설정 같은 문제가...


좀 더 확인해 보니,

JSON이나 SOAP으로는 아주 잘 되는데, Form POST 방식(x-www-form-urlencoded)일 때만 안되는 거였다.


즉, HttpPost 방식이 안되는 거였다.

왜 HttpPost 방식만 안되는 걸까?




그 답은,


기본적으로 ASP.NET에서 ScriptService는 HttpGet과 HttpPost가 disabled 되어 있어서 그런 것이란다.

이걸 허용해 주면 된다.


<system.web>

<webServices>

<protocols>

<add name="HttpPost" />

</protocols>

</webServices>

</system.web>



그러면 다 잘 된다.





Posted by 떼르미
,


자바스크립트를 허용해주세요!
Please Enable JavaScript![ Enable JavaScript ]