0

Trying to implement a WebRequest and return to the caller synchronously. I have tried various implementations and I think this would be the most appropriate so far.

Unfortunately the following code throws an InvalidOperationException with the message

EndGetResponse can only be called once for each asynchronous operation

I really struggled enough to make this happen and its really vital to the library I build to use the WebRequest like this.

The following code is intend to use in Windows Phone 8 and Windows 8 platforms.

I already understand the async/await pattern and used it, but it is REALLY vital for me to use the synchronous version of the web service request in a part of my library.

The code:

public void ExecuteRequest(string url, string requestData)
{
        WebRequest request = WebRequest.Create(new Uri(url));
        request.Method = "POST";
        request.ContentType = "application/x-www-form-urlencoded";
        request.Headers["Header-Key"] = "AKey";

        DTOWebRequest webRequestState = new DTOWebRequest
        {
            Data = requestData,
            Request = request
        };

        ManualResetEventSlim resetEventSlim = new ManualResetEventSlim(false);

        // Begin the request using a delegate
        request.BeginGetRequestStream(ar =>
        {
            DTOWebRequest requestDataObj = (DTOWebRequest )ar.AsyncState;
            HttpWebRequest requestStream = (HttpWebRequest)requestDataObj.Request;

            string data = requestDataObj.Data;

            // Convert the string into a byte array.
            byte[] postBytes = Encoding.UTF8.GetBytes(data);

            try
            {
                // End the operation
                using (Stream endGetRequestStream = requestStream.EndGetRequestStream(ar))
                {
                    // Write to the request stream.
                    endGetRequestStream.Write(postBytes, 0, postBytes.Length);
                }

                // Get the response using a delegate
                requestStream.BeginGetResponse(result =>
                {
                    DTOWebRequest requestDataObjResult = (DTOWebRequest )ar.AsyncState;
                    HttpWebRequest requestResult = (HttpWebRequest)requestDataObjResult.Request;

                    try
                    {
                        // End the operation
                        using (HttpWebResponse response = (HttpWebResponse)requestResult.EndGetResponse(ar)) // Here the exception is thrown.
                        {
                            HttpStatusCode rcode = response.StatusCode;
                            Stream streamResponse = response.GetResponseStream();
                            StreamReader streamRead = new StreamReader(streamResponse);

                            // The Response
                            string responseString = streamRead.ReadToEnd();

                            if (!string.IsNullOrWhiteSpace(requestDataObjResult.FileName))
                            {
                                FileRepository fileRepo = new FileRepository();
                                fileRepo.Delete(requestDataObjResult.FileName);
                            }

                            Debug.WriteLine("Response : {0}", responseString);
                        }
                    }
                    catch (WebException webEx)
                    {
                        WebExceptionStatus status = webEx.Status;
                        WebResponse responseEx = webEx.Response;
                        Debug.WriteLine(webEx.ToString());
                    }

                    resetEventSlim.Set(); // Signal to return handler
                }, requestDataObj);
            }
            catch (WebException webEx)
            {
                WebExceptionStatus status = webEx.Status;
                WebResponse responseEx = webEx.Response;
                Debug.WriteLine(webEx.ToString());
            }    
        }, webRequestState);

        resetEventSlim.Wait(5000); // Wait either for Set() or a timeout 5 secs.
    }
}

Thank you.

George Taskos
  • 8,324
  • 18
  • 82
  • 147

1 Answers1

1

You can't do synchronous web calls in Windows Phone and that's why you aren't.

If you were, you'd be calling GetRequestStream instead of BeginGetRequestStram/EndGetRequestStream.

The only reason to be synchronous on Windows Phone is to block the UI which is a very bad idea.

You should use an HttpClient and àsync-await` instead.

But if you really think you should (and can) do asynchronous calls on Windows Phone, you can always try something like this:

public void ExecuteRequest(string url, string requestData)
{
    try
    {
        WebRequest request = WebRequest.Create(new Uri(url));
        request.Method = "POST";
        request.ContentType = "application/x-www-form-urlencoded";
        request.Headers["Header-Key"] = "AKey";

        // Convert the string into a byte array.
        byte[] postBytes = Encoding.UTF8.GetBytes(requestData);

        using (var requestStream = request.EndGetRequestStream(request.BeginGetRequestStream(null, null)))
        {
            // Write to the request stream.
            endGetRequestStream.Write(postBytes, 0, postBytes.Length);
        }

        using (var response = request.EndGetResponse(request.BeginGetResponse(null, null)))
        {
            using (var streamRead = new StreamReader(response.GetResponseStream()))
            {
                // The Response
                string responseString = streamRead.ReadToEnd();

                if (!string.IsNullOrWhiteSpace(requestDataObjResult.FileName))
                {
                    var fileRepo = new FileRepository();
                    fileRepo.Delete(request.FileName);
                }

                Debug.WriteLine("Response : {0}", responseString);
            }
        }
    }
    catch (WebException webEx)
    {
        WebExceptionStatus status = webEx.Status;
        WebResponse responseEx = webEx.Response;
        Debug.WriteLine(webEx.ToString());
    }    
}

But I really think you should revise your decision/need.

Paulo Morgado
  • 14,111
  • 3
  • 31
  • 59
  • Never crossed my mind to pass the BeginXxx method to the EndXxx method. Since they receive an IAsyncResult it waits for it to finish! Thank you so much for the answer. But, it works great for the Windows 8, though for Windows Phone 8 it throws a NotSupportedException in - using (var response = request.EndGetResponse(request.BeginGetResponse(null, null))) - Any idea? I can provide the stacktrace if you want. – George Taskos Sep 08 '13 at 23:01
  • I guess it's telling you to never do that, hence the message, Specified method is not supported! – George Taskos Sep 08 '13 at 23:13
  • I hadn't tested for Windows Phone. It's just a known technique used since.NET 2.0 because you can't mix sync and async APIs when using HttpWebRequest. It's telling you that you can't do it synchronously. Why do you think you have to do it synchronously? – Paulo Morgado Sep 09 '13 at 06:01
  • I really know that synchronously is bad, but there is an action before application termination that I want to happen synchronously. Other than that I use heavily async/await and the APM pattern, it's just one case.Hope I can make it work to Windows Phone too, but it doesn't seem to let you do it. Windows 8 works great though! – George Taskos Sep 09 '13 at 12:33
  • Try explicitly writing the callback methods instead of using lambdas. It will give you better control and feedback of what error is happening and why. In your DTO class send only what's needed (I would say only the data). I'll try that myself later, if I can. – Paulo Morgado Sep 09 '13 at 17:08
  • I followed your answer. No lambdas here. I also removed the DTO completely since it is not needed. Thank you for trying with me. Appreciate it. – George Taskos Sep 09 '13 at 18:43
  • And where did that get you? – Paulo Morgado Sep 09 '13 at 22:47
  • To the error. I have created a new question for this. http://stackoverflow.com/q/18699949/122769 – George Taskos Sep 09 '13 at 23:58