CustomExceptionFilter和GlobalExceptionHandler有什么区别?

Raysefo

知名会员
已加入
2019年2月22日
留言内容
192
编程经验
10+
你好

在我的asp.net Web API中,我同时使用GlobalExceptionHandler和CustomExceptionFilter。我认为他们做了同样的工作,向客户端隐藏了实际的错误并发送给他们"HTTP 500-内部服务器错误。请与您的管理员联系。"响应。我想知道是否删除GlobalExcepitonHandler并将Nlog添加到CustomExceptionFilter中,仍然可以发送相同的响应并记录错误吗?您对此有何看法?

UnhandledExceptionLogger无法捕获所有错误,这就是为什么我想在其中之一中添加日志记录的原因。

CustomExceptionFilter:
C#:
public class CustomExceptionFilter : ExceptionFilterAttribute
    {

        public override void OnException(HttpActionExecutedContext actionExecutedContext)
        {
            base.OnException(actionExecutedContext);


            var response = new HttpResponseMessage(HttpStatusCode.InternalServerError)
            {
                //Content = new StringContent("An unhandled exception was thrown by service."),
                ReasonPhrase = "HTTP 500-内部服务器错误。请与您的管理员联系。"
            };

            actionExecutedContext.Response = response;
        }
    }

GlobalExceptionHandler:
C#:
public class GlobalExceptionHandler : ExceptionHandler
    {
        
        public override void Handle(ExceptionHandlerContext context)
        {
            var result = new HttpResponseMessage(HttpStatusCode.InternalServerError)
            {
                Content = new StringContent("HTTP 500-内部服务器错误。请与您的管理员联系。"),
                ReasonPhrase = "Exception"
            };

            
            context.Result = new ErrorMessageResult(context.Request, result);
        }

        public class ErrorMessageResult : IHttpActionResult
        {
            private HttpRequestMessage _request;
            private readonly HttpResponseMessage _httpResponseMessage;

            public ErrorMessageResult(HttpRequestMessage request, HttpResponseMessage httpResponseMessage)
            {
                _request = request;
                _httpResponseMessage = httpResponseMessage;
            }

            public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
            {
                return Task.FromResult(_httpResponseMessage);
            }
        }

      }
UnhandledExceptionFilter:
C#:
public class UnhandledExceptionLogger : ExceptionLogger
    {
        private static readonly Logger logger = LogManager.GetCurrentClassLogger();

        public override void Log(ExceptionLoggerContext context)
        {
            var timestamp = DateTime.UtcNow;

            //NLOG
            NLog(logger, context.Exception, timestamp);
        }

        private void NLog(Logger logger, Exception message, DateTime timestamp)
        {
            var sb = new StringBuilder();
            sb.AppendLine(message.ToString());
            sb.AppendLine(timestamp.ToLongDateString());
            logger.Error(sb.ToString());
        }
    }
 

Raysefo

知名会员
已加入
2019年2月22日
留言内容
192
编程经验
10+
我按如下方式更改了主叫方:

C#:
HttpResponseMessage response;

//Call Game
using (var cts = new CancellationTokenSource(new TimeSpan(0, 5, 0)))
{
      response = await _httpClient.PostAsync("//test.com/" + url, content,cts.Token);
}

我遇到了同样的错误"System.Threading.Tasks.TaskCanceledException:任务被取消。"仍然没有来自第三方管理员的回复。
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,919
地点
英国
编程经验
10+
一件事 不做。不要将httpClient包装在using块中。 httpClient实际上是一个共享对象,您应确保在一个 非IDisposable块,并保持静态。
you can check CancellationToken.IsCancellationRequested属性(System.Threading) 如果返回的是false,则可以肯定在某个地方超时了。
第三次聚会没有任何等待。如果IsCancellationRequested返回false,则说明您的httpclient超时。由于您将其包装在using语句中,将对其进行处理,这可能会使TIME_WAIT挂起一些调用,这很可能是IsCancellationRequested返回true的原因。进行更改,看看接下来会遇到什么错误...
 

约翰·H

C#论坛主持人
工作人员
已加入
2011年4月23日
留言内容
1,022
地点
挪威
编程经验
10+
不要将httpClient包装在using块中。 httpClient实际上是一个共享对象,您应确保在一个 非IDisposable块,并保持静态。
不是HttpClient是该代码中使用和配置的资源,而是CancellationTokenSource。 HttpClient在UtilitiesTest类中是静态的,它应该在第19章中出现。
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,490
地点
弗吉尼亚州切萨皮克
编程经验
10+
我按如下方式更改了主叫方:

