WebMethod performance slows - c#

I have a very basic JavaScript AJAX request using jQuery:
$.ajax({
type: "POST",
url: "TabbedSummaryPage.aspx/RunReport",
data: "{'itemId': '', 'lType': '', 'reportId': '', 'requestXml': ''}",
contentType: "application/json",
dataType: "json",
success: function (data, textStatus, jqXHR) {
},
error: function (jqXHR, textStatus, errorThrown) {
},
complete: function (jqXHR, textStatus) {
}
});
Which is calling and executing the following C# code:
[WebMethod]
public static RunReportResponse RunReport(string itemId, string lType, string reportId, string requestXml)
{
var result = new RunReportResponse();
return result;
}
public struct RunReportResponse
{
public string reportTitle;
public string reportError;
public string reportHtml;
public string reportStyles;
public bool showWordMenu;
}
public class Global : System.Web.HttpApplication
{
}
I've stripped everything down so nothing is really being executed.
If I make multiple concurrent AJAX requests something strange starts to happen.
The average time to get the first byte from the response is around 50ms if I make 11 requests but 5 of them always take between 2 and 3 seconds to respond with the first byte.
When I added some logging to find out what was going on it seems it is always the last set of requests, whether I send 6 requests and get 3 slow requests back, or I send 11 requests and get 5 slow requests back, it is always the last requests to be executed by ASP.NET which IIS provides a response to the client with an added 1000+ milliseconds.
We have a high performance app which relies on responding to these requests without a delay so it is imperative we can figure out what is causing this delay.
The same test results are found in Internet Explorer 11, Chrome and Firefox.
Requests which don't use the WebMethod attribute don't suffer this issue.
The following is the headers sent during a request:
POST /pharmadotnet/Pharma/TabbedSummaryPage.aspx/RunReport HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-GB,en;q=0.7,en-US;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: application/json; charset=UTF-8
X-Requested-With: XMLHttpReques
Referer: http://localhost/pharmadotnet/Pharma/tabbedsummarypage.aspx?lType=coInfo&itemId=1293&compId=co&reportingCurrency=&sceName=&showTabs=
Content-Length: 223
Cookie: sTab=tabs-1; ASP.NET_SessionId=r01d2eqjxu0vdnexbl5mmd3p; curSessionId=r01d2eqjxu0vdnexbl5mmd3p; NewSession=true; __AntiXsrfToken=ec03abb28ee14f50a18e34216aa59d85; .ASPXUSERDEMO=DF6CD4223C47DB289B82E0240DAB40AAF253BACE9A753863E77BA07F9CEE61D00235255A2BAA58F555ECF3166D8470E77654DB8C3E2594E54B5BAF38A5ACDCCA5FDED79ECD0B89DC3583B4F7E56911C15EE894365CA1444CF0A8D2AB8FEF19AA915CE3989F07DCFE6F4941DA69FBB38593BC51A9
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
The following is the response headers from one of the requests:
HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
Date: Tue, 08 Sep 2015 12:50:58 GMT
Content-Length: 174
The same issue is exhibited regardless of whether we are debugging or not.
IIS is not configured to cache the request.
The WebForms page is not configured to cache the request.
Is there any particular reason as to why this might be happening?

It turns out that the problem was session locking.
By creating a separate WebForm, setting EnableSessionState to false, and moving the web method to the web form that doesn't make use of sessions I was able to work around the issue.

Related

fullCalendar, IE 11, refetchEvents only fires when debugger/developer window [F12] is open

