programing

ASP를 호출할 때 오류가 발생했습니다.잘못된 형식의 Json을 사용하는 NET 웹 메서드

goodsources 2023. 8. 19. 10:14
반응형

ASP를 호출할 때 오류가 발생했습니다.잘못된 형식의 Json을 사용하는 NET 웹 메서드

오래된 ASP가 있습니다. jQuery를 WebForms 프로그램 AJAX 파일 NET WebForms 파일$.ajax()측에서 , 라이언측의호, 된페지코호로 장식된 합니다.[WebMethod]특성.

WebMethod 내에서 처리되지 않은 예외가 발생할 경우, 다음을 실행하지 않습니다.Application_Error이벤트가 발생하여 오류 로거(ELMAH)에 의해 픽업되지 않습니다.이것은 잘 알려져 있고 문제가 아닙니다. 우리는 ELMAH에 수동으로 기록되는 예외를 제외하고 모든 WebMethod 코드를 try-catch 블록으로 감쌌습니다.

하지만, 저를 당황하게 만든 사례가 하나 있습니다.잘못된 형식의 Json이 WebMethod URL에 게시되면 코드를 입력하기 전에 예외를 던지는데, 이를 트랩할 방법을 찾을 수 없습니다.

예: 이 WebMethod 서명

[WebMethod]
public static string LeWebMethod(string stringParam, int intParam)

일반적으로 다음과 같은 Json 페이로드로 호출됩니다.

{"stringParam":"oh hai","intParam":37}

Fiddler를 사용하여 잘못된 형식의 Json에 페이로드를 편집하는 테스트를 시도했습니다.