C#:
HttpResponseMessage response;

//Call Game
using (var cts = new CancellationTokenSource(new TimeSpan(0, 5, 0)))
{
      response = await _httpClient.PostAsync("//test.com/" + url, content,cts.Token);
}

我遇到了同样的错误"System.Threading.Tasks.TaskCanceledException:任务被取消。"仍然没有来自第三方管理员的回复。

好吧根据 文件资料,如果经过取消令牌的时间过去了,则令牌将被取消。看起来像 //test.com 在5分钟内未回复您的帖子。

对我来说,这很有意义。如果您正在执行突发测试,您确定所调用的第三方API也可以支持该级别的突发流量吗?
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,490
地点
弗吉尼亚州切萨皮克
编程经验
10+
如果您阅读了该第三方的服务条款,他们特别指出您不应该针对他们的API进行负载测试,我不会感到惊讶。
 

Raysefo

知名会员
已加入
2019年2月22日
留言内容
192
编程经验
10+
哪有这回事 :) 我问了两次,他们只是说,这是一个测试服务器,它的容量可能不如生产服务器好 :)
顺便说一句,我想知道是否有出站连接限制?我只想确保这不是由于托管我的API的服务器造成的。我读了这篇文章 出站连接限制 我应该使用这样的东西吗?

C#:
// ServicePointManager setup
ServicePointManager.UseNagleAlgorithm = false;
ServicePointManager.Expect100Continue = false;
ServicePointManager.DefaultConnectionLimit = int.MaxValue;
ServicePointManager.EnableDnsRoundRobin = true;
ServicePointManager.ReusePort = true;
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,919
地点
英国
编程经验
10+
它是CancellationTokenSource。 HttpClient在UtilitiesTest类中是静态的,它应该在第19章中出现。
它的确是。我不好,不回头。

是的,在与调试器进行检查之后,我注意到的是,如果服务器超时,您将得到 System.OperationCanceledException OperationCanceledException类(系统) -这不是此类错误的正确例外,我认为一个Microsoft需要修复。我只是在这里进行总结,但是httpclient的异常处理程序中可能会有一个异常覆盖方法。因为stacktrace显示了另一种方法 (HandleFinishSendAsyncError) 发生超时时被调用以取消当前任务:
C#:
   at System.Net.Http.HttpClient.HandleFinishSendAsyncError(Exception e, CancellationTokenSource cts)
   at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
我还没有在MSDN References上研究该方法,但是我确实做了一个快速的Google搜索,发现此链接可能对您感兴趣,以解决可能的超时问题: HttpClient:OperationCanceledException隐藏先前的异常·问题#33957·dotnet / corefx

屏幕截图_8.jpg


但是,如果您的异步方法内部发生任何错误; IsCancellationRequested也将返回true,从而将令牌的取消设置为true并中止任务。对您而言,坏消息是,这使您很难锻炼导致任务终止的原因。查看上面的github链接。我所知道的是,如果有超时,任务可以并且将终止。如果记忆为我服务,我们应该得到一个完全不同的错误吗?这是我如何测试它:
屏幕截图_6.jpg

我选择了一个封闭的端口,并使用了一个我会响应的服务。使用google.com:81将导致超时。因为端口81未打开。下面的屏幕截图显示了无论实际上发生了什么错误,httpclient都会确保IsCancellationRequested设置为true,并且正如我之前指出的那样,这导致该方法进入正在运行的任务的取消过程,这不是我期望为之找到的东西客户超时...
屏幕截图_2.jpg

似乎任何异常都可能导致IsCancellationRequested设置为true。我相信这是一个可能的错误,应该予以修复。我相信这是在httpclient的幕后完成的,但是在那里配置了错误处理程序……这是我将需要更多时间进行研究的东西。
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,919
地点
英国
编程经验
10+
只是为了在上面的stacktrace上增加一些清晰度。在MSDN Docs中没有引用正在调用的方法,但是在.Net中引用了该方法。 Git仓库. The link shows, if an error occurs in an async method for the httpclient, it will bounce and throw this error : OperationCanceledException : as seen in this undocumented event. If its not evident already, this means that the wrong error message is being thrown for errors relating to connectivity issues reported by the httpclient. When I read this back; to me, its kinda like trowing an exception for an exception that isn't part of the first exception. This is a method that lacks superior information about other possible exceptions, and only because HandleFinishSendAsyncError is only checking bool for IsCancellationRequested regardless of the type of elaborate exception being passed in.
C#:
            if (NetEventSource.IsEnabled) NetEventSource.Error(this, e);

            // If the cancellation token was canceled, we consider the exception to be caused by the
            // cancellation (e.g. WebException when reading from canceled response stream).
            if (cts.IsCancellationRequested && e is HttpRequestException)
            {
                if (NetEventSource.IsEnabled) NetEventSource.Error(this, "Canceled");
                throw new OperationCanceledException(cts.Token);
            }
