.Net Task<T>的一种比较神奇的卡死情况(Wait/Result卡死, await能得到结果)

出现的环境.Net4.0 + WebApi1(4.0.30506.0) + Microsoft.Bcl.Async.1.0.168

自己死活看不出原因, 分享出来给大家看看,希望有人能找到问题的关键

 

出现错误的是下面这两个模块

下面的CorsMessageHandler,抄的http://www.cnblogs.com/artech/p/cors-4-asp-net-web-api-04.html, 做了部分修改

  1     public class CorsMessageHandler : DelegatingHandler
  2     {
  3         private static readonly CorsAttribute DEFAULT_CORS = new CorsAttribute("*");//默认支持所有
  4 
  5         protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  6         {
  7             cancellationToken.ThrowIfCancellationRequested();
  8             try
  9             {
 10                 //得到描述目标Action的HttpActionDescriptor
 11                 HttpMethod originalMethod = request.Method;
 12                 bool isPreflightRequest = request.IsPreflightRequest();
 13                 if (isPreflightRequest)
 14                 {
 15                     string method = request.Headers.GetValues("Access-Control-Request-Method").First();
 16                     request.Method = new HttpMethod(method);
 17                 }
 18                 HttpConfiguration configuration = request.GetConfiguration();
 19                 HttpControllerDescriptor controllerDescriptor = configuration.Services.GetHttpControllerSelector().SelectController(request);
 20                 HttpControllerContext controllerContext = new HttpControllerContext(request.GetConfiguration(), request.GetRouteData(), request)
 21                 {
 22                     ControllerDescriptor = controllerDescriptor
 23                 };
 24                 //避免权限错误
 25                 //HttpActionDescriptor actionDescriptor = configuration.Services.GetActionSelector().SelectAction(controllerContext);
 26 
 27                 //根据HttpActionDescriptor得到应用的CorsAttribute特性
 28                 CorsAttribute corsAttribute = null;
 29                 //corsAttribute = actionDescriptor.GetCustomAttributes<CorsAttribute>().FirstOrDefault();
 30                 corsAttribute = corsAttribute?? controllerDescriptor.GetCustomAttributes<CorsAttribute>().FirstOrDefault();
 31                 if (null == corsAttribute)
 32                 {
 33                     corsAttribute = DEFAULT_CORS;
 34                     //return base.SendAsync(request, cancellationToken);
 35                 }
 36 
 37                 //利用CorsAttribute实施授权并生成响应报头
 38                 IDictionary<string, string> headers;
 39                 request.Method = originalMethod;
 40                 bool authorized = corsAttribute.TryEvaluate(request, out headers);
 41                 HttpResponseMessage response;
 42                 if (isPreflightRequest)
 43                 {
 44                     if (authorized)
 45                     {
 46                         response = new HttpResponseMessage(HttpStatusCode.OK);
 47                     }
 48                     else
 49                     {
 50                         response = request.CreateErrorResponse(HttpStatusCode.BadRequest, corsAttribute.ErrorMessage);
 51                     }
 52                 }
 53                 else
 54                 {
 55                     var tmp = base.SendAsync(request, cancellationToken);
 56                     tmp.Wait();
 57                     response = tmp.Result;
 58                 }
 59 
 60                 if (headers != null)
 61                 {
 62                     foreach (var item in headers)
 63                     {
 64                         response.Headers.Add(item.Key, item.Value);
 65                     }
 66                 }
 67                 return response;
 68             }
 69             catch
 70             {
 71             }
 72             //catch -> fallback
 73             return await base.SendAsync(request, cancellationToken);
 74         }
 75     }
 76     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
 77     public class CorsAttribute : Attribute
 78     {
 79         public Uri[] AllowOrigins { get; private set; }
 80         public string ErrorMessage { get; private set; }
 81 
 82         public CorsAttribute(params string[] allowOrigins)
 83         {
 84             var tmp = (allowOrigins ?? new string[0]);
 85             if (tmp.Length == 1 && "*" == tmp[0])
 86             {
 87                 this.AllowOrigins = null;
 88             }
 89             else
 90             {
 91                 this.AllowOrigins = tmp.Select(origin => new Uri(origin)).ToArray();
 92             }
 93         }
 94 
 95         public bool TryEvaluate(HttpRequestMessage request, out IDictionary<string, string> headers)
 96         {
 97             headers = null;
 98 
 99             //bugfix: GetValues在找不到时会报错
100             IEnumerable<string> origins;
101             if (request.Headers.TryGetValues("Origin", out origins))
102             {
103                 string origin = origins.FirstOrDefault();
104                 if (!String.IsNullOrEmpty(origin))
105                 {
106                     Uri originUri = new Uri(origin);
107                     if (this.AllowOrigins == null || this.AllowOrigins.Contains(originUri))//支持"*"
108                     {
109                         headers = this.GenerateResponseHeaders(request);
110                         return true;
111                     }
112                 }
113             }
114             this.ErrorMessage = "Cross-origin request denied";
115             return false;
116         }
117 
118         private IDictionary<string, string> GenerateResponseHeaders(HttpRequestMessage request)
119         {
120             //设置响应报头"Access-Control-Allow-Methods"
121             string origin = request.Headers.GetValues("Origin").First();
122             Dictionary<string, string> headers = new Dictionary<string, string>();
123             headers.Add("Access-Control-Allow-Origin", origin);
124             if (request.IsPreflightRequest())
125             {
126                 //设置响应报头"Access-Control-Request-Headers"
127                 //和"Access-Control-Allow-Headers"
128                 string requestHeaders = request.Headers.GetValues("Access-Control-Request-Headers").FirstOrDefault();
129                 if (!string.IsNullOrEmpty(requestHeaders))
130                 {
131                     headers.Add("Access-Control-Allow-Headers", requestHeaders);
132                 }
133                 //string requestMethods = request.Headers.GetValues("Access-Control-Request-Method").FirstOrDefault();
134                 //if (!string.IsNullOrEmpty(requestHeaders))
135                 //{
136                 //    headers.Add("Access-Control-Allow-Methods", requestMethods + ", OPTIONS");
137                 //}
138                 //else
139                 //{
140                     headers.Add("Access-Control-Allow-Methods", "*");
141                 //}
142             }
143             headers.Add("Access-Control-Allow-Credentials", "true");//true, 允许跨域传cookie, 要在POST的返回值中也存在
144             return headers;
145         }
146     }
View Code

