I'm using the Jquery full calendar plugin, and i want to be able to click on an event and the details of the event to be populated via AJAX into a div with the id of #details.
here is my controller action that i'm trying to load. When debugging, the action does not consider the incoming request to be AJAX and returns the full view instead of the partial. Does it matter if the full view is called the same as the partial view? Ie; 'Details.aspx' & 'Details.ascx'?
public ActionResult Details(int id)
{
Pol_Event pol_Event = eventRepo.GetEvent(id);
ViewData["EventTypes"] = et.GetEventType(id);
if (pol_Event == null)
return View("NotFound");
else
{
if(HttpContext.Request.IsAjaxRequest()){
return PartialView("Details");
}
else
return View(pol_Event);
}
}
Here is the jquery code i'm using. Am i missing not using .load() correctly in the eventClick function? The Developer of the calendar plugin has confirmed that eventClick has nothing to do with AJAX so the fault must lie in my code.
$(document).ready(function() {
$('#calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
events: "/Events/CalendarData",
allDayDefault: false,
selectable: true,
eventClick: function(event) {
$('details').load(event.url);
},
eventRender: function(event, element) {
element.qtip({
content: event.title + " # " + event.venue,
position: {
corner: {
target: 'topLeft',
tooltip: 'bottomLeft'
}
}
});
}
});
});
So am i using the Jquery.Load() function incorrectly, or is there something wrong with my controller?
More updates: I finally caught the problem. The XMLHttpRequest is being sent but i'm encountering a 500 internal server error, not solved yet as i can't figure out what's causing the error.
Host: localhost:4296
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.8) Gecko/20100722
Firefox/3.6.8
Accept: text/html, */*
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: 115
Connection: keep-alive
X-Requested-With: XMLHttpRequest
Referer: http://localhost:4296/Events/EventCalendar
Cookie: .ASPXAUTH=56C5F4FD536564FF684F3F00E9FB51A5F2F1B22D566C517923D71FEAF599D266531CAC52BF49D2700E048DD420A4575456855303CC2DCB5875D4E1AD8883821EA62E5169969503776C78EB3685DAA48C
UPDATE: I finally figured out what the problem was. I wasn't passing in the model to the partial so the line
return PartialView("Details");
Should have been
return PartialView("Details", pol_Event);
this was generating the 500 internal service error.
When you make an Ajax request you're suppose to set the 'X-Requested-With' HTTP header to something like 'XMLHttpRequest' eg.
Host www.google.com
User-Agent Mozilla/5.0 (Windows; U; Windows NT 6.1; (snip...)
Accept */*
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 115
Connection keep-alive
X-Requested-With XMLHttpRequest
Referer http://www.google.com
This 'X-Requested-With' header is what the 'IsAjaxRequest()' method looks for. Normally, jQuery's Ajax methods will send this header automatically. My guess is that for some reason the jQuery Calendar plugin isn't sending this header.
I would download something like fiddler, or install Firebug for Firefox and check the raw HTTP Request/Response data when the Ajax request/Calendar control is fired/initialised. See if the X-Requested-With header is being included.
Yes. Although it doesn't have to be sent as a HTTP Request Header. You can POST it in a form data or in the query string of a GET. www.example.com?x-requested-with=XMLHttpRequest (case sensitive value)
Seems ridiculous but true. I've tried it and it works :) Reflection on the IsAjaxRequest() extension method will prove this:
return ((request["X-Requested-With"] == "XMLHttpRequest") || ((request.Headers != null) && (request.Headers["X-Requested-With"] == "XMLHttpRequest")));
Related
I am building a React.js front-end portion of a web application using an already existing back-end that is built with ASP.NET MVC Framework. The method in the API's controller is named Post (which should default it to a post method) and even has the [HttpPost] decorator. I have recently enabled CORS, but I am not sure if that matters.
The API also has Swagger (it's like postman if you haven't used it before) enabled, and when I call the method as POST from the swagger, it will work and return the correct data.
However, when I try to call the method from my UI component, the API endpoint will give a 405 Method Not Allowed status code.
It says that the method only allows GET, which is baffling since it is supposed to only allow POST and it works that way on swagger or if I manually travel to the URL using a browser. Has anyone had a problem like this using React components or working with ASP.NET MVC/Web API 2?
I am completely stuck...
EDIT:
I am adding the API's method for the post endpoint.
[Route("{id}/merchant/SearchMerchants")]
[HttpPost]
[AuthorizeUserPermissionsToken(Cookie)]
public Response Post(Request rq)
{ // I put a breakpoint here to debug
// some code
}
I had to redact a lot of the code but here is the post method.
But I don't think it has to do with the API endpoint.
This is because when I debug, it will not even reach the breakpoint or call the method at all, when I am using the UI. However, it will reach the breakpoint when I travel to the endpoint on the browser or use swagger.
EDIT 2:
EDIT 3: (removed EDIT 2 and moved the original headers to bottom to reduce confusion)
Pre-flight OPTIONS request headers
Accept:*/*
Accept-Encoding:gzip, deflate
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:content-type
Access-Control-Request-Method:POST
Cache-Control:no-cache
Connection:keep-alive
Host: *redacted domain*
Origin:http://localhost:49702
Pragma:no-cache
Referer:http://localhost:49702/FindMerchants
User-Agent:Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Mobile Safari/537.36
response headers to OPTIONS request:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:Origin, X-Requested-With, Content-Type, Accept
Access-Control-Allow-Origin:http://localhost:49702
Allow:OPTIONS, TRACE, GET, HEAD, POST
Content-Length:0
Date:Mon, 07 Aug 2017 18:41:42 GMT
Public:OPTIONS, TRACE, GET, HEAD, POST
Server:Microsoft-IIS/10.0
X-Content-Type-Options:nosniff
X-Frame-Options:SAMEORIGIN
then the real POST request headers after pre-flight:
Accept:*/*
Accept-Encoding:gzip, deflate
Accept-Language:en-US,en;q=0.8
Cache-Control:no-cache
Connection:keep-alive
Content-Length:154
Content-Type:application/json
Cookie: *redacted a bunch of cookies*
Host:a *redacted domain*
Origin:http://localhost:49702
Pragma:no-cache
Referer:http://localhost:49702/FindMerchants
User-Agent:Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Mobile Safari/537.36
The real response headers are like so:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:Origin, X-Requested-With, Content-Type, Accept
Access-Control-Allow-Origin:http://localhost:49702
Allow:GET
Cache-Control:no-cache
Content-Length:73
Content-Type:application/json; charset=utf-8
Date:Mon, 07 Aug 2017 17:58:28 GMT
Expires:-1
Pragma:no-cache
Server:Microsoft-IIS/10.0
X-Content-Type-Options:nosniff
X-Frame-Options:SAMEORIGIN
EDIT 4:
Request URL:*redacted domain*/380/merchant/SearchMerchant
Request Method:POST
Status Code:405 Method Not Allowed
Remote Address:127.0.0.1:80
Referrer Policy:no-referrer-when-downgrade
EDIT #9999 (jk):
My react component simply calls a custom data service that I wrote, which has a post method like this.
post(relativeUrl, data, apiVersion) {
var url = this.getUrl(relativeUrl, apiVersion);
var res = $.ajax({
type: 'POST',
url: url,
contentType: 'application/json',
xhrFields: {
withCredentials: true
},
crossDomain: true,
data: JSON.stringify(data)
});
return res;
}
I simply pass relativeUrl to fetch a list related to a user account.
Before I make a call that fails (which shows 405), I use this same method to log in and update the global redux state for the app. After updating the global state, I render a different component and use this method again with another relativeUrl to fetch the list. Then I get the error. I also make sure that the login call is finished and returns and updates the state correctly before moving on.
To follow my comment,
in web.config file:
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Methods" value="POST, GET, OPTIONS, HEAD" />
</customHeaders>
</httpProtocol>
I have an action that generates a password reset link and emails it to the user
public ActionResult SendResetPasswordEmail(string userName)
{
var webUser = LoadUser(userName);
if (webUser != null)
{
var token = WebSecurity.GeneratePasswordResetToken(webUser.UserName);
emailSender.SendPasswordResetEmail(webUser, token, resetAction);
return new HttpStatusCodeResult(HttpStatusCode.OK);
}
return new HttpStatusCodeResult(HttpStatusCode.BadRequest, "No user found with username: " + userName);
}
The first time I call the action from the browser, I get an HTTP 200 response (and hit my breakpoint in the action).
The second time I call the action from the browser, I get an HTTP 304 response indicating that the content is unchanged.
There are no [OutputCache] attributes anywhere in the source file (not on the class or the action).
What is causing the web server to decide that the content is unchanged and return the HTTP 304?
I'm aware of a work-around
https://stackoverflow.com/a/18620970/141172
I'm interested in understanding the root cause for the HTTP 304 response.
Update
Headers on first request:
Request Headers
Request GET /Companies/SendResetPasswordEmail/?userName=ej HTTP/1.1
X-Requested-With XMLHttpRequest
Accept */*
Referer http://local:6797/Companies
Accept-Language en-US
Accept-Encoding gzip, deflate
User-Agent Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Host localhost:6797
DNT 1
Connection Keep-Alive
Cookie __RequestVerificationToken=sNOBS6qz32LtnJpLWgHHELhaE44DfIVE1LSMUgjzHjcwsvxlUFa4lOSyA5QeB8keLXYL08Psjg29CRI7W73uHLJy6A81; .ASPXAUTH=DAF8AF47E955F723EE9438866BE1B4BFBF91BA01912EF087824F03581DBCA05A4AECA01373FAF40DF0C4D5C17F17DEFA2F85C1B702988B7E0F750BFE19566FC711C7D6BD81D8F0B0ABD68AF5B3D9BA032286361F; ASP.NET_SessionId=5e2gcvkc2p3rji25z5emyqzd; HelixPlugins1.0=IEPlugin1.0
Response Headers
Response HTTP/1.1 200 OK
Server ASP.NET Development Server/11.0.0.0
Date Thu, 03 Apr 2014 23:29:02 GMT
Cache-Control private, s-maxage=0
Content-Length 0
Connection Close
NOTE: I changed localhost to local in the above because StackOverflow does not allow links containing localhost to be posted :-)
The browser is Internet Explorer 10.
IE caches ajax responses by default, you need to explicitly tell it not to do any ajax caching by setting your ajax object's cache property to false.
Browsers such as Chrome automatically append a random token to your request to make it unique.
I am converting an old ASP.NET web forms site to ASP.NET MVC 5. I would like to issue permanent redirects for the old page URLs.
Here is what I have done -
RouteConfig.cs:
routes.MapRoute("About_old",
"About/About.aspx",
new { controller = "Home", action = "About_old" });
HomeController.cs:
public ActionResult About_old()
{
return new RedirectResult("/About", true);
// I've also tried
// return RedirectToActionPermanent("About");
// return RedirectPermanent("/About");
}
All attempts load the correct /About view, however the URL does not change, and I do not see a 301 code in the response. In other words, the URL is "localhost/About/About.aspx" and I expect it to be "localhost/About"
Complete Request/Repsonse from Chrome:
Request URL:http://localhost:55774/About/About.aspx
Request Method:GET
Status Code:200 OK
Request Headers
GET /About/About.aspx HTTP/1.1
Host: localhost:55774
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Response Headers
Cache-Control:private
Content-Encoding:gzip
Content-Length:2284
Content-Type:text/html; charset=utf-8
Date:Sat, 01 Mar 2014 18:10:41 GMT
Server:Microsoft-IIS/8.0
Vary:Accept-Encoding
X-AspNet-Version:4.0.30319
X-AspNetMvc-Version:5.1
X-Powered-By:ASP.NET
Nowhere do I see a 301 and the URL does not change. I have to think this has to do with how I am mapping the route of the old aspx page in RouteConfig.cs as all action methods have the same results. NOTE I have put a solution using global.asax below, however I would prefer it to work as I am attempting above, so I have not accepted my answer.
Am I doing something wrong or just missing something? How do I get the 301 to issue and URL to change?
Here is my solution (Global.asax)
protected void Application_PreRequestHandlerExecute(Object sender, EventArgs e)
{
string currentUrl = HttpContext.Current.Request.Path.ToLower();
if (currentUrl.EndsWith("/about/about.aspx"))
{
Response.Status = "301 Moved Permanently";
Response.AddHeader("Location", "/About");
Response.End();
}
}
From answer here: Global 301 redirection from domain to www.domain
I'm just looking at some Ajax requests in Fiddler whilst testing some exception handling classes and code in my application and I'm not sure my requests are well formed and as they should be.
My Javascript is:
$(function () {
$('#createentry').submit(function () {
e.preventDefault();
$.ajax({
url: this.action,
type: this.method,
dataType: "json",
data: $(this).serialize(),
success: function(result) {
$('#entries-list').append("<li>" + $('#newentry').val() + "</li>");
$('#newentry').val('').blur();
},
error: function (xhr)
{
try
{
var json = $.parseJSON(xhr.responseText);
alert(json.errorMessage);
}
catch (e)
{
alert('something bad happened');
}
}
});
return false;
});
});
In Fiddler the request looks like:
Host: localhost:54275
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64; rv:20.0) Gecko/20100101 Firefox/20.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-gb,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost:54275/Diary
Cookie: __RequestVerificationToken= <snipped for brevity>
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 216
I would expect the Accept: header to be set to json automatically I guess that's an incorrect assumption on my part, how is this set?
Also if I looks at the result of this in my action method the value is always false:
[HttpPost, ValidateAntiForgeryToken, JsonExceptionFilter]
public JsonResult PostNewEntry(DiaryEntryViewModel diaryEntry)
{
var req = Request.IsAjaxRequest();
req always = false so my JsonExceptionFilter isn't kicking in and taking care of the error reporting as expected.
Is there also a defined way of forcing only accepting requests correctly setup as Ajax requests in MVC?
I found this bug that is over five years old that says using .ajax() with a dataType of script or JSON would result in the header missing is a feature. But I would imagine Request.IsAjaxRequest() is looking for that exact header.
As a work around you could try doing something like:
$(document).ajaxSend(function (event, request, settings) {
request.setRequestHeader("X-Requested-With", "XMLHttpRequest");
});
which would append that header to every Ajax call jQuery sends.
After trying pretty much every solution I could find on the web with absolutely no success, I opted to start using the jQuery Form Plugin and I am now getting well formed Ajax requests on the server side.
Download the plugin from the link above and I then replaced my Ajax JavaScript with:
$(document).ready(function () {
$('#createentry').ajaxForm(function () {
alert("Thank you for your comment!");
});
});
After including this script and testing a call to:
Request.IsAjaxRequest();
Now correctly returns true.
Using Firebug to examine the requests being posted the required header that denotes the request as an Ajax request is present:
X-Requested-With XMLHttpRequest
I am using Ajax binding with the Grid and ran into a problem where ASP.NET MVC was throwing a HttpRequestValidationException when I attempted an operation on the grid that invoked the Ajax call (like sorting).
Using Fiddler I was able to determine my browser was attempting to post back my entire model in the query string and there were some characters that would have triggered ASP.NET's request validation.
I was able to work around this by simply adding
[ValidateInput(false)]
to my controller action.
I'm wondering why was the grid sending back so much data? And is this a symptom of a bug in my code or Telerik's?
Here is what I saw in Fiddler (note the extremely long query string, even though the HTTP verb is "POST").
POST http://127.0.0.1:52601/MyController/DatabindGrid?Items=System.Collections.ObjectModel.Collection%601%5BPMyNamespace.Data.ViewEntities.Alert%5D&DetailsClientUrl=&GetJsonUrl=%2FMonitor%2FGetGridJson&ViewName=&CurrentItem=&PageTitle=My%20Web%20Page&CopyrightText=Copyright%20%26%23x00A9%3B%202004-2010%20My%20Company%20Corporation&BrowserCapabilities=&OemName=&UiVersion=20101123 HTTP/1.1
Referer: http://ipv4.fiddler:52601/MyController/MyAction
Content-Type: application/x-www-form-urlencoded
X-Requested-With: XMLHttpRequest
Accept: text/plain, */*; q=0.01
Accept-Language: en-US
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Host: 127.0.0.1:52601
Content-Length: 56
Connection: Keep-Alive
Pragma: no-cache
Cookie: ASP.NET_SessionId=bvgwuno4bounqio2werepkw4; .ASPXAUTH=612E18BB916F720D13A0F0D1695A86079B94DFE94A3BF5A9A8F19E35A37AE987282B7B684201C112CEC6081181E3D1C52C5517A66D9158E4CF83C1C3F523EE32FF783BD2E3B6E0A42A35E1874E63BA76C7735F9E8ABBA4E58BF61EB29DA03789E07A201A1BA9E7B85F941516ED7EA26E3E8E1E65D0836F39A109201E357EE97478D1A359B3FB4B4AD4C64A02A0CE7BBB39DC8FE1F73B179F284A14CF55D9C67D
page=1&size=10&orderBy=LocationName-asc&groupBy=&filter=
Rick,
I think you might be interested in this...
http://tv.telerik.com/watch/aspnet-mvc/video/building-a-responsive-site-with-mvc
very detailed and informative.
~AZee