Tại sao HttpContext.Current là rỗng sau khi chờ đợi?


89

Tôi có mã WebAPI thử nghiệm sau, tôi không sử dụng WebAPI trong sản xuất nhưng tôi thực hiện điều này vì tôi đã có một cuộc thảo luận về câu hỏi này: Câu hỏi về WebAPI Async

Dù sao, đây là phương pháp WebAPI vi phạm:

public async Task<string> Get(int id)
{
    var x = HttpContext.Current;
    if (x == null)
    {
        // not thrown
        throw new ArgumentException("HttpContext.Current is null");
    }

    await Task.Run(() => { Task.Delay(500); id = 3; });

    x = HttpContext.Current;
    if (x == null)
    {
        // thrown
        throw new ArgumentException("HttpContext.Current is null");
    }

    return "value";
}

Ở đây tôi tin rằng ngoại lệ thứ hai được mong đợi bởi vì khi awaithoàn tất, nó có thể sẽ nằm trên một luồng khác, nơi HttpContext.Currentmột biến tĩnh của luồng sẽ không còn phân giải thành giá trị thích hợp. Bây giờ, dựa trên bối cảnh đồng bộ hóa, nó thực sự có thể bị buộc quay lại cùng một chuỗi sau thời gian chờ đợi nhưng tôi không làm bất cứ điều gì ưa thích trong thử nghiệm của mình. Đây chỉ là một cách sử dụng đơn giản, ngây thơ await.

Trong các nhận xét trong một câu hỏi khác, tôi đã được nói rằng HttpContext.Currentsẽ giải quyết sau một thời gian chờ đợi. Thậm chí có một bình luận khác về câu hỏi này cho thấy điều tương tự. Vậy sự thật là gì? Nó có nên giải quyết? Tôi nghĩ là không, nhưng tôi muốn một câu trả lời có thẩm quyền về điều này bởi vì asyncawaitđủ mới để tôi không thể tìm thấy bất cứ điều gì dứt khoát.

TL; DR: Có HttpContext.Currentkhả năng nullsau một await?


3
Câu hỏi của bạn không rõ ràng - bạn đã nói những gì bạn mong đợi sẽ xảy ra và các nhận xét chỉ ra rằng đó điều đang xảy ra ... vậy điều gì khiến bạn bối rối?
Jon Skeet

@ user2674389, điều này gây hiểu lầm. Đó là AspNetSynchronizationContextchăm sóc HttpContext, không phải await. Hơn nữa, lệnh gọi lại tiếp tục cho awaitcó thể (và rất có thể sẽ) xảy ra trên một luồng khác cho mô hình thực thi API Web.
mũi,

chỉnh sửa để đặt ra một câu hỏi ngắn gọn
welegan

1
@JoepBeusenberg Việc tạo các hội đồng riêng biệt chỉ hoạt động khi chúng được gọi từ một hội đồng đang thực thi trong ngữ cảnh của một yêu cầu HTTP của một ngăn xếp web cụ thể có vẻ như việc này có thể khiến việc thử nghiệm, bảo trì và sử dụng lại trở thành một thách thức.
Darrel Miller

1
@DarrelMiller Hoàn toàn ngược lại. Tôi đã tách logic nghiệp vụ khỏi dự án web thực tế. Bằng cách sử dụng phương thức tiêm phụ thuộc, tôi có thể thêm một thư viện nhận biết webapi ở đầu logic nghiệp vụ. Nhưng thư viện này bị hỏng khi logic nghiệp vụ đã thực hiện .ConfigureAwait(false)ở đâu đó dưới dòng. Không có yêu cầu hoặc bộ điều khiển nào được chuyển giao rõ ràng qua lớp nghiệp vụ, vì lớp đó không được biết đến trên web. Điều này rất hữu ích, chẳng hạn đối với một mô-đun ghi nhật ký có thể đưa vào chi tiết yêu cầu khi logic nghiệp vụ viết một mô-đun chung TraceInformation.
Joep Beusenberg,

Câu trả lời:


148

Hãy đảm bảo rằng bạn đang viết một ứng dụng ASP.NET 4.5 và nhắm mục tiêu 4.5. asyncawaitcó hành vi không xác định trên ASP.NET trừ khi bạn đang chạy trên 4.5 đang sử dụng ngữ cảnh đồng bộ hóa "thân thiện với tác vụ" mới.

Đặc biệt, điều này có nghĩa là bạn phải:

  • Đặt httpRuntime.targetFrameworkthành 4.5, hoặc
  • Trong của bạn appSettings, đặt aspnet:UseTaskFriendlySynchronizationContextthành true.

Thông tin thêm có sẵn tại đây .


2
Tôi vừa tạo một dự án ASP.NET 4.5 WebAPI mới, sao chép / dán mã của bạn và thực hiện một bài kiểm tra. Nó hoạt động hoàn hảo đối với tôi (không có ngoại lệ nào được ném ra). Vui lòng kiểm tra lại xem bạn có đang chạy và nhắm mục tiêu 4.5 hay không.
Stephen Cleary