一个简单的异常过滤器

 1     public class JsonExceptionFilter : FilterAttribute, IExceptionFilter//, IActionFilter
 2     {
 3 
 4         public Task ExecuteExceptionFilterAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
 5         {
 6             return Task.Factory.StartNew((obj) =>
 7             {
 8                 CancellationToken ct = (CancellationToken)obj;
 9                 if (actionExecutedContext.Exception != null)
10                 {
11                     var res = new ResultModel<String>(false, actionExecutedContext.Exception.GetType().ToString());
12                     var resText = JsonConvert.SerializeObject(res);
13                     if (actionExecutedContext.Response == null)
14                     {
15                         actionExecutedContext.Response = new HttpResponseMessage();
16                     }
17                     actionExecutedContext.Response.Content = new StringContent("{\"State\":-255}", Encoding.UTF8, "application/json");
18                 }
19             }, cancellationToken, cancellationToken);
20         }
21     }
View Code

现在存在的问题是如果Action内部有异常被过滤器捕获, CorsMessageHandler就卡死在

var tmp = base.SendAsync(request, cancellationToken);
response = tmp.Result;//卡死在这里, 用tmp.Wait();也是一样卡死

调试看task的State是WaitingForActivation, 但是用Wait/Result无限期卡死无法得到结果, 但是用await(Microsoft.Bcl.Async引入)就不存在问题, 能正常执行出结果

  

posted @ 2014-09-05 11:17 逸风之狐 阅读(...) 评论(...) 编辑 收藏