The problem:
fullCalendar does not update events using .fullCalendar('refetchEvents') after succesfull AJAX call.
Using
MSIE 11.0.41 (11.0.9600.18638) -- problem occurs
FireFox 53.0 (32-bit) -- problem does not occur
Chrome 58.0.3029.81 (64-bit) -- problem does not occur
VS2015, C# MVC on localhost, IIS Express
fullcalendar.js v3.3.0
jquery-3.1.1.js
jquery-ui-1.12.1.js
Description of symptoms:
When inserting a new event into fullCalendar by using below $.ajax call
$.ajax({
type: 'POST',
url: '#Url.Action("SaveEvent","Calendar")',
data: dataRow,
success: function (response) {
if (response == 'True') {
$('#_Calendar').fullCalendar('refetchEvents');
alert('New event saved!');
}
else {
alert('Error, could not save event!');
}
}
});
When the callback comes, the 'refetchEvents' method of fullCalendar will only fire -in MSIE 11- if the debugger/developer window is open. It doesn't matter if there is an actual breakpoint set, only having the debugger/developer window open makes the routine work ?
Even triggering the 'refetchEvents' completely separate from the $.ajax call has the same behaviour. I.E. if I trigger a function as below:
<button type="button" class="btn" onclick="fetchEvents(); return false;">trigger</button>
with function:
function fetchEvents() {
$('#_Calendar').fullCalendar('refetchEvents');
}
The effect is exactly the same, the 'refetchEvents' will only fire -in MSIE 11- if the debugger/developer window is open ?
I thought it might be a timing problem, hence my manual trigger option, yet the behaviour is the same even though the 'refetchEvents' call is way after the event has been inserted. Even page-refresh does not trigger the 'refetchEvents'the refresh of the events only fires -in MSIE 11- when debugger/developer window is open.
As indicated, no other browser on any platform (that I have tested) has the same result ?
(FireFox/Chrome/Safari/Android[Chrome & FireFox] all work flawless...)
Has anybody come across this behaviour and/or potentially have a solution ?
I would appreciate your input !
As it turns out it is indeed a caching problem, as per Alex K.'s answer, where IE is too enthusiastic in caching the events for the calendar and does not renew the events.
Solution
In an earlier stage I stumbled across this question and fantastic answer.
Somewhat later I also noted this question with an equally good answer. (make sure to upvote them !)
I have combined the two together in one attribute as per below.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class NoCacheAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
filterContext.HttpContext.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
filterContext.HttpContext.Response.Cache.SetValidUntilExpires(false);
filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
filterContext.HttpContext.Response.Cache.SetNoStore();
//Added later from: https://stackoverflow.com/questions/49547/how-to-control-web-page-caching-across-all-browsers
filterContext.HttpContext.Response.AppendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
filterContext.HttpContext.Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0.
filterContext.HttpContext.Response.AppendHeader("Expires", "0"); // Proxies.
base.OnResultExecuting(filterContext);
}
}
So, by decorating my GetDiaryEvents method with a [NoCache] attribute, the JSON response is appropriately tagged with correct headers, telling the browser NOT to cache the returned values.
Like so:
//GET: All Diary events
[NoCache]
public JsonResult GetDiaryEvents(string start, string end)
{
// code code code...
var rows = eventList.ToArray();
return Json(rows, JsonRequestBehavior.AllowGet);
}
And so the JSON message is sent to the browser as:
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/10.0
X-AspNetMvc-Version: 5.2
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?SDpcTWFydGlqbiBUaG9sZW5cTXkgRG9jdW1lbnRzXFZpc3VhbCBTdHVkaW8gMjAxNVxQcm9qZWN0c1xGaW5pc2hMaW5lXzQuMlxGaW5pc2hMaW5lXzQuMlxDYWxlbmRhclxHZXREaWFyeUV2ZW50cw==?=
X-Powered-By: ASP.NET
Date: Thu, 04 May 2017 09:36:35 GMT
Content-Length: 9265
problem solved...

(Instagram API) Trouble Reaching Tag Endpoints

I'm trying to gather a list of recent posts that contain a certain hashtag. The API Documentation states that I should be using the following GET call:
https://api.instagram.com/v1/tags/{tag-name}/media/recent?access_token=ACCESS-TOKEN
When I load the page where I want this information displayed, I perform the following:
using(HttpClient Client = new HttpClient())
{
var uri = "https://api.instagram.com/v1/tags/" + tagToLookFor + "/media/recent?access_token=" + Session["instagramaccesstoken"].ToString();
var results = Client.GetAsync(uri).Result;
// Result handling below here.
}
For reference, tagToLookFor is a constant string defined at the top of the class (eg. foo), and I store the Access Token returned from the OAuth process in the Session object with a key of 'instagramaccesstoken'.
While debugging this, I checked to make sure the URI was being formed correctly, and it does contain both the tag name and the just-created access_token. Using Apigee with the same URI (Save for a different access_token) returns the valid results I would expect. However, attempting to GET using the URI on my webstie returns:
{
StatusCode: 400,
ReasonPhrase: 'BAD REQUEST',
Version: 1.1,
Content: System.Net.Http.StreamContent,
Headers:{
X-Ratelimit-Remaining: 499
Vary: Cookie
Vary: Accept-Language
X-Ratelimit-Limit: 500
Pragma: no-cache
Connection: keep-alive
Cache-Control: no-store, must-revalidate, no-cache, private
Date: Fri, 27 Nov 2015 21:39:56 GMT
Set-Cookie: csrftoken=97cc443e4aaf11dbc44b6c1fb9113378; expires=Fri, 25-Nov-2016 21:39:56 GMT; Max-Age=31449600; Path=/
Content-Length: 283
Content-Language: en
Content-Type: application/json; charset=utf-8
Expires: Sat, 01 Jan 2000 00:00:00 GMT
}
}
I'm trying to determine what the difference between the two could be; the only thing that I can think of is that access_token is somehow being invalidated when I switch between pages. The last thing I do on the Login/Auth page is store the access_token using Session.Add, then call Server.Transfer to move to the page that I'm calling this on.
Any Ideas on what the issue could be? Thanks.
Attach the token to the header when making the request.
Client.DefaultRequestHeaders.Add("access_token", "Bearer " + token);
The problem ended up being one regarding Sandbox Mode. I had registered an app after the switch, and I was the only user in my sandbox. As a result, it had no problem finding my posts/info, but Sandbox Mode acts as if the Sandbox users are the only users on Instagram, so naturally it would not find anything else.
It turns out there was an existing registered application in my organization (made before the switch date) that does not have any such limitations, so I have been testing using that AppID/secret.
tl;dr: If you're the only user in your app's sandbox, work on getting users into your sandbox. See their article about it for more info.

