سؤال

كيف يمكنني استخدام HttpWebRequest (.NET, C#) بشكل غير متزامن؟

هل كانت مفيدة؟

المحلول

HttpWebRequest.BeginGetResponse()

HttpWebRequest webRequest;

void StartWebRequest()
{
    webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}

void FinishWebRequest(IAsyncResult result)
{
    webRequest.EndGetResponse(result);
}

ويتم استدعاء الدالة رد الاتصال عندما يكون عملية غير متزامنة كاملة. تحتاج إلى استدعاء ما لا يقل عن EndGetResponse() من هذا وظيفة.

نصائح أخرى

ونظرا الجواب:

HttpWebRequest webRequest;

void StartWebRequest()
{
    webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}

void FinishWebRequest(IAsyncResult result)
{
    webRequest.EndGetResponse(result);
}

هل يمكن أن ترسل مؤشر الطلب أو أي شيء آخر من هذا القبيل:

void StartWebRequest()
{
    HttpWebRequest webRequest = ...;
    webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), webRequest);
}

void FinishWebRequest(IAsyncResult result)
{
    HttpWebResponse response = (result.AsyncState as HttpWebRequest).EndGetResponse(result) as HttpWebResponse;
}

تحياتي

والجميع كان حتى الآن خطأ، لأن BeginGetResponse() يفعل بعض العمل على الترابط الحالي. من وثائق :

<اقتباس فقرة>   

وطريقة BeginGetResponse يتطلب بعض مهام الإعداد متزامن ل   كامل (القرار DNS، كشف وكيل واتصال مأخذ توصيل TCP،   على سبيل المثال) قبل أن يصبح هذا الأسلوب غير متزامن. كنتيجة ل،   لا ينبغي أبدا أن يسمى هذا الأسلوب على واجهة المستخدم (UI) موضوع   لأنه قد يستغرق وقتا طويلا (تصل إلى عدة دقائق   اعتمادا على إعدادات الشبكة) لاستكمال متزامن الأولي   مهام الإعداد قبل استثناء لخطأ يتم طرح أو الأسلوب   ينجح.

وذلك لقيام هذا الحق:

void DoWithResponse(HttpWebRequest request, Action<HttpWebResponse> responseAction)
{
    Action wrapperAction = () =>
    {
        request.BeginGetResponse(new AsyncCallback((iar) =>
        {
            var response = (HttpWebResponse)((HttpWebRequest)iar.AsyncState).EndGetResponse(iar);
            responseAction(response);
        }), request);
    };
    wrapperAction.BeginInvoke(new AsyncCallback((iar) =>
    {
        var action = (Action)iar.AsyncState;
        action.EndInvoke(iar);
    }), wrapperAction);
}

ويمكنك بعد ذلك تفعل ما تريد لمع الاستجابة. على سبيل المثال:

HttpWebRequest request;
// init your request...then:
DoWithResponse(request, (response) => {
    var body = new StreamReader(response.GetResponseStream()).ReadToEnd();
    Console.Write(body);
});

أسهل طريقة حتى الآن هي باستخدام TaskFactory.FromAsync من TPL.إنها حرفيًا عبارة عن سطرين من التعليمات البرمجية عند استخدامها مع الجديد غير متزامن/انتظار الكلمات الدالة:

var request = WebRequest.Create("http://www.stackoverflow.com");
var response = (HttpWebResponse) await Task.Factory
    .FromAsync<WebResponse>(request.BeginGetResponse,
                            request.EndGetResponse,
                            null);
Debug.Assert(response.StatusCode == HttpStatusCode.OK);

إذا لم تتمكن من استخدام برنامج التحويل البرمجي C#5، فيمكن تحقيق ما سبق باستخدام مهمة.متابعة مع طريقة:

Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse,
                                    request.EndGetResponse,
                                    null)
    .ContinueWith(task =>
    {
        var response = (HttpWebResponse) task.Result;
        Debug.Assert(response.StatusCode == HttpStatusCode.OK);
    });

وانتهى بي الأمر باستخدام BackgroundWorker، فمن غير متزامن بالتأكيد عكس بعض الحلول المذكورة أعلاه، فإنه يعالج العودة إلى موضوع GUI بالنسبة لك، وأنه من السهل جدا أن نفهم.

وكما أنه من السهل جدا لمعالجة الاستثناءات، لأنها في نهاية المطاف في طريقة RunWorkerCompleted، ولكن تأكد من قراءة هذا: <لأ href = "https://stackoverflow.com/questions/1044460/unhandled-exceptions-in -backgroundworker "> استثناءات غير معالج في BackgroundWorker

واعتدت WebClient ولكن من الواضح هل يمكن استخدام HttpWebRequest.GetResponse إذا أردت.

var worker = new BackgroundWorker();

worker.DoWork += (sender, args) => {
    args.Result = new WebClient().DownloadString(settings.test_url);
};

worker.RunWorkerCompleted += (sender, e) => {
    if (e.Error != null) {
        connectivityLabel.Text = "Error: " + e.Error.Message;
    } else {
        connectivityLabel.Text = "Connectivity OK";
        Log.d("result:" + e.Result);
    }
};

connectivityLabel.Text = "Testing Connectivity";
worker.RunWorkerAsync();

لقد تغير .NET منذ نشر العديد من هذه الإجابات، وأود تقديم إجابة أكثر حداثة.استخدم طريقة غير متزامنة لبدء ملف Task التي سيتم تشغيلها على موضوع الخلفية:

private async Task<String> MakeRequestAsync(String url)
{    
    String responseText = await Task.Run(() =>
    {
        try
        {
            HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
            WebResponse response = request.GetResponse();            
            Stream responseStream = response.GetResponseStream();
            return new StreamReader(responseStream).ReadToEnd();            
        }
        catch (Exception e)
        {
            Console.WriteLine("Error: " + e.Message);
        }
        return null;
    });

    return responseText;
}

لاستخدام الطريقة غير المتزامنة:

String response = await MakeRequestAsync("http://example.com/");

تحديث:

لا يعمل هذا الحل مع تطبيقات UWP التي تستخدم WebRequest.GetResponseAsync() بدلاً من WebRequest.GetResponse(), ، ولا يستدعي Dispose() الأساليب عند الاقتضاء.لدىdragansr حل بديل جيد يعالج هذه المشكلات.

public void GetResponseAsync (HttpWebRequest request, Action<HttpWebResponse> gotResponse)
    {
        if (request != null) { 
            request.BeginGetRequestStream ((r) => {
                try { // there's a try/catch here because execution path is different from invokation one, exception here may cause a crash
                    HttpWebResponse response = request.EndGetResponse (r);
                    if (gotResponse != null) 
                        gotResponse (response);
                } catch (Exception x) {
                    Console.WriteLine ("Unable to get response for '" + request.RequestUri + "' Err: " + x);
                }
            }, null);
        } 
    }
public static async Task<byte[]> GetBytesAsync(string url) {
    var request = (HttpWebRequest)WebRequest.Create(url);
    using (var response = await request.GetResponseAsync())
    using (var content = new MemoryStream())
    using (var responseStream = response.GetResponseStream()) {
        await responseStream.CopyToAsync(content);
        return content.ToArray();
    }
}

public static async Task<string> GetStringAsync(string url) {
    var bytes = await GetBytesAsync(url);
    return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top