Why is action result cached? - asp.net-mvc

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.

Related

Set-Cookie in response header is not working

I have my backend(Ruby On Rails) running at port number 3000 with login endpoint(http://localhost:3000/login).I also have angular server running on port 4200.So with angular I am hitting login endpoint and upon successful login, response is sent back with Set-cookie option(authentication token is set).But it is not set in the browser.Below is the response header i am receiving:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Headers:*
Access-Control-Allow-Methods:POST, GET, OPTIONS
Access-Control-Allow-Origin:http://localhost:4200
Access-Control-Max-Age:1728000
Cache-Control:max-age=0, private, must-revalidate
Content-Type:application/json; charset=utf-8
ETag:W/"9dae161444bc908fd3289f4ed0fb15ed"
Set-Cookie:auth_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyLCJleHAiOjE1MDU4NDM0Nzh9.0E22wlacZWvmazpdJPLC4oRq3QM-8DX-npQu3w-U8Vs; path=/
Transfer-Encoding:chunked
Vary:Origin
X-Request-Id:267cfe1f-e60c-40b5-9a7b-4801d358cfd4
X-Runtime:0.273751
Request Headers
view source
While if I hit that API with Postman then the cookie is saved without problem.

API Endpoint only allows GET when it should take POST

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>

socket.http headers in lua

I am using the socket.http module to send http request but I am unable to get all the response headers from the http request.
Below is the sample code am using
I need to print all the response headers
Connection close, TE
Content-Length 210
Content-Type application/x-www-form-urlencoded
Host XXXX
TE trailers
User-Agent LuaSocket 3.0-rc1
method POST
protocol HTTP/1.1
test.lua
http = require("socket.http")
header = { }
local result,b,c,h = http.request{ url = "myurl", headers = header,method="POST" }
for k,v in pairs(c) do print(k,v) end
This is the output am getting:
content-type text/html; charset=UTF-8
server Apache/2.2.15 (CentOS)
date Tue, 16 Jun 2015 13:32:50 GMT
connection close
content-length 348
x-powered-by PHP/5.4.35
But I need all the headers like
Connection close, TE
Content-Length 210
Content-Type application/x-www-form-urlencoded
Host XXXX
TE trailers
User-Agent LuaSocket 3.0-rc1
method POST
protocol HTTP/1.1
You do print all response headers.
You can verify it by inspecting the information in the browser's development tools or by inspecting the TCP traffic directly.
By the way, you cannot expect request fields (e.g. user-agent) to be present in the response header.

localhost cookies not set

I'm trying to set a cookie in my application.
Here's the code that sets the cookie:
public HttpResponseMessage LogIn(UserLoginVM user)
{
// Do login stuff
var cookie = new CookieHeaderValue("STUPID-COOKIE", "12345");
cookie.Domain = Request.RequestUri.Host;
cookie.Path = "/";
cookie.HttpOnly = true;
// Get user's profile
HttpResponseMessage res = Request.CreateResponse<UserProfileVM>(HttpStatusCode.OK, profile);
res.Headers.AddCookies(new CookieHeaderValue[] { cookie });
return res;
}
The response from the server is the following:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
Set-Cookie: STUPID-COOKIE=12345; domain=localhost; path=/; httponly
Access-Control-Allow-Origin: *
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcUFJPSkVDVFNcU2Ftc2tpcC5TZXJ2aWNlV2ViTmV3XFNhbXNraXAuQXV0aEFQSVxTYW1za2lwLkF1dGhBUElcbG9naW4=?=
X-Powered-By: ASP.NET
Date: Wed, 18 Feb 2015 11:58:07 GMT
Content-Length: 8019
Notice the following header:
Set-Cookie: STUPID-COOKIE=12345; domain=localhost; path=/; httponly
However, when I go under "Cookies" in "Resources" tab in Chrome, nothing is set. Also when I send a request to the server no cookie is in the headers.
Here's the code that reads the cookie:
CookieHeaderValue cookie = Request.Headers.GetCookies("STUPID-COOKIE").FirstOrDefault();
cookie variable is always null.
My application is running on http://localhost:53998 and the authentication service is running on http://localhost:60858
My Chrome version is 40.0.2214.111.
Here's a GIF demonstrating the problem:
http://i.imgur.com/q7lkXBz.gif
Edit: This seems to be non-specific to Chrome. This doesn't work on FireFox (v35) either. GIF: http://i.imgur.com/ZewnEtc.gif
I ran into this issue today and Gaui's answer was quite useful to me, bearing in mind ideally you do not want to open up your server to CORS requests from any site. I'm in my dev environment and my server and client are on different origins since they are on different ports on localhost. Also I'm using .net core 3.0
My issue was caused by my server not sending cookies to my client side as my CORS settings were blocking the sending of cookie to my domain this was evident by the server not using the header Access-Control-Allow-Credentials: true. To resolve this I configured my server in Startup.cs to allow requests from my client side to allow credentials (A credential is a cookie's authorization headers or TLS client certificates).
var allowedOrigins = new[] {"localhost:3000"}; // Ideally comes from appsettings
app.UseCors(builder =>
builder.WithOrigins(allowedOrigins).AllowCredentials().AllowAnyMethod().AllowAnyHeader().Build());
For the cookie options; I found that the you do not have to set Domain if you do not want to, Secure works even when the site is not using https.
Google chrome now supports cookies on localhost, I believe it didn't used to as a lot of older SO posts have users who faced that issue.
On the client side, you need to configure it to accept cookies as well, as in Gaui's answer above. I was using fetch, and so had to add the option:
credentials: 'include'
Which tells fetch to retrieve cookies across domains. See the docs here
I highly suspect that localhost is not a valid domain name so Chrome rejects it. If you simply remove 'domain=localhost' from the Set-Cookie then it will work and Chrome will assign the domain to localhost for you.
I would personally create a local domain name like "test.dev" and add it to your Windows hosts file, 127.0.0.1 test.dev
I finally managed to solve this.
In the response from the API I had to add Access-Control-Allow-Credentials: true headers, or by adding the following in the WebApiConfig class:
public static void Register(HttpConfiguration config)
{
var cors = new EnableCorsAttribute("*", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
}
Then I had to enable it on the client-side, by setting the withCredentials property in the XMLHTTPRequest object to true. In AngularJS app config function you can do the following:
$httpProvider.defaults.withCredentials = true;
Also, for Chrome, I had to omit the Domain (or set it as null).
Hope this helps others struggling with this.

Response Permanent Redirect in MVC not issuing 301 or changing URL

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

Resources