从微软的角度来说,这简直是可怜的。如果你不清楚 @raysefo,您遇到的这个问题永远不会向您报告实际的异常,除非他们修补错误的处理方式,并将其扩展以适应其他可能的情况。目前,您真的无能为力。早些时候,当我认为您在客户端上使用了块时,这就是为什么这会成为问题。您的连接将轮询并停留在TIME_WAIT中,如下所示:
屏幕截图_9.jpg

但是,如果不将它们包装在IDispossible代码中,您将看不到。如下所示:
屏幕截图_10.jpg

我的观点是确保使用基本的控制台应用程序,您实际上可以通过使用类似这样的方式来打入gamesultan api(并且不要在rest应用程序中运行此程序,请使用控制台进行测试):
C#:
private static readonly HttpClient httpc = new HttpClient();
C#:
private static async Task RunWithoutUsingHttpClient()
        {
            Console.WriteLine("Polling Begins");
            for (int x = 0; x < 50000; x++)
            {
                HttpResponseMessage message = await httpc.GetAsync("http://google.com");
                 Console.WriteLine(message.StatusCode);
            }
            Console.WriteLine("Connections Established");
            Console.ReadLine();
        }
并避免写作 像这样的代码:
C#:
        private static async Task RunWithUsingHttpClient()
        {
            Console.WriteLine("Polling Begins");
            for (int x = 0; x < 50000; x++)
            {
                using (HttpClient httpc = new HttpClient())
                {
                    HttpResponseMessage message = await httpc.GetAsync("http://google.com");
                    Console.WriteLine(message.StatusCode);
                }
            }
            Console.WriteLine("Connections Established");
        }
切记将地址更改为您要命中的服务器的URL。您可以通过在Windows命令中运行netstat来管理连接交互。如果可以毫无问题地连接,则说明问题出在您自己的服务器设置或restapi的代码中。我明天再回来看看情况如何。顺便说一句,您需要与您的第三方进行实时通信,以使他们检查您是否在攻击他们的服务器,特别是如果其他人据称正在使用此秘密的非公开API。
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,490
地点
弗吉尼亚州切萨皮克
编程经验
10+
Until the bug is fixed, it's time to fallback on the good old HttpWebRequest and HttpWebResponse classes. Under the covers, HttpClient still uses these classes. It just so happens that HttpClient has put together an interface that is so much easier to use and understand.
 

Raysefo

知名会员
已加入
2019年2月22日
留言内容
192
编程经验
10+
我在服务器上安装了AppOptics APM工具。 (试用)我可以从此工具获得任何帮助吗?

@跳伞 我应该对HttpWebResponse使用这样的东西吗:
C#:
var request = (HttpWebRequest)WebRequest.Create("http://url");
request.ContentType = "application/json";
request.Method = "POST";

using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
    string json = new JavaScriptSerializer().Serialize(new
                {
                    user = "Foo",
                    password = "Baz"
                });

    streamWriter.Write(json);
}

var response = (HttpWebResponse)request.GetResponse();
using (var streamReader = new StreamReader(response.GetResponseStream()))
{
        var result = streamReader.ReadToEnd();
}
 
Last edited:

Raysefo

知名会员
已加入
2019年2月22日
留言内容
192
编程经验
10+
你好 @谢平 ,

我更改了一些代码,以便仅打10次迭代即可正确访问API。我在云端的服务器上运行了它。

2019-08-25 15_49_47-C__Users_197199_Documents_Visual Studio 2017_Projects_TestGameProduct_Test ... png


这是我的代码示例:
C#:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;

namespace TestGameProduct
{
    internal class Program
    {
        private static readonly HttpClient httpc = new HttpClient();

