I'm quite new to the Windows Phone dev and I have to do an application to communicate with a Restful API. Everything works fine to get the informations back from the API but my problem occurs when I try to update the content. For example, I have a profile and I try to update the user's information (change the city let's say). On the server side I can see that my update worked properly but when I go back to my profile in my WP app nothing has changed, the city is still the same as the old one. This is my code :
public MainPage()
{
InitializeComponent();
this.ApplicationBar = this.Resources["HomeBar"] as ApplicationBar;
Requester requester = new Requester();
requester.initGetRequest("/me/", GetResponseCallback, true);
}
private void GetResponseCallback(IAsyncResult asynchronousResult)
{
try
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
Stream streamResponse = response.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
string read = streamRead.ReadToEnd();
GlobalData.GetInstance().user = JsonConvert.DeserializeObject<MeClass>(read);
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(read);
});
//Create the profile and stuff
streamResponse.Close();
streamRead.Close();
response.Close();
}
catch (WebException webException)
{
HttpStatusCode status = ((HttpWebResponse)webException.Response).StatusCode;
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(status.ToString());
});
}
}
I figured out that the string 'read' is always equal to the old one, even after the update so this is why the content is not updated but how can the response be exactly the same as before, even if the update worked fine on the server side (if I check in Postman after my update, I can see that my city is the new one). If I restart my app I can see the update.
I can also show you my initGetRequest() :
public void initGetRequest(String endPoint, Action<IAsyncResult> callback, Boolean header)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url + endPoint);
if (header == true)
request.Headers["Authorization"] = GlobalData.GetInstance().Header;
request.BeginGetResponse(new AsyncCallback(callback), request);
}
Thank you for your help !
I finally found why my request was still the same even after the update. The HttpWebRequest uses a cache by default. I only added a small bit of code before calling my request :
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url + endPoint);
if (header == true)
request.Headers["Authorization"] = GlobalData.GetInstance().Header;
request.Headers[HttpRequestHeader.IfModifiedSince] = DateTime.UtcNow.ToString();
request.BeginGetResponse(new AsyncCallback(callback), request);
I had no idea about that cache so I hope this answer will help someone having the same issue !
Related
I am in a situation where when I get an HTTP 400 code from the server, it is a completely legal way of the server telling me what was wrong with my request (using a message in the HTTP response content)
However, the .NET HttpWebRequest raises an exception when the status code is 400.
How do I handle this? For me a 400 is completely legal, and rather helpful. The HTTP content has some important information but the exception throws me off my path.
It would be nice if there were some way of turning off "throw on non-success code" but if you catch WebException you can at least use the response:
using System;
using System.IO;
using System.Web;
using System.Net;
public class Test
{
static void Main()
{
WebRequest request = WebRequest.Create("http://csharpindepth.com/asd");
try
{
using (WebResponse response = request.GetResponse())
{
Console.WriteLine("Won't get here");
}
}
catch (WebException e)
{
using (WebResponse response = e.Response)
{
HttpWebResponse httpResponse = (HttpWebResponse) response;
Console.WriteLine("Error code: {0}", httpResponse.StatusCode);
using (Stream data = response.GetResponseStream())
using (var reader = new StreamReader(data))
{
string text = reader.ReadToEnd();
Console.WriteLine(text);
}
}
}
}
}
You might like to encapsulate the "get me a response even if it's not a success code" bit in a separate method. (I'd suggest you still throw if there isn't a response, e.g. if you couldn't connect.)
If the error response may be large (which is unusual) you may want to tweak HttpWebRequest.DefaultMaximumErrorResponseLength to make sure you get the whole error.
I know this has already been answered a long time ago, but I made an extension method to hopefully help other people that come to this question.
Code:
public static class WebRequestExtensions
{
public static WebResponse GetResponseWithoutException(this WebRequest request)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
try
{
return request.GetResponse();
}
catch (WebException e)
{
if (e.Response == null)
{
throw;
}
return e.Response;
}
}
}
Usage:
var request = (HttpWebRequest)WebRequest.CreateHttp("http://invalidurl.com");
//... (initialize more fields)
using (var response = (HttpWebResponse)request.GetResponseWithoutException())
{
Console.WriteLine("I got Http Status Code: {0}", response.StatusCode);
}
Interestingly, the HttpWebResponse.GetResponseStream() that you get from the WebException.Response is not the same as the response stream that you would have received from server. In our environment, we're losing actual server responses when a 400 HTTP status code is returned back to the client using the HttpWebRequest/HttpWebResponse objects. From what we've seen, the response stream associated with the WebException's HttpWebResponse is generated at the client and does not include any of the response body from the server. Very frustrating, as we want to message back to the client the reason for the bad request.
I had similar issues when trying to connect to Google's OAuth2 service.
I ended up writing the POST manually, not using WebRequest, like this:
TcpClient client = new TcpClient("accounts.google.com", 443);
Stream netStream = client.GetStream();
SslStream sslStream = new SslStream(netStream);
sslStream.AuthenticateAsClient("accounts.google.com");
{
byte[] contentAsBytes = Encoding.ASCII.GetBytes(content.ToString());
StringBuilder msg = new StringBuilder();
msg.AppendLine("POST /o/oauth2/token HTTP/1.1");
msg.AppendLine("Host: accounts.google.com");
msg.AppendLine("Content-Type: application/x-www-form-urlencoded");
msg.AppendLine("Content-Length: " + contentAsBytes.Length.ToString());
msg.AppendLine("");
Debug.WriteLine("Request");
Debug.WriteLine(msg.ToString());
Debug.WriteLine(content.ToString());
byte[] headerAsBytes = Encoding.ASCII.GetBytes(msg.ToString());
sslStream.Write(headerAsBytes);
sslStream.Write(contentAsBytes);
}
Debug.WriteLine("Response");
StreamReader reader = new StreamReader(sslStream);
while (true)
{ // Print the response line by line to the debug stream for inspection.
string line = reader.ReadLine();
if (line == null) break;
Debug.WriteLine(line);
}
The response that gets written to the response stream contains the specific error text that you're after.
In particular, my problem was that I was putting endlines between url-encoded data pieces. When I took them out, everything worked. You might be able to use a similar technique to connect to your service and read the actual response error text.
Try this (it's VB-Code :-):
Try
Catch exp As WebException
Dim sResponse As String = New StreamReader(exp.Response.GetResponseStream()).ReadToEnd
End Try
An asynchronous version of extension function:
public static async Task<WebResponse> GetResponseAsyncNoEx(this WebRequest request)
{
try
{
return await request.GetResponseAsync();
}
catch(WebException ex)
{
return ex.Response;
}
}
This solved it for me:
https://gist.github.com/beccasaurus/929007/a8f820b153a1cfdee3d06a9c0a1d7ebfced8bb77
TL;DR:
Problem:
localhost returns expected content, remote IP alters 400 content to "Bad Request"
Solution:
Adding <httpErrors existingResponse="PassThrough"></httpErrors> to web.config/configuration/system.webServer solved this for me; now all servers (local & remote) return the exact same content (generated by me) regardless of the IP address and/or HTTP code I return.
I have a question about this create/Update leads API, http://developers.marketo.com/documentation/rest/createupdate-leads/.
There is no sample code for C# or JAVA. Only ruby available. So I have to try it by myself. But I always get null return from the response.
Here is my code:
private async Task<CreateLeadResponseResult> CreateLead(string token)
{
string url = String.Format(marketoInstanceAddress+"/rest/v1/leads.json?access_token={0}", token);
var fullUri = new Uri(url, UriKind.Absolute);
CreateLeadResponseResult createLeadResponse = new CreateLeadResponseResult();
CreateLeadInput input = new CreateLeadInput { email = "123#123.com", lastName = "Lee", firstName = "testtesttest", postCode = "00000" };
CreateLeadInput input2 = new CreateLeadInput { email = "321#gagaga.com", lastName = "Lio", firstName = "ttttttt", postCode = "00000" };
List<CreateLeadInput> inputList = new List<CreateLeadInput>();
inputList.Add(input);
inputList.Add(input2);
CreateLeadRequest createLeadRequest = new CreateLeadRequest() { input = inputList };
JavaScriptSerializer createJsonString = new JavaScriptSerializer();
string inputJsonString = createJsonString.Serialize(createLeadRequest);
using (var client = new HttpClient())
{
HttpResponseMessage response = await client.PostAsJsonAsync(fullUri.OriginalString, inputJsonString).ConfigureAwait(false);
// I can see the JSON string is in the message body in debugging mode.
if (response.IsSuccessStatusCode)
{
createLeadResponse = await response.Content.ReadAsAsync<CreateLeadResponseResult>();
}
else
{
if (response.StatusCode == HttpStatusCode.Forbidden)
throw new AuthenticationException("Invalid username/password combination.");
else
throw new ApplicationException("Not able to get token");
}
}
return createLeadResponse;}
//get null here.
Thank you.
-C.
The best way to debug this is to capture the exact URL, parameters and JSON that are submitted by your app and try submitting those manually via a tool like Postman (Chrome plug-in) or SOAP UI. Then you see the exact error message, which you can look up here: http://developers.marketo.com/documentation/rest/error-codes/. Based on that you can update your code. I don't know much about Java, but this is how I got my Python code to work.
Your example code was really helpful in getting my own implementation off the ground. Thanks!
After playing with it for a bit, I realized that the JavaScriptSerializer step is unnecessary since PostAsJsonAsync automatically serializes whatever object you pass to it. The double serialization prevents Marketo's API from processing the input.
Also, I agree with Jep that Postman is super helpful. But in the case of this error, Postman was working fine (using the contents of inputJsonString) but my C# code still didn't work properly. So I temporarily modified the code to return a dynamic object instead of a CreateLeadResponseResult. In debugging mode this allowed me to see fields that were discarded because they didn't fit the CreateLeadResponseResult type, which led me to the solution above.
I have been using service stack via AJAX calls for some time without issue, but have recently created a quick winforms app which utilizes the service stack client (specifically JsonServiceClient).
However - I have hit a problem whereby I consistently get a timeout on a call which works successfully on the the first TWO attempts. It looks like either the service stack client is holding on to some resource, or I am using the client in the wrong way. It only occurs when running against a remote service (works every time on a local machine). Here is my code, and the exception:
var url = "http://www.TestServer.com/api";
var taskId = Guid.Parse("30fed418-214b-e411-80c1-22000a5b9fe5");
var email = "admin#example.com";
using (var client = new JsonServiceClient(url))
{
var result = client.Send(new Authenticate {UserName = "username", Password = "Password01", RememberMe = true});
client.Put(new AssignTask { AdminTaskId = taskId, Assignee = email });//Call #1 - works fine
client.Put(new AssignTask { AdminTaskId = taskId, Assignee = email });//Call #2 - works fine
try
{
client.Put(new AssignTask { AdminTaskId = taskId, Assignee = email });//Call #3 - works fine
}
catch (WebException ex)
{
//Times out every time
//at System.Net.HttpWebRequest.GetRequestStream(TransportContext& context)
//at System.Net.HttpWebRequest.GetRequestStream()
//at ServiceStack.Net40PclExport.GetRequestStream(WebRequest webRequest)
//at ServiceStack.ServiceClientBase.<>c__DisplayClassa.<SendRequest>b__9(HttpWebRequest client)
//at ServiceStack.ServiceClientBase.PrepareWebRequest(String httpMethod, String requestUri, Object request, Action`1 sendRequestAction)
//at ServiceStack.ServiceClientBase.SendRequest(String httpMethod, String requestUri, Object request)
//at ServiceStack.ServiceClientBase.Send[TResponse](String httpMethod, String relativeOrAbsoluteUrl, Object request)
//at ServiceStack.ServiceClientBase.Put[TResponse](String relativeOrAbsoluteUrl, Object requestDto)
//at ServiceStack.ServiceClientBase.Put(Object requestDto)
//at SSClientIssue.Program.Main(String[] args) in c:\Users\David\Documents\Visual Studio 2013\Projects\SSClientIssue\SSClientIssue\Program.cs:line 27
throw;
}
}
After the timeout, I can close and reload the app (server stays up), and then get same behavior again (two successful calls). IIS logs show that the 3rd call does not make it to the server, so looks like a Client issue.
I have been looking at this for 8 hours and I think my eyes are starting to bleed...If anyone can help I will buy you a beer!
The issue is due to your ServiceClient requests not specifying a known response type.
Response types can either be marked on the Request DTO using the IReturn<T> marker (recommended):
public class GetAllAdminUsernamesRequest : IReturn<List<string>> { ... }
By adding this on the Request DTO, the ServiceClient is able to automatically infer and convert the response, e.g:
List<string> response = client.Get(new GetCurrentAdminUserAdminTasks());
Otherwise an alternative to specifying the Response on the Request DTO, is to specify it on the call-site, e.g:
List<string> response = client.Get<List<string>>(new GetCurrentAdminUserAdminTasks());
If you don't do this the Response is unknown so the ServiceClient will just return the underlying HttpWebResponse so you can inspect the response yourself.
HttpWebResponse tasks = client.Get(new GetCurrentAdminUserAdminTasks());
In order to be able to inspect and read from the HttpWebResponse the response cannot be disposed by the ServiceClient, so it's up to the call-site making the request to properly dispose of it, i.e:
using (HttpWebResponse tasks = client.Get(new GetCurrentAdminUserAdminTasks())) {}
using (HttpWebResponse adminUsers = client.Get(new GetAllAdminUsernames())) {}
try
{
using (client.Put(new AssignTask { AdminTaskId = taskId, Assignee = user })) {}
using (client.Put(new AssignTask { AdminTaskId = taskId, Assignee = user })) {}
using (client.Put(new AssignTask { AdminTaskId = taskId, Assignee = user })) {}
using (client.Put(new AssignTask { AdminTaskId = taskId, Assignee = user })) {}
}
...
Disposing of your WebResponses responses will resolve your issue.
If you don't do this the underlying WebRequest will throttle open connections and only let a limited number of simultaneous connections through at any one time, possibly as a safe-guard to prevent DDOS attacks. This is what keeps the underlying connections open and WebRequest to block, waiting for them to be released.
I have two sites: one of them controls the other sending some commands through Web API. The idea is: the action of the controller site sends a command to the other site, gets the response and perform some business rules, without redirecting to the other site.
I have tons of examples explaining how to implement this via jQuery, but I want to make the controller post the data to the other site, instead of the view.
I found an approach at this answer: How to use System.Net.HttpClient to post a complex type?, but I want the answer for an JSON approach.
Can someone post a simple example using JSON showing how to do this?
As I didn't find a brief answer to my question, I'll post the solution I've made.
As the method uses an HttpClient method that requires async statements, the action below was implemented retuning a Task<ActionResult>. Another modification is if you're saving an object in context.
Instead of using:
context.SaveChanges();
You'll have to use:
await context.SaveChangesAsync();
The code below implements an Action from an ASP.NET MVC4 Controller:
[HttpPost]
public async Task<ActionResult> Create(MyModel model)
{
if (ModelState.IsValid)
{
// Logic to save the model.
// I usually reload saved data using something kind of the statement below:
var inserted = context.MyModels
.AsNoTracking()
.Where(m => m.SomeCondition == someVariable)
.SingleOrDefault();
// Send Command.
// APIMyModel is a simple class with public properties.
var apiModel = new APIMyModel();
apiModel.AProperty = inserted.AProperty;
apiModel.AnotherProperty = inserted.AnotherProperty;
DataContractJsonSerializer jsonSer = new DataContractJsonSerializer(typeof(APIMyModel));
// use the serializer to write the object to a MemoryStream
MemoryStream ms = new MemoryStream();
jsonSer.WriteObject(ms, apiModel);
ms.Position = 0;
//use a Stream reader to construct the StringContent (Json)
StreamReader sr = new StreamReader(ms);
// Note if the JSON is simple enough you could ignore the 5 lines above that do the serialization and construct it yourself
// then pass it as the first argument to the StringContent constructor
StringContent theContent = new StringContent(sr.ReadToEnd(), System.Text.Encoding.UTF8, "application/json");
HttpClient aClient = new HttpClient();
Uri theUri = new Uri("http://yoursite/api/TheAPIAction");
HttpResponseMessage aResponse = await aClient.PostAsync(theUri, theContent);
if (aResponse.IsSuccessStatusCode)
{
// Success Logic. Yay!
}
else
{
// show the response status code
String failureMsg = "HTTP Status: " + aResponse.StatusCode.ToString() + " - Reason: " + aResponse.ReasonPhrase;
}
return RedirectToAction("Index");
}
// if Model is not valid, you can put your logic to reload ViewBag properties here.
}
i am developing an windows phone 8 app , in my app i am calling services and downloading some data into my app.
i am testing my app in windows phone 8 emulator, many times it throws this error :
"The remote server returned an error: NotFound."
This is how i have created and used my httpwebrequest :-
public async Task<string> ServiceRequest(string serviceurl, string request, string methodname)
{
string response = "";
try
{
var httpwebrequest = WebRequest.Create(new Uri(serviceurl)) as HttpWebRequest;
httpwebrequest.Method = "POST";
httpwebrequest.Headers["SOAPAction"] = "http://tempuri.org/" + iTestservice + "/" + methodname + "";
httpwebrequest.ContentType = "text/xml";
byte[] data = Encoding.UTF8.GetBytes(request);
using (var requestStream = await Task<Stream>.Factory.FromAsync(httpwebrequest.BeginGetRequestStream, httpwebrequest.EndGetRequestStream, null))
{
await requestStream.WriteAsync(data, 0, data.Length);
}
var httpTask = httpRequest(httpwebrequest);
var completeTask = await Task.WhenAny(httpTask, TaskEx.Delay(300000));
if (completeTask == httpTask)
return await httpTask;
else
httpwebrequest.Abort();
throw new TimeoutException("Service Timed Out");
}
catch (TimeoutException Tex)
{
throw Tex;
}
catch (Exception ex)
{
throw ex;
}
}
public async Task<string> httpRequest(HttpWebRequest request)
{
string received;
using (var response = (HttpWebResponse)(await Task<WebResponse>.Factory.FromAsync(request.BeginGetResponse, request.EndGetResponse, null)))
{
using (var responseStream = response.GetResponseStream())
{
using (var sr = new StreamReader(responseStream))
{
received = await sr.ReadToEndAsync();
}
}
}
return received;
}
i am really not able to figure what was the issue
Please note :-
i tried to open the site (service URL which my app is trying to access) in my emulator browser , it opened correctly , i wasn't facing any issues.
1)is that the problem with my code, if so i request you please correct me ??
2)is this any emulator issue or any connectivity issue ??
3)is this any certification issue opening in emulator ??
Even after a long research to how to fix the issue i was not able to fix it .
Please Help me out.
Thanks in Advance.
I have come across this issue when the webservice requests take longer than 60 seconds. I have read reports that the emulator does not throw this error, but only an actual device. In my experience however, I have had this happen on both emulator and device. This is a default timeout setting that cannot be altered, even if you change it in the config. This issue has been around since WP7.
I've only found this question asked/blogged about once elsewhere. Seems that not a lot of people have picked this up.
AFAIK, Microsoft are aware of this, but don't plan on fixing it. (Proven by the fact that it has been around since WP7, and wasn't fixed for WP8). My guess is that they are of the opinion that web calls shouldn't be taking longer than 60 seconds in any case.
I just resolved it adding empty credentials to https requests, like this
httpwebrequest.Credentials = new NetworkCredential("", "");
here he explains why
http://blog.toetapz.com/2010/11/15/windows-phone-7-and-making-https-rest-api-calls-with-basic-authentication/