Here is the following code:
public static string Post(string requestUriString, string s)
{
var request = (HttpWebRequest)WebRequest.Create(requestUriString);
var data = Encoding.ASCII.GetBytes(s);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
using (var stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
try
{
var response = (HttpWebResponse)request.GetResponse();
return new StreamReader(response.GetResponseStream()).ReadToEnd();
}
catch (WebException webException)
{
Console.WriteLine(webException);
throw;
}
}
When the program throws an exception, the try / catch block works correctly.
However when I try to rethrow the exception, the program crashes.
It should crash as you don't handle exception re-throw, you need to try catch in Main() method as below.
static void Main(string[] args)
{
try
{
Post("https://stackoverflow.com/", "test");
}
catch (Exception)
{
// Handle exception re-throw,
}
}
Do something like this:
class Program {
static void Main(string[] args) {
var response = Post(...);
if(response.Status == "success") {
//ok
} else {
//request failed
}
}
public static string Post(string requestUriString, string s) {
var request = (HttpWebRequest)WebRequest.Create(requestUriString);
var data = Encoding.ASCII.GetBytes(s);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
using (var stream = request.GetRequestStream()) {
stream.Write(data, 0, data.Length);
}
try {
var response = (HttpWebResponse)request.GetResponse();
var body = new StreamReader(response.GetResponseStream()).ReadToEnd();
return new Response("success", body);
} catch (WebException webException) {
Console.WriteLine(webException);
return new Response("failed", "");
}
}
class Response {
public string Status { get; private set; }
public string Body { get; private set; }
public Response(string status, string response) {
Status = status;
Body = response;
}
}
}
Depending on your needs & app size you could make the status an enum based on the http response code(200/404/500...) etc and add some more handling because the sample I posted isn't something you should use for something bigger or production critical.
You mentioned it's a console app, if it's just something that does a single request and nothing else this should do the job just fine.
Problem solved with this code :
using System.Net;
using System.Text;
using System.IO;
using System;
namespace Botnet
{
class Program
{
static void Main(string[] args)
{
try
{
Console.WriteLine(Post("http://www.example.com/recepticle.aspx", "thing1=hello&thing2=world"));
}
catch (WebException webException)
{
Console.WriteLine(webException);
}
finally
{
Console.ReadLine();
}
}
public static string Post(string requestUriString, string s)
{
var request = (HttpWebRequest)WebRequest.Create(requestUriString);
var data = Encoding.ASCII.GetBytes(s);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
using (var stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
try
{
var response = (HttpWebResponse)request.GetResponse();
return new StreamReader(response.GetResponseStream()).ReadToEnd();
}
catch (WebException webException)
{
Console.WriteLine(webException);
throw;
}
}
}
}
Hi I will modify previous answer a little bit.
class Response<T> {
public HttpStatusCode Status { get; private set; }
public T Body { get; private set; }
public bool Success { get; private set; }
public string Reason {get; private set; }
public Response(T body) {
Success = true;
Body = body;
HttpStatusCode = HttpStatusCode.OK
}
public Response(string reason)
{
Reason = reason;
Success = false;
Status = HttpStatusCode.BadRequest;
}
public Response( string reason, HttpStatusCode status)
{
Reason = reason;
Status = status;
Success = false;
}
}
This is more generic solution.
Related
How I can change this function to async with Framework 4.6.1? The DGV has a lot of data and blocks the rest of the processes.
DGV is updated every 30 seconds with timer.
any other solution do not block the next process until this is finnished?
I may have something wrong, I am starting in the world of programming and I have many things to learn.
private void FillTimes()
{
var strResponse = CallJson($"RESTAPI URL");
if (strResponse != null)
{
var jResult = JsonConvert.DeserializeObject<JsonResults>(strResponse);
BindingSource bsResults = new BindingSource();
bsResults.DataSource = jResult.Results;
if (bsResults.DataSource != null)
{
DgvOnline.DataSource = bsResults.DataSource;
}
}
}
CallJson
private string CallJson(string strURL)
{
RestTiming rJson = new RestTiming();
rJson.endPoint = strURL;
rJson.token = apiToken;
string strResponse = string.Empty;
strResponse = rJson.makeRequest();
return strResponse;
}
ResTiming
using System;
using System.IO;
using System.Net;
using System.Windows.Forms;
namespace Timing
{
public enum httpVerb
{
GET,
POST,
PUT,
DELETE
}
class RestTiming
{
public string endPoint { get; set; }
public string token { get; set; }
public httpVerb httpMethod { get; set; }
public string userName { get; set; }
public string userPassword { get; set; }
public string postJSON { get; set; }
public RestTiming()
{
endPoint = string.Empty;
token = string.Empty;
}
public string makeRequest()
{
if (InternetAvailable())
{
string strResponseValue = string.Empty;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(endPoint);
request.Method = httpMethod.ToString();
request.ContentType = "application/json";
request.Accept = "application/json";
request.Headers.Add("Authorization", token);
request.UserAgent = #"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36";
if ((request.Method == "POST" || request.Method == "PUT") && postJSON != string.Empty)
{
using (StreamWriter swJSONPayload = new StreamWriter(request.GetRequestStream()))
{
swJSONPayload.Write(postJSON);
swJSONPayload.Close();
}
}
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)request.GetResponse();
// Process the response stream... (could be JSON, XML or HTML etc...)
using (Stream responseStream = response.GetResponseStream())
{
if (responseStream != null)
{
using (StreamReader reader = new StreamReader(responseStream))
{
strResponseValue = reader.ReadToEnd();
}// End of StreamReader
}
}// End of using ResponseStream
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError && ex.Response != null)
{
var resp = (HttpWebResponse)ex.Response;
if (resp.StatusCode == HttpStatusCode.Unauthorized)
{
MessageBox.Show("Unauthorized", "Unauthorized", MessageBoxButtons.OK, MessageBoxIcon.Stop);
Environment.Exit(1);
}
}
}
return strResponseValue;
}
else
{
return null;
}
}
private static Boolean InternetAvailable()
{
try
{
using (WebClient client = new WebClient())
{
using (client.OpenRead("http://www.google.com/"))
{
return true;
}
}
}
catch
{
return false;
}
}
}
}
Thanks
I'm not going to weigh in on whether your code should be async or not.
If you want the synchronous work to happen in a background thread, you can execute it using Task.Run as follows:
private async Task FillTimes()
{
return Task.Run(() => {
{
var strResponse = CallJson($"RESTAPI URL");
if (strResponse != null)
{
var jResult = JsonConvert.DeserializeObject<JsonResults>(strResponse);
BindingSource bsResults = new BindingSource();
bsResults.DataSource = jResult.Results;
if (bsResults.DataSource != null)
{
DgvOnline.DataSource = bsResults.DataSource;
}
}
}
}
Update
If CallJson is IO Bound work then you could make that async, also you would append the Async suffix to the the method name to be consistent. Your resulting method would look like this.
private async Task FillTimes()
{
var strResponse = await CallJsonAsync($"RESTAPI URL");
if (strResponse != null)
{
var jResult = JsonConvert.DeserializeObject<JsonResults>(strResponse);
BindingSource bsResults = new BindingSource();
bsResults.DataSource = jResult.Results;
if (bsResults.DataSource != null)
{
DgvOnline.DataSource = bsResults.DataSource;
}
}
}
And your async method would look like this
private async Task<responseType> CallJsonAsync(<something>)
{
...
await SomethingAsync(...);
...
return something;
}
Original
How I can change this function to async with Framework 4.6.1
There is no obvious IO Bound work, and no method that would lend itself towards an async call (that i can see).
So to answer the question, "How I can change this function to async?", the answer is you shouldn't, let it be synchronous.
If this does not update the UI, you could call this method from a Task, and await that
Updated
class RestTiming
{
public string endPoint { get; set; }
public string token { get; set; }
public httpVerb httpMethod { get; set; }
public string userName { get; set; }
public string userPassword { get; set; }
public string postJSON { get; set; }
public RestTiming()
{
endPoint = string.Empty;
token = string.Empty;
}
public async Task<string> makeRequest() //1. Changed to async and return type
{
if (InternetAvailable())
{
string strResponseValue = string.Empty;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(endPoint);
request.Method = httpMethod.ToString();
request.ContentType = "application/json";
request.Accept = "application/json";
request.Headers.Add("Authorization", token);
request.UserAgent = #"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36";
if ((request.Method == "POST" || request.Method == "PUT") && postJSON != string.Empty)
{
using (StreamWriter swJSONPayload = new StreamWriter(await request.GetRequestStreamAsync())) //2. changed to asynchronous call
{
swJSONPayload.Write(postJSON);
swJSONPayload.Close();
}
}
WebResponse response = null; //(a) updated
try
{
response =await request.GetResponseAsync();
// Process the response stream... (could be JSON, XML or HTML etc...)
using (Stream responseStream =response.GetResponseStream()) //(b) updated
{
if (responseStream != null)
{
using (StreamReader reader = new StreamReader(responseStream))
{
strResponseValue = await reader.ReadToEndAsync(); // (c) updated
}// End of StreamReader
}
}// End of using ResponseStream
}
catch (WebException ex)
{
if (ex.Status == WebExceptionStatus.ProtocolError && ex.Response != null)
{
var resp = (HttpWebResponse)ex.Response;
if (resp.StatusCode == HttpStatusCode.Unauthorized)
{
MessageBox.Show("Unauthorized", "Unauthorized", MessageBoxButtons.OK, MessageBoxIcon.Stop);
Environment.Exit(1);
}
}
}
return strResponseValue;
}
else
{
return null;
}
}
private static Boolean InternetAvailable()
{
try
{
using (WebClient client = new WebClient())
{
using (client.OpenRead("http://www.google.com/"))
{
return true;
}
}
}
Changes in the second method
private async Task<string> CallJson(string strURL) //1. Changed to async and return type
{
RestTiming rJson = new RestTiming();
rJson.endPoint = strURL;
rJson.token = apiToken;
string strResponse = string.Empty;
strResponse =await rJson.makeRequest(); //2. made asynchronous
return strResponse;
}
The change in the first method
private async Task FillTimes() // 1. Make async and change return type
{
var strResponse =await CallJson($"RESTAPI URL"); // 2. add await to make asynchronous
...
}
Please let me know if this helps.
Additionally, check this link (Getting the Response of a Asynchronous HttpWebRequest)
Trying to call REST service using C# application:
namespace SrvClient
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
public class RestClient
{
public string EndPoint { get; set; }
public HttpVerb Method { get; set; }
public string ContentType { get; set; }
public string PostData { get; set; }
public RestClient()
{
EndPoint = "";
Method = HttpVerb.GET;
ContentType = "text/xml";
PostData = "";
}
public RestClient(string endpoint)
{
EndPoint = endpoint;
Method = HttpVerb.GET;
ContentType = "text/xml";
PostData = "";
}
public RestClient(string endpoint, HttpVerb method)
{
EndPoint = endpoint;
Method = method;
ContentType = "text/xml";
PostData = "";
}
public RestClient(string endpoint, HttpVerb method, string postData)
{
EndPoint = endpoint;
Method = method;
ContentType = "text/xml";
PostData = postData;
}
public string MakeRequest()
{
return MakeRequest("");
}
public string MakeRequest(string parameters)
{
var request = (HttpWebRequest)WebRequest.Create(EndPoint + parameters);
request.Method = Method.ToString();
request.ContentLength = 0;
request.ContentType = ContentType;
if (!string.IsNullOrEmpty(PostData) && Method == HttpVerb.PUT)
{
var encoding = new UTF8Encoding();
var bytes = Encoding.GetEncoding("iso-8859-1").GetBytes(PostData);
request.ContentLength = bytes.Length;
using (var writeStream = request.GetRequestStream())
{
writeStream.Write(bytes, 0, bytes.Length);
}
}
using (var response = (HttpWebResponse)request.GetResponse())
{
var responseValue = string.Empty;
if (response.StatusCode != HttpStatusCode.OK)
{
var message = String.Format("Request failed. Received HTTP {0}", response.StatusCode);
throw new ApplicationException(message);
}
// grab the response
using (var responseStream = response.GetResponseStream())
{
if (responseStream != null)
using (var reader = new StreamReader(responseStream))
{
responseValue = reader.ReadToEnd();
}
}
return responseValue;
}
}
} // class
private void button1_Click(object sender, EventArgs e)
{
var client = new RestClient();
client.EndPoint = #"http://localhost:11332";
client.Method = HttpVerb.GET;
client.PostData = "{postData: value}";
var json = client.MakeRequest("/Account/Login");
}
}
}
As call result I have string:
"<!DOCTYPE html>\r\n<html>\r\n<head>\r\n\t<meta charset=\"utf-8\" />\r\n\t<title>Login - My ASP.NET Application</title>\r\n\t\r\n</head>\r\n<body>\r\n\t\t<p>Welcome login)</p>\r\n\t\r\n\r\n\r\n<h2>Login</h2>\r\n\r\n<form action=\"/Account/Login\" method=\"post\"><input name=\"__RequestVerificationToken\" type=\"hidden\" value=\"_fykbIPsr7u1kGba8zUirPqbqnMG_-eyztTSwSJxB3XksVdrUrLWjsyC9NXWugXn34AxZerny4coFb5_6ILNpMmmG4GZgI-U_Oqhl1VQSzk1\" />\t<p>username:<br /><input class=\"text-box single-line\" id=\"UserName\" name=\"UserName\" type=\"text\" value=\"\" /></p>\r\n\t<p>pin:<br /><input class=\"text-box single-line\" id=\"Pin\" name=\"Pin\" type=\"text\" value=\"\" /></p>\r\n\t<p><input type=\"submit\" value=\"login\" /></p>\r\n</form>\r\n\t<script type=\"text/javascript\" src=\"/Content/js/jquery-2.1.4.min.js\"></script>\r\n\t<script type=\"text/javascript\" src=\"/Content/js/jquery-ui-1.11.4.min.js\"></script>\r\n\t\r\n\r\n<!-- Visual Studio Browser Link -->\r\n<script type=\"application/json\" id=\"__browserLink_initializationData\">\r\n {\"appName\":\"Unknown\",\"requestId\":\"fba1d156c3114ecbbe989a83cd4616c7\"}\r\n</script>\r\n<script type=\"text/javascript\" src=\"http://localhost:21213/b011fdffd90446038f2d9d01cd2a32bf/browserLink\" async=\"async\"></script>\r\n<!-- End Browser Link -->\r\n\r\n</body>\r\n</html>\r\n"
Looks like I need to login. How to make login call in this case?
I am working on a requirement where I need to create multiple issues in one go by Using the REST API. However, I start with uploading a single issue because I am new in API integration. I write few lines of code in c#. Here is my code:
static void Main(string[] args)
{
JavaScriptSerializer jss = new JavaScriptSerializer();
JiraCreateIssueRequest jcir = new JiraCreateIssueRequest();
//////////////////////////////////////////////////////////////////
string sUsername = "aaa#xyz.com";
string sPassword = "TestPassword";
string uri = #"https://domain.atlassian.net/rest/api/2/issue";
Uri address = new Uri(uri);
HttpWebRequest request;
//HttpWebResponse response = null;
StreamReader sr;
string sData = null;
string returnXML = string.Empty;
if (address == null) { throw new ArgumentNullException("address"); }
//jcir.project.ID = 100;
//jcir.Summary = "This issue is created by JIRA REST Api";
//jcir.Description = "This issue is created by JIRA REST Api";
//jcir.IssueType.ID = 1;
sData = #"{""fields"":{""project"":{""key"": ""SITT""},""summary"": ""REST API Uploading"",""description"":""Creating an issue via REST API"",""issuetype"": {""name"": ""Test""}}}";
//sData = jcir.ToString();
try
{
// Create and initialize the web request
request = WebRequest.Create(address) as HttpWebRequest;
request.Method = "POST";
request.ContentType = "application/json";
// Add the Authorization header to the web request
request.Credentials = new NetworkCredential(sUsername, sPassword);
//Write Data
if (sData != null)
{
byte[] byteData = UTF8Encoding.UTF8.GetBytes(sData);
// Set the content length in the request headers
request.ContentLength = byteData.Length;
// Write data
using (Stream postStream = request.GetRequestStream())
{
postStream.Write(byteData, 0, byteData.Length);
}
// Get response
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
// Get the response stream
StreamReader reader = new StreamReader(response.GetResponseStream());
string str = reader.ReadToEnd();
}
}
}
catch (WebException wex)
{
// This exception will be raised if the server didn't return 200 - OK
// Try to retrieve more information about the error
if (wex.Response != null)
{
using (HttpWebResponse errorResponse = (HttpWebResponse)wex.Response)
{
try
{
string sError = string.Format("The server returned '{0}' with the status code {1} ({2:d}).",
errorResponse.StatusDescription, errorResponse.StatusCode,
errorResponse.StatusCode);
sr = new StreamReader(errorResponse.GetResponseStream(), Encoding.UTF8);
returnXML = sr.ReadToEnd();
}
finally
{
if (errorResponse != null) errorResponse.Close();
}
}
}
else
{
throw new Exception(wex.Message);
}
}
}
public class JiraCreateIssueRequest
{
protected JavaScriptSerializer jss = new JavaScriptSerializer();
public JiraProject project = new JiraProject();
public string Summary { get; set; }
public string Description { get; set; }
public JiraIssueType IssueType = new JiraIssueType();
public override string ToString()
{
return jss.Serialize(this);
}
}
public class JiraCreateIssueResponse
{
}
public class JiraProject
{
public int ID { get; set; }
//public string Key { get; set; }
}
public class JiraIssueType
{
public int ID { get; set; }
//public string Name { get; set; }
}
But when I am running the above code, I am getting the '400' error. I googled it and found that this usually this error comes when the URL or the Username/Password are incorrect. I cross checked both the things however its correct.
May I know why this error is coming or what will be the resolution of the problem?
Your password is not your login password, it's an API token that you get from here:
https://id.atlassian.com/manage/api-tokens
Generate a token, then use that as your password.
I have following nested JSON:
{"Command":"helo",
"parameter" : {"Configured":false, "ApplicationString":"Something", "Hostname":"some",
"IPAddress":"0.0.0.0",
"UniqueID":"",
"Username":"me"}}
And I need to pass this string as JSON object to POST call to my web service in C#. Could anyone help me how to do this step?
Note: I am able to pass simple JSON like below:
var request = (HttpWebRequest)WebRequest.Create("http://localhost:8084");
request.ContentType = "text/json";
request.Method = "POST";
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
string json = new JavaScriptSerializer().Serialize(new
{
Command = "test",
name="pooja"
});
streamWriter.Write(json);
}
If I follow the same way to pass nested json like below :
string json = new JavaScriptSerializer().Serialize(new
{
Command = "test",
parameter = new JavaScriptSerializer().Serialize(new
{
Command = "test",
}),
});
I get below output :
{"Command":"test","parameter":"{\"Command\":\"test\"}"}
Let me know if you have any issues.
void Main()
{
CommandParamater exampleCommand = new CommandParamater
{
Command = "Foo",
Parameter = new Parameter
{
ApplicationString = "App String Foo",
Configured = true,
Hostname = "Bar",
IPAddress = "8.8.8.8",
UniqueID = Guid.NewGuid().ToString(),
Username = "FooBar"
}
};
string uri = "http://localhost:8084";
string data = JsonConvert.SerializeObject(exampleCommand);
Html htmlClient = new Html();
htmlClient.Post(uri, data, "application/json");
}
public class Html
{
public string Post(string uri, string data, string contentType)
{
byte[] dataBytes = Encoding.UTF8.GetBytes(data);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
request.ContentType = contentType;
request.ContentLength = dataBytes.Length;
using (Stream stream = request.GetRequestStream())
{
stream.Write(dataBytes, 0, dataBytes.Length);
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
}
public class Parameter
{
[JsonProperty("Configured")]
public bool Configured { get; set; }
[JsonProperty("ApplicationString")]
public string ApplicationString { get; set; }
[JsonProperty("Hostname")]
public string Hostname { get; set; }
[JsonProperty("IPAddress")]
public string IPAddress { get; set; }
[JsonProperty("UniqueID")]
public string UniqueID { get; set; }
[JsonProperty("Username")]
public string Username { get; set; }
}
public class CommandParamater
{
[JsonProperty("Command")]
public string Command { get; set; }
[JsonProperty("parameter")]
public Parameter Parameter { get; set; }
}
It may help you.
private string MakeRequest(string uri, string jsnPostData, string method)
{
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
if (request != null)
{
request.Method = method;
request.Timeout = 2000000;
request.ContentType = "application/json";
request.KeepAlive = true;
byte[] data = Encoding.UTF8.GetBytes(jsnPostData);
Stream dataStream = request.GetRequestStream();
dataStream.Write(data, 0, data.Length);
dataStream.Close();
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
string result = new StreamReader(response.GetResponseStream()).ReadToEnd();
return result;
}
else
return "";
}
catch (Exception ex)
{
Response.Write("<b>Error :</b>" + ex.Message + "</br>");
return "";
}
}
I asked this question before but I'm going to complete the question with a solution proposed and make another question.
I'm using this class to make an async WebRequest:
class HttpSocket
{
public static void MakeRequest(Uri uri, Action<RequestCallbackState> responseCallback)
{
WebRequest request = WebRequest.Create(uri);
request.Proxy = null;
Task<WebResponse> asyncTask = Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
ThreadPool.RegisterWaitForSingleObject((asyncTask as IAsyncResult).AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), request, 1000, true);
asyncTask.ContinueWith(task =>
{
WebResponse response = task.Result;
Stream responseStream = response.GetResponseStream();
responseCallback(new RequestCallbackState(response.GetResponseStream()));
responseStream.Close();
response.Close();
});
}
private static void TimeoutCallback(object state, bool timedOut)
{
Console.WriteLine("Timeout: " + timedOut);
if (timedOut)
{
Console.WriteLine("Timeout");
WebRequest request = (WebRequest)state;
if (state != null)
{
request.Abort();
}
}
}
}
And i'm testing the class with this code:
class Program
{
static void Main(string[] args)
{
// Making a request to a nonexistent domain.
HttpSocket.MakeRequest(new Uri("http://www.google.comhklhlñ"), callbackState =>
{
if (callbackState.Exception != null)
throw callbackState.Exception;
Console.WriteLine(GetResponseText(callbackState.ResponseStream));
});
Thread.Sleep(100000);
}
public static string GetResponseText(Stream responseStream)
{
using (var reader = new StreamReader(responseStream))
{
return reader.ReadToEnd();
}
}
}
Once executed, the callback is reached immediately, showing "Timeout: false" and there aren't more throws, so the timeout isn't working.
This is a solution proposed in the original thread but, as you could see, the code works for him.
What I'm doing wrong?
EDIT: Other classes used by the code:
class RequestCallbackState
{
public Stream ResponseStream { get; private set; }
public Exception Exception { get; private set; }
public RequestCallbackState(Stream responseStream)
{
ResponseStream = responseStream;
}
public RequestCallbackState(Exception exception)
{
Exception = exception;
}
}
class RequestState
{
public byte[] RequestBytes { get; set; }
public WebRequest Request { get; set; }
public Action<RequestCallbackState> ResponseCallback { get; set; }
}
This approach works. I would recommend switching this to explicitly handle exceptions (including your timeout, but also bad domain names, etc) slightly differently. In this case, I've split this into a separate continuation.
In addition, in order to make this very explicit, I've shorted the timeout time, put a "real" but slow domain in, as well as added an explicit timeout state you can see:
using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
class HttpSocket
{
private const int TimeoutLength = 100;
public static void MakeRequest(Uri uri, Action<RequestCallbackState> responseCallback)
{
WebRequest request = WebRequest.Create(uri);
request.Proxy = null;
Task<WebResponse> asyncTask = Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
ThreadPool.RegisterWaitForSingleObject((asyncTask as IAsyncResult).AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), request, TimeoutLength, true);
asyncTask.ContinueWith(task =>
{
WebResponse response = task.Result;
Stream responseStream = response.GetResponseStream();
responseCallback(new RequestCallbackState(response.GetResponseStream()));
responseStream.Close();
response.Close();
}, TaskContinuationOptions.NotOnFaulted);
// Handle errors
asyncTask.ContinueWith(task =>
{
var exception = task.Exception;
var webException = exception.InnerException;
// Track whether you cancelled or not... up to you...
responseCallback(new RequestCallbackState(exception.InnerException, webException.Message.Contains("The request was canceled.")));
}, TaskContinuationOptions.OnlyOnFaulted);
}
private static void TimeoutCallback(object state, bool timedOut)
{
Console.WriteLine("Timeout: " + timedOut);
if (timedOut)
{
Console.WriteLine("Timeout");
WebRequest request = (WebRequest)state;
if (state != null)
{
request.Abort();
}
}
}
}
class RequestCallbackState
{
public Stream ResponseStream { get; private set; }
public Exception Exception { get; private set; }
public bool RequestTimedOut { get; private set; }
public RequestCallbackState(Stream responseStream)
{
ResponseStream = responseStream;
}
public RequestCallbackState(Exception exception, bool timedOut = false)
{
Exception = exception;
RequestTimedOut = timedOut;
}
}
class RequestState
{
public byte[] RequestBytes { get; set; }
public WebRequest Request { get; set; }
public Action<RequestCallbackState> ResponseCallback { get; set; }
}
class Program
{
static void Main(string[] args)
{
// Making a request to a nonexistent domain.
HttpSocket.MakeRequest(new Uri("http://www.tanzaniatouristboard.com/"), callbackState =>
{
if (callbackState.RequestTimedOut)
{
Console.WriteLine("Timed out!");
}
else if (callbackState.Exception != null)
throw callbackState.Exception;
else
Console.WriteLine(GetResponseText(callbackState.ResponseStream));
});
Thread.Sleep(100000);
}
public static string GetResponseText(Stream responseStream)
{
using (var reader = new StreamReader(responseStream))
{
return reader.ReadToEnd();
}
}
}
This will run, and show a timeout appropriately.
Use 2 different classes:
class RequestCallbackException : Exception
{
public RequestCallbackException(Stream responseStream, Exception exception) : base(exception)
{
}
}
and
class RequestCallbackStream
{
public Stream ResponseStream { get; private set; }
public RequestCallbackState(Stream responseStream)
{
ResponseStream = responseStream;
}
}
You will notice that sometimes GetResponseStream() returns null, which immediately raise an exception in
asyncTask.ContinueWith() -->
GetResponseText(callbackState.ResponseStream)-->
using (var reader = new StreamReader(responseStream)) // responseStream is null
{
}