I'm trying to use http caching. In my controller I'm setting a response as follows:
$response->setPublic();
$response->setMaxAge(120);
$response->setSharedMaxAge(120);
$response->setLastModified($lastModifiedAt);
dev mode
In dev environment first response is a 200 with following headers:
cache-control:max-age=120, public, s-maxage=120
last-modified:Wed, 29 Feb 2012 19:00:00 GMT
For next 2 minutes every response is a 304 with following headers:
cache-control:max-age=120, public, s-maxage=120
This is basically what I expect it to be.
prod mode
In prod mode response headers are different. Note that in app.php I wrap the kernel in AppCache.
First response is a 200 with following headers:
cache-control:must-revalidate, no-cache, private
last-modified:Thu, 01 Mar 2012 11:17:35 GMT
So it's a private no-cache response.
Every next request is pretty much what I'd expect it to be; a 304 with following headers:
cache-control:max-age=120, public, s-maxage=120
Should I worry about it? Is it an expected behaviour?
What will happen if I put Varnish or Akamai server in front of it?
I did a bit of debugging and I figured that response is private because of last-modified header. HttpCache kernel uses EsiResponseCacheStrategy to update the cached response (HttpCache::handle() method).
if (HttpKernelInterface::MASTER_REQUEST === $type) {
$this->esiCacheStrategy->update($response);
}
EsiResponseCacheStrategy turns a response into non cacheable if it uses either Last-Response or ETag (EsiResponseCacheStrategy::add() method):
if ($response->isValidateable()) {
$this->cacheable = false;
} else {
// ...
}
Response::isValidateable() returns true if Last-Response or ETag header is present.
It results in overwriting the Cache-Control header (EsiResponseCacheStrategy::update() method):
if (!$this->cacheable) {
$response->headers->set('Cache-Control', 'no-cache, must-revalidate');
return;
}
I asked this question on Symfony2 user group but I didn't get an answer so far: https://groups.google.com/d/topic/symfony2/6lpln11POq8/discussion
Update.
Since I no longer have access to the original code I tried to reproduce the scenario with the latest Symfony standard edition.
Response headers are more consistent now, but still seem to be wrong.
As soon as I set a Last-Modified header on the response, the first response made by a browser has a:
Cache-Control:must-revalidate, no-cache, private
Second response has an expected:
Cache-Control:max-age=120, public, s-maxage=120
If I avoid sending If-Modified-Since header, every request returns must-revalidate, no-cache, private.
It doesn't matter if the request was made in prod or dev environment anymore.
I have faced same problem. I had to supply 'public' headers my cdn. By default when gateway caching is enabled in prod mode, it returns 200 OK with private, nocache must validate headers.
I solved problem this way.
In app.php, before I send response to user ($respond->send), I have overwritten the cache control header to blank and set cache headers to public and max age(some value).
//code snippet from app.php
$response = $kernel->handle($request);
$response->headers->set('Cache-Control', '');
$response->setPublic();
$response->setMaxAge(86400);
$response->send();
The behavior you experience is intended. Symfony2 Docs explicitly describe the situations when private and public are used, default being private.
Related
I have a web api method:
[HttpPost]
public IActionResult Post([FromBody]string entryInJson)
{ .... }
This method is hit by a remote domain, so CORS is needed. As i wasn't successful in finding the relevant cs file in my mvc6 app, i used this solution in web.config:
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
When I do the post, using online tools like http://requestmaker.com/, i am getting this:
Request Headers Sent:
POST /api/Entries/ HTTP/1.1
Host: justalk.tukuoro.com
Accept: */*
Content-Type: text/plain
Content-Length: 179
Response Headers:
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 0
Expires: -1
Location: https://justalk.tukuoro.com/Account/Login?ReturnUrl=%2FHome%2FError
X-Powered-By: ASP.NET
Access-Control-Allow-Origin: *
Date: Mon, 16 May 2016 05:25:53 GMT
Now, it seems like it is trying to redirect me (302) but to the error page, and it is not sending anything back from the post method, which returns:
return new ObjectResult(true);
I am not sure what is going on:
Am I getting to the right controller? i have code that is supposed to log the incoming string but it does not create the log.
If it is not working? why? is the response header telling me something I don't understand?
Am I doing it right?
thank you for your time!
if you want send json post request this entryInJson must be object type not string
for example
[HttpPost]
public IActionResult Post([FromBody]EntryInJson entryInJson)
{ .... }
chat comment
the code doing the http request to my post method - how does it convert this class "RemoteEntry" to JSON data
http://www.newtonsoft.com/json/help/html/serializingjson.htm
Update :
302 is not an error but a redircet to a tempoaray location. you could resend the request to the new location received in the response header.
and it's propably a server misconfiguration issue then.
modSecurity firewall blocking your requests an making automatics redirections (302).
I'm writing a chrome extension that uses AJAX to talk to a server. I want to read the KEY in set-cookie from AJAX response, but I cannot find a way to do this.
I have tried document.cookie and xhr.getAllResponseHeaders()
$.get(myURL,
function (output, status, xhr) {
console.log(document.cookie); //empty
console.log(xhr.getAllResponseHeaders());
//Only Connection and Content-Type shows
}
);
The raw response header is:
Connection:Close
Content-Type:text/html
Set-Cookie:KEY=DFDSFDCB; PATH=/;
I know that according to spec, getAllResponseHeaders() is supposed to filter out set-cookie field. Is there any workaround?
CORS is starting to fry my brain a bit. Everything is good now, apart from one method. I'm building an app with backbone on the frontend and node.js/restify on the backend. The server.coffee looks like this:
server.get '/todos', todos.find_all
server.get '/todos/:id', todos.find_by_id
server.del '/todos/:id', todos.delete
Whenever a model in backbone calls destroy however I get this rather annoying error:
MLHttpRequest cannot load http://localhost:8080/todos/. Method DELETE is not allowed by Access-Control-Allow-Methods.
I read about this a bit and using restify done the following:
unknownMethodHandler = (request, response) ->
if(request.method.toLowerCase() == 'options')
allowHeaders = ['Accept', 'Accept-Version', 'Content-Type', 'Api-Version']
if(response.methods.indexOf('OPTIONS') == -1) then response.methods.push('OPTIONS')
response.header 'Access-Control-Allow-Credentials', true
response.header 'Access-Control-Allow-Headers', allowHeaders.join(', ')
response.header 'Access-Control-Allow-Methods', ['GET', 'DELETE', 'TEST!']
response.header 'Access-Control-Allow-Origin', request.headers.origin
response.send 204
else
response.send new restify.MethodNotAllowedError()
server.on 'MethodNotAllowed', unknownMethodHandler
But even still, I get this as the response header:
HTTP/1.1 204 No Content
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Api-Version, X-Request-Id, X-Response-Time
Connection: Keep-Alive
Date: Mon, 04 Feb 2013 12:24:25 GMT
Server: restify
X-Request-Id: fbd4e15a-a22e-48b6-bf5c-a46b94926748
X-Response-Time: 0
I just don't get what I'm doing wrong!
If you're expecting a response, you should use a '200' response code, not a 204 as that's a No Content response. See the W3C Spec for the details
9.7 DELETE
The DELETE method requests that the origin server delete the resource identified by the Request-URI. This method MAY be overridden
by human intervention (or other means) on the origin server. The
client cannot be guaranteed that the operation has been carried out,
even if the status code returned from the origin server indicates that
the action has been completed successfully. However, the server SHOULD
NOT indicate success unless, at the time the response is given, it
intends to delete the resource or move it to an inaccessible location.
A successful response SHOULD be 200 (OK) if the response includes an entity describing the status, 202 (Accepted) if the action has not
yet been enacted, or 204 (No Content) if the action has been enacted
but the response does not include an entity.
If the request passes through a cache and the Request-URI identifies one or more currently cached entities, those entries SHOULD
be treated as stale. Responses to this method are not cacheable.
You're seeing the Access-Control-Allow-Origin: * in the response header. This is coming from the .../restify/lib/router.js preflight() method. The comment states "user will need to defined their own .opts handler".
Use server.opts method to wirte your own handler for OPTIONS request.
Below is the example you can use.
Also tell me if you are using set-credentials flag to true while making request from the browser. This handle in that case would have to respond with access cookies.
In the example below, I am returning the allowed origin for exact match.
You can tweak it to be substring match also. But always return the exact value as found in request header origin in the response header 'Access-Control-Allow-Origin'. Its a good practice.
server.opts('/api/(.)*', (req, res) => {
const origin = req.header('origin');
const allowedOrigins = ['example.com', 'example.org'];
if (allowedOrigins.indexOf(origin) === -1) {
//origin is not allowed
return res.send(405);
}
//set access control headers to allow the preflight/options request
res.setHeader('Access-Control-Allow-Origin', header);
res.setHeader('Access-Control-Allow-Headers', 'Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version');
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE,OPTIONS');
// Access-Control-Max-Age header catches the preflight request in the browser for the desired
// time. 864000 is ten days in number of seconds. Also during development you may want to keep
// this number too low e.g. 1.
res.setHeader('Access-Control-Max-Age', 864000);
return res.send(200);
});
Just set header res.setHeader('Access-Control-Allow-Methods', '*');
Here is the answer: https://github.com/mcavage/node-restify/issues/296#issuecomment-12333568
I'm trying to get a list of Cities by sending the State name through Ajax in my SpringMVC 3.0 project.
For the purpose, I've used the following call (using jQuery) in my JSP:
<script type="text/javascript">
function getCities() {
jq(function() {
jq.post("getCities.html",
{ stateSelect: jq("#stateSelect").val()},
function(data){
jq("#cities").replaceWith('<span id="cities">Testing</span>');
});
});
}
</script>
And here's my Controller code:
#RequestMapping(value = "/getCities", method = RequestMethod.POST)
public #ResponseBody List<StateNames> getCities(#RequestParam(value="stateSelect", required=true) String stateName,
Model model) {
// Delegate to service to do the actual adding
List<StateNames> listStates = myService.listCityNames(stateName);
// #ResponseBody will automatically convert the returned value into JSON format
// You must have Jackson in your classpath
return listStates;
}
But I get HTTP 406 error stating the following when i run it:
406 Not Acceptable
The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request.
I've used Jackson in my Maven dependencies & have defined in my context file.
I've googled extensively & I guess the problem is #ResponseBody is not automatically converting my List to appropriate JSON object.
My Firebug says:
Response Headers
Server Apache-Coyote/1.1
Content-Type text/html;charset=utf-8
Content-Length 1070
Date Sat, 12 Feb 2011 13:09:44 GMT
Request Headers
Host localhost:8080
User-Agent Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13
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
Content-Type application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With XMLHttpRequest
Referer http://localhost:8080/MyApplication/
Content-Length 17
Cookie JSESSIONID=640868A479C40792F8AB3DE118AF12E0
Pragma no-cache
Cache-Control no-cache
Please guide me. What am i doing wrong?? HELP!!
As Peter had written in his comment, the cause of the problem is inability of Spring to load Jackson. It is not loaded by dependencies by default. After I've added the dependency
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.2</version>
</dependency>
the JSON was returned after typing the address in the browser, without any tricks with Accept headers (as it is supposed to do).
Tested on Tomcat 7.0.
You have incorrect response content type it supposed to be application/json.
You need to add jackson to your /lib directory.
and you should have
<mvc:annotation-driven />
In your serlvet-name.xml file.
In addition I recommend you to map your request as get and try to browse it with Google Chrome,to see if it returns correct result. It has very good json representation.
The problem is not on server side, but on the client one.
Take a look at the error message carefully: The requested resource (generated by server side) is only capable of generating content (JSON) not acceptable (by the client!) according to the Accept headers sent in the request.
Examine your request headers:
Accept */*
Try this way:
function getCities() {
jq(function() {
jq.post(
"getCities.html", // URL to post to
{ stateSelect: jq("#stateSelect").val() }, // Your data
function(data) { // Success callback
jq("#cities").replaceWith('<span id="cities">Testing</span>');
},
"json" // Data type you are expecting from server
);
});
}
This will change your Accept header to the following (as of jQuery 1.5):
Accept: application/json, text/javascript, */*; q=0.01
This will explicitly tell the server side that you are expecting JSON.
Using jQuery , you can set contentType to desired one (application/json; charset=UTF-8' here) and set same header at server side.
REMEMBER TO CLEAR CACHE WHILE TESTING.
I too had a similar problem while using the Apache HTTPClient to call few services. The problem is the client and not the server. I used a HTTPRequester with header accepting application/json and it worked fine.
My goal is to upload file with ajax-way.
I use this javascript library http://valums.com/wp-content/uploads/ajax-upload/demo-jquery.htm
There is a link on my page like "Upload" button on example page.
When I click it, "Open file" dialog is open.
I choose file and form is automatically submitted.
This is my javascript code.
var upload_btn = $('#upload-opml');
new AjaxUpload(upload_btn.attr('id'), {
action: upload_btn.attr('href'),
name: 'opml',
onComplete: function (file, response) {
//
}
});
This is server code in Ruby on Rails.
def upload_opml
render :text => 'hello'
end
Headers, taken from Firebug.
>> Response headers
Server nginx/0.7.62
Date Wed, 09 Jun 2010 19:03:28 GMT
Content-Type text/html; charset=utf-8
Connection keep-alive
Etag "5d41402abc4b2a76b9719d911017c592"
X-Runtime 18
Content-Length 5
Cache-Control private, max-age=0, must-revalidate
Set-Cookie _RssWebApp_session=BAh7CDoPc2Vzc2lvbl9pZCIlMzJhMTQ0ZWZhOGM3YmIwODFhZmFmNjkwYTI1YWQ2ZjQ6EF9jc3JmX3Rva2VuIjEvZHVzdm1NOVlMTUF6bEw3cGRFT2I3RzZvcVJZUU42bCtMNS9PVVYrNHdBPToMdXNlcl9pZGkG--13f1950a9530591881404fbfab7b1246f98f0d81; path=/; HttpOnly
>> Request headers
Host readbox.cz
User-Agent Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.2) Gecko/20100115 Firefox/3.6
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language ru,en-us;q=0.7,en;q=0.3
Accept-Encoding gzip,deflate
Accept-Charset windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive 115
Connection keep-alive
Referer http://readbox.cz/view
Cookie _RssWebApp_session=BAh7CDoPc2Vzc2lvbl9pZCIlMzJhMTQ0ZWZhOGM3YmIwODFhZmFmNjkwYTI1YWQ2ZjQ6EF9jc3JmX3Rva2VuIjEvZHVzdm1NOVlMTUF6bEw3cGRFT2I3RzZvcVJZUU42bCtMNS9PVVYrNHdBPToMdXNlcl9pZGkG--13f1950a9530591881404fbfab7b1246f98f0d81; login=1; APE_Cookie=%7B%22frequency%22%3A11%7D; show-tsl=0
But in Firefox I get an error
!:#8?BC http://readbox.cz (document.domain=http://readbox.cz) >B:070=> 2 #07#5H5=88 =0 ?>;CG5=85 A2>9AB20 HTMLDocument.readyState 87 http://readbox.cz (document.domain =5 1K; CAB0=>2;5=).
[Break on this error] if (doc.readyState && doc.readyState != 'complete') {
In Google Chrome
Unsafe JavaScript attempt to access frame with URL http://readbox.cz/subscriptions/upload_opml from frame with URL http://readbox.cz/view#/posts/all. Domains, protocols and ports must match.
/javascripts/ajaxupload.js?1276107673:574
Uncaught TypeError: Cannot read property 'readyState' of undefined
Domain readbox.info points to 127.0.0.1. It's for development.
I had the same problem and I fix it editing the ajaxupload library, with this commit:
https://github.com/felipelalli/ajax-upload/commit/9307f5eb6ded1ec63eac828a7ef4b8187acb9617
I already sent a pull request to the author.
I had this problem when I was using the sandbox developer environment (opensocial for Orkut). I just check now if "doc" is undefined. The upload works fine, but the callback now has no answer (the answer is undefined).
I don't know exactly what is the cause, but I think it is some kind of limitation of the dev environment.
If you want to download the fix, please check it out: https://github.com/felipelalli/ajax-upload/commits/3.9.1