        static async Task Main(string[] args)
        {
            FormUrlEncodedContent content = null;

            var productsDto = new ProductRequestDto();

            var applicationCode = "fadc3727ed7";
            var secretKey = "72e61";
            var version = "V1";
            string source = applicationCode + version + secretKey;

            using (var md5Hash = MD5.Create())
            {
                var hash = GetMd5Hash(md5Hash, source);
                productsDto.signature = hash;
                productsDto.ApplicationCode = applicationCode;
                productsDto.version = version;
            }

            //Convert request
            var keyValues = productsDto.ToKeyValues();
            content = new FormUrlEncodedContent(keyValues);

            await RunWithoutUsingHttpClient(content);
        }

     
        private static async Task RunWithoutUsingHttpClient(FormUrlEncodedContent content)
        {
            httpc.DefaultRequestHeaders.Authorization =
                new AuthenticationHeaderValue("Basic", "j9qJjc=");
            Console.WriteLine("Polling Begins");
            for (int x = 0; x < 10; x++)
            {
                HttpResponseMessage message = await httpc.PostAsync("//teststore.gamesultan.com/Product/", content);
             
                Console.WriteLine(message.StatusCode);
            }
            Console.WriteLine("Connections Established");
            Console.ReadLine();
        }

     
        public static string GetMd5Hash(HashAlgorithm md5Hash, string input)
        {
            // Convert the input string to a byte array and compute the hash.
            byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));

            // Create a new Stringbuilder to collect the bytes
            // and create a string.
            StringBuilder sBuilder = new StringBuilder();

            // Loop through each byte of the hashed data
            // and format each one as a hexadecimal string.
            for (int i = 0; i < data.Length; i++)
            {
                sBuilder.Append(data[i].ToString("x2"));
            }

            // Return the hexadecimal string.
            return sBuilder.ToString();
        }


    }

    public static class ObjectExtension
    {
        public static IDictionary<string, string> ToKeyValues(this object metaToken)
        {
            if (metaToken == null)
            {
                return null;
            }

            JToken token = metaToken as JToken;
            if (token == null)
            {
                return ToKeyValues(JObject.FromObject(metaToken));
            }

            if (token.HasValues)
            {
                var contentData = new Dictionary<string, string>();

                foreach (var child in token.Children().ToList())
                {
                    var childContent = child.ToKeyValues();
                    if (childContent != null)
                    {
                        contentData = contentData.Concat(childContent).ToDictionary(k => k.Key, v => v.Value);
                    }
                }

                return contentData;
            }

            var jValue = token as JValue;
            if (jValue?.Value == null)
            {
                return null;
            }

            var value = jValue?.Type == JTokenType.Date
                ? jValue?.ToString("o", CultureInfo.InvariantCulture)
                : jValue?.ToString(CultureInfo.InvariantCulture);

            return new Dictionary<string, string> { { token.Path, value } };
        }
    }
    public class ProductRequestDto
    {
        public string version { get; set; }
        public string signature { get; set; }
        public string ApplicationCode { get; set; }
    }


}
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,919
地点
英国
编程经验
10+
Last edited:

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,919
地点
英国
编程经验
10+
最多运行50次,然后等待几分钟再进行一次。然后尝试100次,然后300次,然后500次,然后800次,然后2000次,然后5000次,在每次执行前等待几分钟。正如我之前所建议的那样,这将证明您是否打算针对他们的API进行负载测试,很可能他们可能会阻止您的请求。
 

Raysefo

知名会员
已加入
2019年2月22日
留言内容
192
编程经验
10+
测试控制台应用程序可以正常工作,但是在我的其余API中,我没有调用此地址: http://qa-api.mol.com/pinstore/。 (我现在很失望)我将在服务器上安装Jmeter并在IIS中测试我的API。 (我在笔记本电脑上进行了测试)也许服务器上安装的APM工具可以告诉我一些信息。

我应该使用这样的东西吗?
ConfigureAwait(false)
 
Last edited:

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,919
地点
英国
编程经验
10+
现在有点常识。

为什么将数据发送到重定向流量的服务器地址。使用新的URI。

跳伞者也建议使用旧课程...
 

Raysefo

知名会员
已加入
2019年2月22日
留言内容
192
编程经验
10+
跳伞者也建议使用旧课程...

应该异步吗?
C#:
HttpWebRequest request = new HttpWebRequest(http://url);
request.Method = "POST";

string postString = String.Format("username={0}&password={1}&grant_type=password", HttpUtility.HtmlEncode(username), HttpUtility.HtmlEncode(password));
byte[] bytes = Encoding.UTF8.GetBytes(postString);
using (Stream requestStream = await request.GetRequestStreamAsync())
{
     requestStream.Write(bytes, 0, bytes.Length);
}

try
{
     HttpWebResponse httpResponse =  (HttpWebResponse)(await request.GetResponseAsync());
     string json;
     using (Stream responseStream = httpResponse.GetResponseStream())
     {
           json = new StreamReader(responseStream).ReadToEnd();
     }
     Model response = JsonConvert.DeserializeObject<Model>(json);
     return response;
}
catch (Exception ex)
{
    //exception
}
 
最佳 底部