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.
Related
Background
Notification service has a Notify method which is invoked when an event occurs, so here Im creating the FloorActor and sending the message consisting data and post url to the Actor.
public class NotificationService
{
//HttpClient client = new HttpClient();
ActorSystem notificationSystem = ActorSystem.Create("NotificationSystem");
public void Notify(int clientID, FloorEventData data)
{
try
{
string postUrl = "http://localhost:6001";
FloorData floorData = new FloorData() { Data = data,PostURL=postUrl };
//This commented line of code post data and listener gets the POST request
//client.PostAsJsonAsync<FloorEventData>(postUrl, data);
//Create Floor Actor
var floorActor = notificationSystem.ActorOf<FloorActor>("floorActor");
floorActor.Tell(data);
}
catch (Exception exception)
{
//Log exception
}
}
}
ReceiveAsync method of the Floor Actor data just posts the event data to the specified URL.Framework used to implement Actor Model :Akka.Net
public class FloorActor : ReceiveActor
{
HttpClient client = new HttpClient();
public FloorActor()
{
ReceiveAsync<FloorData>(floorActor => client.PostAsJsonAsync<FloorEventData>(floorActor.PostURL, floorActor.Data));
}
}
Issue-
When I debugged the issue the code flow works as expected:-
1) When event occurs Notify method is invoked
2) Notify method creates the Floor Actor and sends the data
3) Floor Actor's ReceiveAsync method is called and the line of code is executed without any errors or exceptions.But the POST listener, doesn't get the POST data request, so not sure what is happening ?
Tried POST data directly from the Notify method its works, the listener gets the POST request.You can see this code snippet commented above in the Notify method.
So when I try to POST data from my Actor's Receive method, the Http listener does not get the request and there is no errors or exception.
Please let me know if I have to change anything?
ActorSystem should be treated as a singleton for your entire system. So put that instance in a static somewhere and reference it from there.
Also try to await on client.PostAsJsonAsync
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 !
Written or started to write a WEB API rest service in WCF. It's all going relatively well. However, I've come across a small problem. I've implemented this;
http://blogs.msdn.com/b/rjacobs/archive/2010/06/14/how-to-do-api-key-verification-for-rest-services-in-net-4.aspx
For key validation. (I'm not sure if this is the correct approach for WCF WEB API, since it looks more like the rest service implementation).
Anyway, it seems to work. However, when the api key is not provided the exception is not been displayed in the browser. I.e. if I provide the key, it returns correctly, if I don't it just shows a blank page.
private static void CreateErrorReply(OperationContext operationContext, string key)
{
// The error message is padded so that IE shows the response by default
using (var sr = new StringReader("<?xml version=\"1.0\" encoding=\"utf-8\"?>" + APIErrorHTML))
{
XElement response = XElement.Load(sr);
using (Message reply = Message.CreateMessage(MessageVersion.None, null, response))
{
HttpResponseMessageProperty responseProp = new HttpResponseMessageProperty() { StatusCode = HttpStatusCode.Unauthorized, StatusDescription = String.Format("'{0}' is an invalid API key", key) };
responseProp.Headers[HttpResponseHeader.ContentType] = "text/html";
reply.Properties[HttpResponseMessageProperty.Name] = responseProp;
operationContext.RequestContext.Reply(reply);
// set the request context to null to terminate processing of this request
operationContext.RequestContext = null;
}
}
}
Instead of this showing an error, the result is a blank response. Can anyone help?
I have a WCF web service which throws exceptions when invalid data is submitted. The data is submitted via an HTTP Post using the WebClient object.
Here is the code for the web service:
[WebInvoke(UriTemplate = "update", Method = "POST")]
public JsonValue Update(HttpRequestMessage message)
{
var context = new Entities();
dynamic response = new JsonObject();
// in order to retrieve the submitted data easily, reference the data as a dynamic object
dynamic data = message.Content.ReadAs(typeof(JsonObject), new[] { new FormUrlEncodedMediaTypeFormatter() });
// retrieve the submitted data
int requestId = data.requestId;
int statusId = data.statusId;
string user = data.user;
string encryptedToken = data.token;
string notes = data.notes;
// retrieve the request with a matching Id
var request = context.Requests.Find(requestId);
// make sure the request exists
if (request == null)
throw new FaultException("The supplied requestId does not exist.");
// make sure the submitted encrypted token is valid
var token = DecryptToken(encryptedToken);
if (token == null)
throw new FaultException("Invalid security token.");
// TODO: Validate other token properties (e.g. email)?
if (!request.User.UserName.Equals(token.UserName))
throw new FaultException("Invalid security token.");
// additional logic removed ...
}
And here is the code that submits data to the web service:
// use the WebClient object to submit data to the WCF web service
using (var client = new WebClient())
{
client.Encoding = Encoding.UTF8;
// the data will be submitted in the format of a form submission
client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
var data = new NameValueCollection();
// prepare the data to be submitted
data.Add("requestId", requestId.ToString());
data.Add("statusId", this.StatusId);
data.Add("token", token.ToString());
data.Add("user", this.User);
data.Add("notes", this.Notes);
// submit the data to the web service
var response = client.UploadValues(this.Address, data);
}
I keep getting an exception with message: "The remote server returned an error: (500) Internal Server Error" at client.UploadValues(this.Address, data);.
Is there a way I can make sure that more detailed information is returned to the WebClient?
Also, how can I make sure that these exceptions (in the WCF service) are logged to the EventLog? (Basically I just need to know what happened).
Take a look at HttpResponseException (namespace Microsoft.ApplicationServer.Http.Dispatcher) - they're the way where you can control the response for error cases. You can specify the status code, and you have control over the HttpResponseMessage, in which you can control the message body.
On the client side, when you call WebClient.UploadValues, wrap that call and catch a WebException. If the service returns a response with a non-successful status code (e.g., 500, 400), the Response property of the WebException will have the body, in which you can read in your client.
Another option is to use HttpClient instead of the WebClient, in which case you can simply look at the HttpResponseMessage directly.
I'm using the HTTP Web Request class to call a RESTful web service. I need to pass data and receive data and it all seems to work very well. Today I attempted to configure the time-out of the class because there is a high likelihood of the server running the service being offline and I don't want to waste time waiting. I configured it all but it seemed to make no difference. The call still waited over 10 seconds before failing.
On looking into it I found that the time-out only deals with the processing of the call and that the DNS lookup beforehand is not included. As this would be the problem it would make sense as to why the time-out wasn't working as I'd expected.
Further reading suggested using the HttpWebRequest class in an asynchronous style instead. I've had a look at the code to do so but don't understand how to retrieve the callback in my code which is effectively synchronous.
The code I have as follows is as so:
HttpWebRequest _serviceRequest = (HttpWebRequest)WebRequest.Create(new Uri("http://mywebservice.com"));
_serviceRequest.Timeout = 3000;
HttpWebResponse response = (HttpWebResponse)_serviceRequest.GetResponse();
XmlReader reader = XmlReader.Create(response.GetResponseStream(), set);
The code I have to call asynchronously ends with the following line, but I'm not sure as to what I should do to get the response object.
IAsyncResult result = (IAsyncResult)req.BeginGetResponse(new AsyncCallback(RespCallback), reqState);
I'm also concerned about a half baked asynchronous solution such as this. Is it good practice to use an asynchronous method through a synchronous piece of code.
Any helpers appreciated...
The response would be available when the callback function RespCallback is invoked. I don't know what reqState has, but I assume it contains a reference to the original HttpWebRequest object. If this is the case, this would be a simple implementation of the RespCallback method:
void RespCallback(IAsyncResult asyncResult)
{
ReqState reqState = (ReqState)asyncResult.AsyncState;
HttpWebResponse resp = (HttpWebResponse)reqState.Request.EndGetResponse(asyncResult);
// do what you need with the response.
}
Update: more info as asked in the comment
If you want the response in the same method where you did the Begin call, you can have an event which will be set when the callback is received, and you can wait on that event after the Begin call, like in the example below
class ReqState {
public HttpWebRequest Request { get; set; }
public HttpWebResponse Response { get; set; }
public AutoResetEvent Evt { get; set; }
}
void RespCallback(IAsyncResult asyncResult) {
ReqState reqState = (ReqState)asyncResult.AsyncState;
reqState.Response = (HttpWebResponse)reqState.Request.EndGetResponse(asyncResult);
reqState.Evt.Set();
}
void CallMethod() {
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(...);
// set properties on req
ReqState state = new ReqState();
state.Request = req;
state.Evt = new ManualResetEvent(false);
req.BeginGetResponse(RespCallback, state);
state.Evt.WaitOne(TimeSpan.FromSeconds(30)); // wait for 30 seconds
// access response via state.Response
}
Now notice that you're essentially doing a synchronous call in an asynchronous way. That gives you more control over the timeout, but with the price of code complexity. Another thing, this will not work on platforms such as Silverlight (and Windows Phone, IIRC), where synchronous calls (even those dressed up as asynchronous) are forbidden.