The issue described below doesn't happen on local development, only once I have deployed everything to test environment, which is hosted on Azure Web App.
When calling an API endpoint from Angular (where I use Observer and subscribe on the request), the request is processed and may sometimes take longer than 2 minutes to be processed. When the request hits 2 minutes, I instantly get a 502 bad gateway, while the async process continues until finished.
In the request processing I do a couple of remote powershell script executions, I made sure that both my powershell methods run asynchronously, and made sure that the API endpoint is async. In my angular frontend from where I do the request, I use observer and subscribe on the request being made.
My next step was wanting to make some sort of request that just starts the process, and then somehow be able to keep requesting until I get a response that the response has finished, to avoid the 502 error, but I don't know where or how to start, but I don't know if this approach could even work for sure.
Backend is set up using C#, .NET Core 2.2. Frontend is Angular (7?).
I want to get the expected response instead of hitting 502 Bad Gateway everytime processing hits 2 minutes.
Code:
First I call
this.objectService.processObjectsForMerge(request).subscribe(res => {});
Which, from the service, prepares following request
processObjectsForMerge(req: ProcessObjectsForMergeRequest): Observable<ProcessObjectsForMergeResponse>{
return this.http.post<ProcessObjectsForMergeResponse>(this.API_URL + '/Object/ProcessObjectsForMerge',req, {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.auth.getAccessToken()
})
});
}
And is received in the following endpoint
[HttpPost]
[Route("ProcessObjectsForMerge/")]
public async Task<IActionResult> ProcessObjectsForMerge([FromBody] ProcessMergeRequestModel request)
{
if (request == null || string.IsNullOrEmpty(request.VersionNumber) || string.IsNullOrEmpty(request.CuLevel)) return BadRequest("Failed to validate data");
var permissionGranted = DoesUserHavePermissionForRequest(request.ShortGuid);
if (!permissionGranted) return BadRequest();
var isSuccessful = await new ProcessObjectsForMergeService().Process(request);
var message = !isSuccessful ? "Failed to process objects for merge." : "Successfully processed the objects for merge.";
return Ok(new {message, log});
}
Update:
I have found a temporary solution, in web.config of the API I was able to increase the timeout time.
I feel this is bad practice though, so gonna implement a message queue (using rabbitMQ probably) to execute my initial idea on how to solve it and see if it works out the way I expect.
Related
I have an asp.net mvc async method waiting for 10 second.
The problem is IIS Express (production IIS also) stops processing incoming requests until that async method finishes.
I thought await keyword frees current thread for new incoming request. But it seems that I missed something.
public async Task<ActionResult> AsyncMethod()
{
using (DeliveryPortalEntities context = new DeliveryPortalEntities())
{
await context.Database.ExecuteSqlCommandAsync("WAITFOR DELAY '00:00:10'");
}
return Json(new { status = "ok" });
}
Update! Can anyone explain why during this code execution IIS stops answer requests immediately.
public async Task<ActionResult> AsyncMethod()
{
await Task.Delay(10000).ConfigureAwait(false);
return new EmptyResult();
}
I thought await keyword frees current thread for new incoming request.
It does; the thread is free. However, the request is not complete. And if that request has a read/write lock on the session, then no other requests for that session can be processed until that lock is free.
To resolve this, you'll need to change your session usage. If you can go sessionless, that's best - it allows really good horizontal scalability. If not, try to go read-only. Long requests with a read-only session lock can run concurrently with other requests in that same session if they also only need a read-only session lock.
I have a XML-RPC server (using XML-RPC.net) running as a .NET console application. I'm trying to connect to it via my ASP.NET Core (2.1.1) web app but the client keeps timing out. Postman also returns a response immediately without issues.
Here is how I'm calling it:
HttpClient client = _clientFactory.CreateClient();
client.Timeout = TimeSpan.FromSeconds(10);
var httpRequest = new HttpRequestMessage(HttpMethod.Post, instance.ServiceUrl);
var stringContent = new ByteArrayContent(Encoding.UTF8.GetBytes(request.ToString()));
httpRequest.Content = stringContent;
httpRequest.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("text/xml");
var httpResponse = await client.SendAsync(httpRequest);
var response = await httpResponse.Content.ReadAsByteArrayAsync();
I can see that the request was made successfully as the console app returns a response. Fiddler shows there was a 200 response but await client.SendAsync(httpRequest); times-out!
The request usually completes in under 10ms so the timeout value is just for debugging, if I leave it out it would take 60s. The response returns XML.
I've tried rewriting this to use StringContent and using PostAsync, same issue. I also attempted to rewrite this using WebClient but it returned The remote server returned an error: (100) Continue. not sure if that's relevant.
Been stuck on this for a whie, anyone know what could be happening?
OK I did some googling and it looks like I needed this line:
client.DefaultRequestHeaders.ExpectContinue = true;
It was definitely related to 100 status code returned not being handled properly.
Found it here:
https://social.msdn.microsoft.com/Forums/en-US/042016f0-d70e-42f9-9924-5febeb2bea86/excluding-the-quotexpect-100continuequot-header-from-httpwebrequest-posts?forum=winappswithcsharp
So basically, the client calls the server using a Meteor.call. The server method then does some validations and calls a web service using a meteor package. If validation fails and a meteor error is thrown, it reaches the server. If the package response has an error, it only logs on the server. I need the error to reach the client.
Here's how the code looks like.
Client
Meteor.call('callService', (err, result) => {
if(err) {
console.log(err.reason);
}
});
Server
Meteor.methods({
'callService'(){
if (!Meteor.user()) {
// Error 1
throw new Meteor.Error('insufficient-permissions', 'You need to login first');
}
// Using an meteor package to actually call the service
package.callService(apiKey, (err, response) => {
if (response.status === 'error') {
// Error 2
throw new Meteor.Error('service-error', response.message);
}
});
},
});
In the server method, if an error is thrown at Error 1, it does reach the client, but Error 2 does not. Error 2 only logs on the server.
I guess your package.callService() is async (given that it accepts a callback).
In that case, your Meteor method starts the async task, then continues its process and returns (since there is no more instructions), while the async task is still running (actually waiting for a response from your remote web service). Therefore your client Meteor call's callback receives a "no error" response.
Once your "Error 2" happens, the Meteor call is already completed, and the error can only be logged on the server.
If you want to "hang up" your method so that it waits for the result of your package.callService() to determine whether it is a success or an error and complete the Meteor call accordingly, you could try using Meteor.wrapAsync().
By the way, if you do use synchronous task to actually wait for a remote service, you would be interested in this.unblock() to allow your server to process other tasks (methods) instead of just idling.
Is there something special I need to define in an ASP.NET MVC application to read an incoming response from a ASP.NET Web API?
From my MVC app, I make a request to an ASP.NET Web API using System.Net.HttpClient. The API receives the request and processes it fine and returns a valid response. However, the MVC application, it appears, never gets the response. I have a break point on the line that makes the request. The flow of control never comes back after executing that line. The MVC app just keeps waiting and times-out after a very long time.
However, I can confirm that the API returns a valid Json response. I have tried composing this request in Chrome Postman and see that the API returns a valid response.
Here's the code from my MVC app that makes the request to the Web API:
public async Task<R> PostAsJsonAsync<T, R>(string uri, T value)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(_baseUri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.PostAsJsonAsync(uri, value);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsAsync<R>();
else return default(R);
}
}
In the past, i.e. before Web API 2, I've had MVC apps talk to the Web API without any problem. I don't know if I am missing something that has been introduced in Web API 2.
I have a feeling you are getting a deadlock. Are you using .Result anywhere? You should be using async all the way. I mean your MVC action method should also be async method and they should await and not use .Result. Read this log post by Stephen Cleary for more info. http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
I am trying to call an external REST service from angular using $http service.
The thing is that I am stuck on the $http.get method, because everytime I call the rest service i get an error with status = 0 and no information in the data parameter of the error callback.So far I've tried calling a local service runnig on port 5000 : $http.get('http://localhost:5000/ping') and this is supposed to return a json object with a property and a value. Another approach was calling http://api.flickr.com/services/rest/?method=flickr.test.echo&name=test in the hope of getting an answer. For both of them I get the same error: that I mentioned earlier.The call is made from an angular controller that has injected the http service.Thanks.
Have you tried:
$http({method: 'GET', url: 'someURL'}).
success(function(data, status, headers, config) {
//set view model or do something.
}).
error(function(data, status, headers, config) {
});
Make sure that you have passed the parameters correctly if there are any.
The general syntax should be like the following :
$http.get('../link/yourApplication/searchBySomeNumber?someNum='+$scope.someNum+'&asOfDate='+asOfDate+'&status=undefined')
.success(function(data, status, headers, config) {
//your code
console.log('Data return successful');
})
.error(function(data, status, headers, config) {
$scope.status = status;
alert('Info Error');
console.log('Group Info Error');
});
As $http returns a Promise, you can use the .then() method to log your results when the promise is resolved, or log an error in case anything goes wrong:
$http.get('http://localhost:5000/ping')
.then(function(returnedJson) {
console.log(returnedJson.data);
})
.catch(console.error) // or $log.error if you are using $log from Angular
Please note that the clean JSON response is obtained by logging the .data property of the returnedJson object. As it is a Promise, it contains other information that are not relevant to consume the web service.
Also note that the web service you want to consume should also be in the same domain as your Angular app, otherwise you may incur into a Cross Domain error, unless the service allows usage from external websites by exposing a Cross Domain Policy.
(Find more info here: Can someone post a well formed crossdomain.xml sample?)
If that's the case, this post should be helpful:
jQuery AJAX cross domain
Hope this helps.