I have written a controller in Laravel 4 to serve up static resources such as image files with some application-level tracking. I don't want people having to constantly hit the web server for each image, though, so I'm trying to set a Cache-Control header. I'm at my wit's end here, it's just plain not working, and I cannot figure out why. Can someone please take a look at the following and tell me what I'm doing wrong?
$headers = array(
'Content-Type' => 'image/png',
'Cache-Control' => 'public, max-age=300',
'Content-Length' => filesize($file),
'Expires' => date('D, d M Y H:i:s ', time() + 300).'GMT',
);
return Response::stream(function() use ($file) {
readfile($file); }, 200, $headers);
When I access the file, it works almost perfectly. The image is displayed, and when I go in and inspect the element using Chrome or Firefox, I can see that the Expires header is set correctly (when I change it and force a hard reload, it resets it appropriately). But no matter what I try, the Cache-Control header is always set to "no-cache, private". I've tried using the following, all to no avail:
$headers['Cache-Control'] = 'public, max-age=300'; // Original setting
$headers['cache-control'] = 'public, max-age=300'; // Case-sensitive?
$headers['Cache-Control'] = 'max-age=300';
$headers['Cache-Control'] = 'private, max-age=300';
$headers['Cache-Control'] = 'max-age=300, public';
$headers['Cache-Control'] = 'max-age=300, private';
But like I said, no matter what I set it to, it always returns a response header showing Cache-Control: no-cache, private.
What am I doing wrong? How can I get my image and binary files returned via this controller to cache? Has Laravel 4 for some reason hard-coded the Cache-Control header in all responses?
See this issue: https://github.com/symfony/symfony/issues/6530 and this line: https://github.com/symfony/symfony/blob/2.4/src/Symfony/Component/HttpFoundation/StreamedResponse.php#L87
It suggests to use BinaryFileResponse instead. Something like this:
$response = new Symfony\Component\HttpFoundation\BinaryFileResponse($file);
$response->setContentDisposition('inline');
$response->setTtl(300);
return $response;
//Or with setting the headers manually
return new Symfony\Component\HttpFoundation\BinaryFileResponse($file, 200, $headers, true, 'inline');
Now you can set the Cache-Control headers.
Or using the Response::download() facade:
return Response::download($file)->setTtl(300)->setContentDisposition('inline');
return Response::download($file, null, $headers)->setContentDisposition('inline');
Related
I'm building an application on Laravel 8 and hosting the app on AWS lambda. I want to compress the response with gzip to HTTP request. I had interaction with AWS support and they asked me to add headers with 'Content-Encoding' => 'gzip', and also add isBase64Encoded to true. To achieve this I tried finding out the solution over the internet I found out a documentation for Laravel Vapor which handles this perfectly. They made an middleware which adds these attributes:
$response = $next($request);
if (in_array('gzip', $request->getEncodings()) && function_exists('gzencode')) {
$response->setContent(gzencode($response->getContent(), 9));
$response->headers->add([
'Content-Encoding' => 'gzip',
'X-Vapor-Base64-Encode' => 'True',
]);
}
return $response;
My current header looks like:
Since I'm not using Laravel Vapor So I need to put isBase64Encoded into the header:
I tried executing this via:
$response = $next($request);
if (in_array('gzip', $request->getEncodings()) && function_exists('gzencode')) {
$response->setContent(gzencode($response->getContent(), 9));
$response->headers->add([
'Content-Encoding' => 'gzip',
]);
$response->isBase64Encoded = true;
dd($response);
}
return $response;
As you can see I'm trying to add attribute by $response->isBase64Encoded = true;, once implemented the header changes to something unexpected:
Also the content is not encoded. Help me out in adding the isBase64Encoded attributes in response
I have created custom REST API and in response i am getting old data result. I am using WP Engine and cache is enabled in the wp-config.php file. I don't want to disable cache, just want my api respone without cached data result. I tried to set header Cache-Control: no-cache, must-revalidate, max-age=0, ob_flush(), clearstatcache() but still having same issue. Also, in my ajax request when i return html data, it shows me old entries. I cross checked in database, it have the correct values but response of ajax and api request are the previous one. When i clear cache from the WP Engine settings on Dashboard then i am getting correct result in ajax and api response.
Here is my code api code
register_rest_route( PLUGIN_DIRNAME.'/v1', '/data/status', array(
'methods' => 'GET',
'callback' => array( $this, 'get_status' )
));
public function get_status($request){
$response = array();
$progress = get_option('progress_percentage');
$name = get_option('form_submission');
$response["progress"] = $progress;
$response["form_id"] = $name;
if($progress == '100'){
$response["submission_status"] = __( 'successfull');
}
$result = new WP_REST_Response( $response, 200);
$result->set_headers( array(
'Cache-Control' => 'no-cache, must-revalidate, max-age=0'
) );
return $result;
}
In ajax response :
function ajax_response(){
$progress = get_option('progress_percentage');
if($progress == "100"){
$name = get_option('form_submission');
$submission_status = "<div class='complete'>Form submission successfull.</div>";
}
return array(
'message' => get_option('submission_stage'),
'html' => $submission_status,
);
}
Can anyone help me with this issue. I am struggling with this issue from 4 hours. It would be great help if anyone suggestion made my code working.
Thanks in Advance.
Finally i managed to get this issue fixed. I am posting here if in case anyone face the similar issue. I used wp_cache_flush() in the begining of api callback function and in the ajax request callback function. It fixed the cache issue for me.
The problem is the same as described here Laravel Excel Download using Controller
But I just can not believe that there is no method to deal with Excel downloads in Laravel without using another resource. I was already able handle the instant downloads in controller with response() for PDFs.
Mabybe the headers are wrong? My code:
public function getFile($file) {
$path = storage_path('app/excel/exports/' . $file);
$headers = array('Content-Type' => File::mimeType($path));
return response()->download($path, $file, $headers);
}
So the excel file is created and saved correctly in my storage folder (happens before the code above). Then I use an axios.get method to download the file with the function above.
Headers I am getting:
Accept-Ranges:bytes
Cache-Control:public
Connection:keep-alive
Content-Disposition:attachment; filename="test_file.xlsx"
Content-Length:7066
Content-Type:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
But whatever I do or try to change the download just won't start.
You can try with these two headers.
return response()->download($path, $file, [
'Content-Type' => 'application/vnd.ms-excel',
'Content-Disposition' => "attachment; filename='Report.xls'"
]);
Thanks,
I manged to solve it.
It seems to be not possible to solve this with a simple axios.get request to the route. Instead I needed to open the route directly with a link.
Did not work with:
HTML
<button #click="downloadExcel()">Download Table as Excel File</button>
downloadExcel() {
axios.get('/customers/export');
}
Simple solution (instead of just using axios.get):
<a :href="/customers/export"><button>Download Table as Excel File</button></a>
So it would also be possible to open the route after the axios request with:
downloadExcel() {
let newWindow = window.open();
axios.get('/customers/export')
.then(response => {
newWindow.location = 'http://' + window.location.hostname + '/customers/export';
});
}
I have seen many examples on how to set headers on a response but I cannot find a way to inspect the headers of a response.
For example in a test case I have:
public function testGetJson()
{
$response = $this->action('GET', 'LocationTypeController#index', null, array('Accept' => 'application/json'));
$this->assertResponseStatus(200);
//some code here to test that the response content-type is 'application/json'
}
public function testGetXml()
{
$response = $this->action('GET', 'LocationTypeController#index', null, array('Accept' => 'text/xml'));
$this->assertResponseStatus(200);
//some code here to test that the response content-type is 'text/xml'
}
How would I go about testing that the content-type header is 'application/json' or any other content-type? Maybe I'm misunderstanding something?
The controllers I have can do content negation with the Accept header and I want to make sure the content type in the response is correct.
Thanks!
After some digging around in the Symfony and Laravel docs I was able to figure it out...
public function testGetJson()
{
// Symfony interally prefixes headers with "HTTP", so
// just Accept would not work. I also had the method signature wrong...
$response = $this->action('GET', 'LocationTypeController#index',
array(), array(), array(), array('HTTP_Accept' => 'application/json'));
$this->assertResponseStatus(200);
// I just needed to access the public
// headers var (which is a Symfony ResponseHeaderBag object)
$this->assertEquals('application/json',
$response->headers->get('Content-Type'));
}
While not specifically about testing, a nice way of getting at Laravel's response object is to register a 'Finish' callback. These are executed just after the response is delivered, right before the app closes. The callback receives the request and the response objects as arguments.
App::finish(function($request, $response) {
if (Str::contains($response->headers->get('content-type'), 'text/xml') {
// Response is XML
}
}
Take a look at the laravel documentation
Request::header('accept'); // or
Response::header('accept');
Retrieving A Request Header
$value = Request::header('Content-Type');
Another way would be to use getallheaders() :
var_dump(getallheaders());
// array(8) {
// ["Accept"]=>
// string(63) "text/html[...]"
// ["Accept-Charset"]=> ...
For debugging purposes You could simply use this:
var_dump($response->headers);
I'm having trouble getting SOAP compression working and after reading the documentation for the service I've realised it's because they've decided to ignore the HTTP standard and do their own thing.
Basically I need to be able to set the content-encoding header to:
Content-Encoding: accept-gzip,accept-deflate
Rather than using Accept-Encoding, they've decided to use content-encoding which is incredibly annoying and there's zero chance of them changing it.
If I set the compression option in the SOAP client it sends
Content-Encoding: gzip
Which then causes the SOAP client to throw an exception saying "Unknown Content-Encoding".
So is it possible to alter the http request that gets sent using the default php SOAP client?
Maye this help somebody:
$mode = array (
'soap_version' => 'SOAP_1_1', // use soap 1.1 client
'keep_alive' => true,
'trace' => 1,
'encoding' =>'UTF-8',
'compression' => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP | SOAP_COMPRESSION_DEFLATE,
'exceptions' => true,
'cache_wsdl' => WSDL_CACHE_NONE,
'stream_context' => stream_context_create (
array (
'http' => array('header' => 'Content-Encoding: XXXXXXX'),
)
)
);
// initialize soap client
$client = new LoEnvioSoapClient ( $this->wsdl, $mode );
Why not set your own soap headers? If needed, extend the default class and implemented your own version of it.
It's possible to change the HTTP headers used but only by extending PHP's native SoapClient class.
Something like this:
class MySoapClient extends \SoapClient
public function __doRequest($request, $location, $action, $version, $one_way = 0)
{
//Send the HTTP request and get the response using curl or fsockopen,
//of course setting Content-Encoding to accept-gzip,accept-deflate.
//Also set Accept-Encoding to deflate
//Put the response in a variable called $response
//Set the headers used for this request; this is how you would do it if you used curl:
$this->__last_request_headers = curl_getinfo($curlHandle, CURLINFO_HEADER_OUT);
$this->__last_request = $request;
//decompress the response
$response = gzinflate( substr($response, 10, -8) );
return $response;
}
}
It seems that the OP is already aware of this, but here's a tip for others who may not be aware of this: to see the SOAP request as it would be sent out by PHP's native SoapClient class, set the 'trace' option to true:
$client = new \SoapClient($wsdlPath, array('trace'=>true));
Then, after executing your SOAP request you can do this to see the headers that were used:
var_dump( $client->__getLastRequestHeaders() );