JQuery ajax call executes error on 200 [duplicate]

This question already has answers here:
Ajax request returns 200 OK, but an error event is fired instead of success
(14 answers)
Closed 7 years ago.
My c#/WebApi server code looks like:
[HttpPost]
public HttpResponseMessage Logout()
{
// do some stuff here ...
return Request.CreateResponse(HttpStatusCode.OK);
}
In the client I do an ajax call using jquery 2.0.3
ADDED: Jquery code (typescript)...
var ajaxSettings: JQueryAjaxSettings = {};
ajaxSettings.type = 'POST';
ajaxSettings.data = null;
ajaxSettings.contentType = "application/json; charset=utf-8";
ajaxSettings.dataType = "json";
ajaxSettings.processData = false;
ajaxSettings.success = (data: any, textStatus: string, jqXHR: JQueryXHR) => {
console.log("Success: textStatus:" + textStatus + ", status= " + jqXHR.status);
};
ajaxSettings.error = (jqXHR: JQueryXHR, textStatus: string, errorThrow: string) => {
console.log("Error: textStatus:" + textStatus + ", errorThrow = " + errorThrow);
};
$.ajax("http://apidev.someurl.com/v1/users/logout", ajaxSettings);
ADDED 2: Request headers resulting from the above code:
POST http://apidev.someurl.com/v1/users/logout HTTP/1.1
Host: apidev.someurl.com
Connection: keep-alive
Content-Length: 0
Cache-Control: no-cache
Pragma: no-cache
Origin: http://apidev.someurl.com
Authorization: SimpleToken 74D06A21-540A-4F31-A9D4-8F2387313998
X-Requested-With: XMLHttpRequest
Content-Type: application/json; charset=utf-8
Accept: application/json, text/javascript, */*; q=0.01
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36
Referer: http://apidev.someurl.com/test/runner/apitest/index.html?
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
The response is 200, yet the error handler from the ajax call is fired instead of the success handler. Reason: parseerror, unexpected end of input.
One solution is to change serverside code to:
return Request.CreateResponse<String>(HttpStatusCode.OK, "Logout ok");
I understand that an empty response is not valid JSON, but the response message is intentionally empty. The 200 says it all.
The response headers look like:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Server: Microsoft-IIS/7.5
Access-Control-Allow-Origin: http://apidev.someurl.com
Access-Control-Allow-Credentials: true
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Sun, 05 Jan 2014 01:20:30 GMT
Content-Length: 0
Is this a bug in jquery? Or should the Request.CreateResponse(OK) never be used that way? Should I fix this with some workaround in the client? AFAIK the server is not doing it wrong here ... any thoughts?
EDIT:
Thanks to feedback of kevin, nick and John this issue has become clear. The solution I choose is to return a NoContent
return Request.CreateResponse(HttpStatusCode.NoContent);
Serverside this seems for this case the correct code. Clientside this is handled perfectly by JQuery (the succes handler is called). Thanx all for clearing this up!
(I don't know who to give credit for the answer ... since nick and kevin gave their valuable feedback in comments, johns feedback also added a better understanding) ... if no other suggestions are given ... i'll later mark the only 'answer' as the Answer)
Thanks all!
From MSDN, HttpStatusCode.OK is defined as follows (emphasis mine):
Equivalent to HTTP status 200. OK indicates that the request succeeded and that the requested information is in the response. This is the most common status code to receive.
Because a 200 implies some content is sent with the response (even an empty JSON object literal would be fine), you can run into problems if jQuery's Ajax implementation assumes a non-zero length response but does not receive it, especially if it tries parsing JSON (and possibly XML) from it. This is why John S makes the suggestion of changing the dataType to text; doing so would allow you to take specific action when receving an empty response.
On the other hand, HttpStatusCode.NoContent is defined as (emphasis mine):
Equivalent to HTTP status 204. NoContent indicates that the request has been successfully processed and that the response is intentionally blank.
In your particular situation, it may make more sense to set the status code to HttpStatusCode.NoContent to ensure that jQuery Ajax understands that it does not need to attempt any parsing/processing of the response.
This is how jQuery works.
JQuery will assume the response is JSON if either:
You specify dataType: 'json' in the ajax call, or
You do not include the dataType setting but jQuery detects the response is JSON because of a response header.
And when jQuery assumes the response is JSON, it automatically tries to parse the response into objects before it calls the success handler. If the parse fails, the error handler is called instead.
From the jQuery documentation:
As of jQuery 1.9, an empty response is also rejected; the server
should return a response of null or {} instead.
I don't know why jQuery couldn't make an exception for an empty response and treat it the same as null, or even as undefined, but if you cannot (or won't) change the response, a work-around would be to specify dataType: 'text' and then parse the response yourself.
$.ajax(url, {
type: 'post',
dataType: 'text',
data: params,
success: function(text) {
if (text) {
var data = jQuery.parseJSON(text);
// ...
}
}
});

What more do I need for a HTTP GET request?

I'm trying to use a web api to download a page.
A link to the api: http://api.arbetsformedlingen.se/
If I use code like:
HttpClient c = new HttpClient();
c.BaseAddress = new Uri("http://api.arbetsformedlingen.se/");
var response = c.GetAsync("platsannons/soklista/kommuner?lanid=10").Result;
The request looks like:
{Method: GET, RequestUri: 'http://api.arbetsformedlingen.se/platsannons/soklista/kommuner?lanid=10', Version: 1.1, Content: <null>, Headers:{}}
Shouldn't this be enough to get a valid response? What I do get is a 400 Bad request:
StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:{ Connection: close Proxy-Connection: keep-alive Date: Fri, 28 Jun 2013 10:02:34 GMT Server: Apache Content-Length: 117 Content-Type: application/json; charset=UTF-8}
Do I need to have something in the Content?
Shouldn't this be enough to get a valid response?
That totally depends on the server. Try to compare your request with a "real" request issued from a browser, for example using Fiddler, and spot the differences. The server might block automated requests by returning a 400.

Cache-Control Headers in ASP.NET

I am trying to set the cache-control headers for a web application (and it appears that I'm able to do it), but I am getting what I think are odd entries in the header responses. My implementation is as follows:
protected override void OnLoad(EventArgs e)
{
// Set Cacheability...
DateTime dt = DateTime.Now.AddMinutes(30);
Response.Cache.SetExpires(dt);
Response.Cache.SetMaxAge(new TimeSpan(dt.ToFileTime()));
// Complete OnLoad...
base.OnLoad(e);
}
And this is what the header responses show:
-----
GET /Pages/Login.aspx HTTP/1.1
Host: localhost:1974
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.10) Gecko/2009042316 Firefox/3.0.10 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
X-lori-time-1: 1244048076221
Cache-Control: max-age=0
HTTP/1.x 200 OK
Server: ASP.NET Development Server/8.0.0.0
Date: Wed, 03 Jun 2009 16:54:36 GMT
X-AspNet-Version: 2.0.50727
Content-Encoding: gzip
Cache-Control: private, max-age=31536000
Expires: Wed, 03 Jun 2009 17:24:36 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 6385
Connection: Close
-----
Why does the "Cache-Control" property show up twice?
Do I need both "Cache-Control" and the "Expires" properties?
Is "Page_Load" the best place to put this code?
Thanks!
You might also want to add this line if you are setting the max age that far out :
// Summary:
// Sets Cache-Control: public to specify that the response is cacheable
// by clients and shared (proxy) caches.
Response.Cache.SetCacheability(HttpCacheability.Public);
I do a lot of response header manip with documents and images from a file handler that processes requests for files the are saved in the DB.
Depending on your goal you can really force the browsers the cache almost all of you page for days locally ( if thats what u want/need ).
edit:
I also think you might be setting the max age wrong...
Response.Cache.SetMaxAge(new TimeSpan(dt.Ticks - DateTime.Now.Ticks ));
this line set is to 30 min cache time on the local browser [max-age=1800]
As for the 2x Cache Control lines... you might want to check to see if IIS has been set to add the header automatically.
I don't see Cache-control appearing twice. One is in the request, one is in the response. The one in the request is probably because you hit Shift+F5 in the browser or something similar.
To your second question: that depends on what you want to achieve with the cache headers.
I don't know what you wanted to achieve with the max-age. The value is way too high since you converted the DateTime incorrectly to a TimeSpan. Why don't you just use TimeSpan.FromMinutes instead?
Page load is okay. I usually mess around with HTTP headers there myself.

Categories

Resources