{"stringParam":"oh hai","intPara

과 같은 그고다얻었니다습을음리▁the다를 받았습니다.ArgumentException의 오류 JavaScriptObjectDeserializer클라이언트로 전송됨(이것은 사용자 지정 오류 없이 로컬에서 실행되는 간단한 테스트 앱):

{"Message":"Unterminated string passed in. (32): {\"stringParam\":\"oh hai\",\"intPara","StackTrace":"   at
System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeString()\r\n   at
System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeMemberName()\r\n   at
System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeDictionary(Int32 depth)\r\n   at 
System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeInternal(Int32 depth)\r\n   at 
System.Web.Script.Serialization.JavaScriptObjectDeserializer.BasicDeserialize(String input, Int32 depthLimit, JavaScriptSerializer serializer)\r\n   at 
System.Web.Script.Serialization.JavaScriptSerializer.Deserialize(JavaScriptSerializer serializer, String input, Type type, Int32 depthLimit)\r\n   at 
System.Web.Script.Serialization.JavaScriptSerializer.Deserialize[T](String input)\r\n   at 
System.Web.Script.Services.RestHandler.GetRawParamsFromPostRequest(HttpContext context, JavaScriptSerializer serializer)\r\n   at 
System.Web.Script.Services.RestHandler.GetRawParams(WebServiceMethodData methodData, HttpContext context)\r\n   at 
System.Web.Script.Services.RestHandler.ExecuteWebServiceCall(HttpContext context, WebServiceMethodData methodData)","ExceptionType":"System.ArgumentException"}

그것은 여전히 발사되지 않습니다.Application_Error이벤트, 그리고 그것은 우리의 코드에 절대 들어가지 않기 때문에 우리 스스로 오류를 기록할 수 없습니다.

블로그 게시물 " 서비스를 위한 글로벌 예외 처리기를 만드는 방법"에 대한 포인터를 얻은 유사한 질문을 발견했지만, 이 질문은 SOAP 웹 서비스에만 유효하고 AJAX GET/POST에는 유효하지 않습니다.

제 상황에서 커스텀 핸들러를 연결할 수 있는 비슷한 방법이 있나요?

참조 소스에 따르면 내부 방법은 다음과 같은 예외를 모두 포착합니다.GetRawParams그리고 단순히 응답 스트림에 그것들을 씁니다, 그것이 이유입니다.Application_Error호출되지 않음:

internal static void ExecuteWebServiceCall(HttpContext context, WebServiceMethodData methodData) {
    try {
        ...
        IDictionary<string, object> rawParams = GetRawParams(methodData, context);
        InvokeMethod(context, methodData, rawParams);
    }
    catch (Exception ex) {
        WriteExceptionJsonString(context, ex);
    }
}

내가 생각할 수 있는 유일한 해결 방법은 출력을 가로채고 기록하는 출력 필터를 만드는 것입니다.

public class PageMethodExceptionLogger : Stream
{
    private readonly HttpResponse _response;
    private readonly Stream _baseStream;
    private readonly MemoryStream _capturedStream = new MemoryStream();

    public PageMethodExceptionLogger(HttpResponse response)
    {
        _response = response;
        _baseStream = response.Filter;
    }

    public override void Close()
    {
        if (_response.StatusCode == 500 && _response.Headers["jsonerror"] == "true")
        {
            _capturedStream.Position = 0;
            string responseJson = new StreamReader(_capturedStream).ReadToEnd();
            // TODO: Do the actual logging.
        }

        _baseStream.Close();
        base.Close();
    }

    public override void Flush()
    {
        _baseStream.Flush();
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        return _baseStream.Seek(offset, origin);
    }

    public override void SetLength(long value)
    {
        _baseStream.SetLength(value);
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        return _baseStream.Read(buffer, offset, count);
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        _baseStream.Write(buffer, offset, count);
        _capturedStream.Write(buffer, offset, count);
    }

    public override bool CanRead { get { return _baseStream.CanRead; } }
    public override bool CanSeek { get { return _baseStream.CanSeek; } }
    public override bool CanWrite { get { return _baseStream.CanWrite; } }
    public override long Length { get { return _baseStream.Length; } }

    public override long Position
    {
        get { return _baseStream.Position; }
        set { _baseStream.Position = value; }
    }
}

.asax.cs .asax.cs ("HTTP")에 필터를 합니다.Application_PostMapRequestHandler:

protected void Application_PostMapRequestHandler(object sender, EventArgs e)
{
    HttpContext context = HttpContext.Current;
    if (context.Handler is Page && !string.IsNullOrEmpty(context.Request.PathInfo))
    {
        string contentType = context.Request.ContentType.Split(';')[0];
        if (contentType.Equals("application/json", StringComparison.OrdinalIgnoreCase))
        {
            context.Response.Filter = new PageMethodExceptionLogger(context.Response);
        }
    }
}

문서에서는 SoapExtension이 더 쉬운 웹 메서드를 확장하는 두 가지 방법이 있음을 제안합니다.다른 하나는 SoapExtension을 작성하는 방법의 예를 보여줍니다.당신이 메시지 확인을 할 수 있는 곳처럼 보입니다.

페이지에 다음과 같은 정적 메서드가 표시되어 있다고 말할 때WebMethod그리고 당신은 당신이 사용한다고 말합니다.$.ajax그건 그냥 잘못된 것처럼 들립니다.하지만 저는 당신의 시스템의 세부 사항을 모르기 때문에 의심의 여지가 없습니다.

어쨌든 테스트해 보십시오.

  • 페이지에 다음과 같은 스크립트 관리자가 있어야 합니다. (**1)

  • 있는 에서.$.ajax과 같이 2), 페이지 : (**2)

(**1)

<asp:ScriptManager ID="smPageManager"
        runat="server"
        EnablePageMethods="true" 
        ScriptMode="Release" 
        LoadScriptsBeforeUI="true"> 
</asp:ScriptManager>

(**2)

PageMethods.LeWebMethod("hero", 1024, function(response){
    alert(response);
}, function(error){
    alert(error);
});

ASP를 사용하여 알 수 있습니다.NET Ajax Library는 올바른 방법으로 테스트하고 오류가 올바르게 보고되는지 확인합니다.

추신: 북마크 스타일 표기법에 대해 죄송합니다만, SO는 지금 약간의 오작동을 겪고 있는 것 같습니다.

갱신하다

게시물을 읽고 귀하가 직면한 문제를 설명하는 것 같습니다.

(...) System을 구현하는 클래스에 대한 요청인 경우.웹.UI.페이지에서 요청한 메서드를 호출하는 데는 이전 게시물에서 설명한 WebServiceData 클래스가 사용됩니다.메서드가 호출되면 CompleteRequest 메서드가 호출되어 모든 파이프라인 이벤트를 바이패스하고 EndRequest 메서드를 실행합니다. 이를 통해 MS AJAX는 메소드를 호출하기 위해 서비스를 만드는 대신 페이지에서 메소드를 호출할 수 있습니다. (...)

ASP를 사용해 보십시오.NET JavaScript Proxies - Microsoft 생성 코드를 사용하여 오류를 캡처할 수 있는지 확인합니다.

다음은 내부 RestHandler 구현을 내 버전으로 대체하는 솔루션입니다.WriteExceptionJsonString 메서드에서 예외를 기록할 수 있습니다.C# 메소드의 내용을 동적으로 바꾸시겠습니까?에 제공된 답변을 사용합니다.방법을 바꿀 수 있습니다.Global.asax Application_Start 메서드에서 ReplaceRestHandler에 호출을 추가하면 작동한다는 것을 확인했습니다.이렇게 오래 운영하거나 운영 중인 적이 없으므로 자신의 책임 하에 사용하십시오.

using System;
using System.Collections.Specialized;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Web;
using Newtonsoft.Json;

namespace Royal.Common.WebStuff
{
    public static class RestHandlerUtils
    {
        internal static void WriteExceptionJsonString(HttpContext context, Exception ex, int statusCode)
        {
            string charset = context.Response.Charset;
            context.Response.ClearHeaders();
            context.Response.ClearContent();
            context.Response.Clear();
            context.Response.StatusCode = statusCode;
            context.Response.StatusDescription = HttpWorkerRequest.GetStatusDescription(statusCode);
            context.Response.ContentType = "application/json";
            context.Response.AddHeader("jsonerror", "true");
            context.Response.Charset = charset;
            context.Response.TrySkipIisCustomErrors = true;
            using (StreamWriter streamWriter = new StreamWriter(context.Response.OutputStream, new UTF8Encoding(false)))
            {
                if (ex is TargetInvocationException)
                    ex = ex.InnerException;
                var error = new OrderedDictionary();
                error["Message"] = ex.Message;
                error["StackTrace"] = ex.StackTrace;
                error["ExceptionType"] = ex.GetType().FullName;
                streamWriter.Write(JsonConvert.SerializeObject(error));
                streamWriter.Flush();
            }
        }

        public static void ReplaceRestHandler()
        {
            //https://stackoverflow.com/questions/7299097/dynamically-replace-the-contents-of-a-c-sharp-method
            var methodToInject = typeof(RestHandlerUtils).GetMethod("WriteExceptionJsonString",
                BindingFlags.NonPublic | BindingFlags.Static);
            var asm = typeof(System.Web.Script.Services.ScriptMethodAttribute).Assembly;
            var rhtype = asm.GetType("System.Web.Script.Services.RestHandler");
            var methodToReplace = rhtype
                .GetMethod("WriteExceptionJsonString", BindingFlags.NonPublic | BindingFlags.Static, null,
                    new Type[] {typeof(HttpContext), typeof(Exception), typeof(int)}, null);

            RuntimeHelpers.PrepareMethod(methodToReplace.MethodHandle);
            RuntimeHelpers.PrepareMethod(methodToInject.MethodHandle);

            unsafe
            {
                if (IntPtr.Size == 4)
                {
                    int* inj = (int*) methodToInject.MethodHandle.Value.ToPointer() + 2;
                    int* tar = (int*) methodToReplace.MethodHandle.Value.ToPointer() + 2;
                    *tar = *inj;
                }
                else
                {
                    long* inj = (long*) methodToInject.MethodHandle.Value.ToPointer() + 1;
                    long* tar = (long*) methodToReplace.MethodHandle.Value.ToPointer() + 1;
                    *tar = *inj;
                }
            }
        }
    }
}

@Michael Liu의 답변은 훌륭하지만 클래식 모드(통합 모드에서 작동)에서는 중단됩니다.은 그이는 때문입니다._response.Headers["jsonerror"]클래식 모드에서는 지원되지 않습니다.저는 그 체크를 끈 채로 두었고 어쨌든 모든 상태 501이 오류일 것이기 때문에 여전히 저에게 잘 작동하는 것 같습니다.추가 점검이 필요한 시나리오를 생각할 수 없습니다.

이러한 링크는 클라이언트 측의 오류를 처리하는 데 도움이 될 수 있습니다.

스택 오버플로

보이지 않는 혁명

asp.net

엔코시아

그런 다음 클라이언트 측에서 제어 이벤트를 트리거하여 오류를 서버에 전달하고 로깅을 수행할 수 있습니다.

언급URL : https://stackoverflow.com/questions/24054573/catching-errors-from-calling-asp-net-webmethod-with-malformed-json

반응형