3
Tôi có khung mục tiêu: .NET Framework 4.5 đã đặt. Tôi không biết phải nói gì với bạn, nó chắc chắn là trống trên máy cục bộ của tôi.
welegan

24
những <httpRuntime targetFramework="4.5" />gì là giải quyết nó, nhờ làm rõ.
welegan

1
@Vince: 4.5.1 sẽ hoạt động tốt. Tôi không chắc liệu bạn có nên / có thể đặt targetFrameworkthành 4.5.1 hay 4.5 hay không, nhưng không đồng bộ trên 4.5.1 sẽ hoạt động tốt.
Stephen Cleary

1
Điều gì sẽ xảy ra nếu bạn viết trình xử lý được quản lý của riêng mình? Tôi tiếp tục nghĩ ra HttpContext.Current = null trong đó ngay cả sau khi thêm các mục này vào web.config.
Brain2000

28

Như @StephenCleary đã chỉ ra một cách chính xác, bạn cần điều này trong web.config của mình:

<httpRuntime targetFramework="4.5" />

Khi tôi lần đầu tiên khắc phục sự cố này, tôi đã thực hiện tìm kiếm trên toàn bộ giải pháp cho vấn đề trên, xác nhận rằng nó có mặt trong tất cả các dự án web của tôi và nhanh chóng loại bỏ nó là thủ phạm. Cuối cùng, tôi chợt thấy những kết quả tìm kiếm đó trong ngữ cảnh đầy đủ:

<!--
  For a description of web.config changes for .NET 4.5 see http://go.microsoft.com/fwlink/?LinkId=235367.

  The following attributes can be set on the <httpRuntime> tag.
    <system.Web>
      <httpRuntime targetFramework="4.5" />
    </system.Web>
-->

Làm đi.

Bài học: Nếu bạn nâng cấp một dự án web lên 4.5, bạn vẫn cần cài đặt đó theo cách thủ công.


22
Một gotcha khác là cái này khác với <compilation targetFramework "4.5" />
Andrew

3

Thử nghiệm của tôi có sai sót hay có phần tử web.config nào đó mà tôi đang thiếu ở đây có thể khiến HttpContext.Current giải quyết chính xác sau một thời gian chờ đợi không?

Kiểm tra của bạn không có sai sót và HttpContext.Current không được rỗng sau thời gian chờ đợi vì trong ASP.NET Web API khi bạn chờ đợi, điều này sẽ đảm bảo rằng mã sau thời gian chờ này được chuyển đúng HttpContext đã có trước thời gian chờ đợi.


Bạn có chắc chắn về cùng một chuỗi tiếp tục cho WebAPI không? Tôi đã xử lý trường hợp đó là một chủ đề khác.
mũi,

4
ASP.NET sẽ tiếp tục trên bất kỳ luồng nhóm luồng nào, nhưng với ngữ cảnh yêu cầu chính xác.
Stephen Cleary

2
Vâng, bạn nói đúng, luồng có thể không giống nhưng HttpContext.Current sẽ giống như trước khi chờ đợi. Tôi đã cập nhật câu hỏi của mình.
Darin Dimitrov

4
HttpContext.Current là null sau một thời gian chờ đợi trong mã của tôi và tôi đang nhắm mục tiêu .net 4.6.1.
Triynko

1
đối với tôi HttpContext.Current là null trước một hàm await
JobaDiniz

2

Tôi gặp phải vấn đề này gần đây. Như Stephen đã chỉ ra rằng việc không đặt khung mục tiêu rõ ràng có thể tạo ra vấn đề này.

Trong trường hợp của tôi, API Web của chúng tôi đã được di chuyển sang phiên bản 4.6.2 nhưng khung mục tiêu thời gian chạy chưa bao giờ được chỉ định trong cấu hình web, vì vậy về cơ bản điều này bị thiếu trong thẻ <system.web>:

Nếu bạn nghi ngờ về phiên bản khung mà bạn đang chạy, điều này có thể hữu ích: Thêm dòng sau vào bất kỳ phương thức API Web nào của bạn và đặt điểm ngắt để xác minh loại nào hiện đang được tải trong thời gian chạy và xác minh rằng nó không phải là triển khai Kế thừa:

Bạn sẽ thấy điều này (AspNetSynchronizationContext):

nhập mô tả hình ảnh ở đây

Thay vì LegazyAspNetSynchronizationContext (Đó là những gì tôi đã thấy trước khi thêm khung mục tiêu):

nhập mô tả hình ảnh ở đây

Nếu bạn truy cập mã nguồn ( https://referencesource.microsoft.com/#system.web/LegacyAspNetSynchronizationContext.cs ), bạn sẽ thấy rằng việc triển khai Kế thừa của giao diện này thiếu hỗ trợ không đồng bộ.

nhập mô tả hình ảnh ở đây

Tôi đã dành rất nhiều thời gian để cố gắng tìm ra nguồn gốc của vấn đề và phản hồi của Stephen đã giúp ích rất nhiều. Hy vọng câu trả lời này cung cấp thêm một số thông tin về vấn đề.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.