menu

Questions & Answers

Response::json() sending html instead of json

Using Laravel 4, return Response::json(array('foo' => 'bar')) used in one controller will return actual application/json, whereas in another controller, for another action, it will return text/html. Both actions are called via Ajax.

In the faulty controller, I tried to force the content-type with this snippet:

[...]

$response = Response::json($data);
$response->header('Content-Type', 'application/json');
$response->header('Content-Foo', 'Bar'); // test if additional headers are really set
Log::info($response);
return $response;

... while working controller returns correct Json response with:

return Response::json($data);

In both, $data is an array (tested).

Logged response from the faulty controller is:

HTTP/1.0 200 OK
Cache-Control: no-cache
Content-Foo:   Bar
Content-Type:  application/json
Date:          Wed, 17 Sep 2014 10:55:03 GMT

But received response (in Firebug / DevTools) is:

Connection  Keep-Alive
Content-Type    text/html
Date    Wed, 17 Sep 2014 10:55:03 GMT
Keep-Alive  timeout=5, max=93
Server  Apache/2.2.25 (Unix) mod_ssl/2.2.25 OpenSSL/0.9.8y DAV/2 PHP/5.5.3
Transfer-Encoding   chunked
X-Powered-By    PHP/5.5.3

I tried to directly return Response::json(array('foo' => 'bar')) at the beginning of the faulty controller action but it still sends the response as text/html.

I would like to know why would the content-type switch from application/json to text/html for no reason? And why the mock header isn't in the received response?

-- EDIT --

The problem seems to be located around the validator.

public function faultyAction()
{
    if(!Request::ajax()) App::abort(405);

    $validator = Validator::make(
        array('trackfile' => Input::file('trackfile')),
        array('trackfile' => 'required|audio')); // audio is a custom validator

    if($validator->fails())
    {
        Log::info('validation failed!');
        return Response::json(array('code' => 1, 'message' => 'File validation has failed.'));
    } 
    else
    {
        Log::info('validation passed!');
        return Response::json(array('code' => 0, 'filename' => 'test'));
    }
}

... returns text/html response while validation passed.

public function faultyAction()
{
    if(!Request::ajax()) App::abort(405);

    $validator = Validator::make(
        array('trackfile' => Input::file('trackfile')),
        array('trackfile' => 'required|audio')); // audio is a custom validator

    return Response::json(array('code' => 0, 'filename' => 'test'));
}

... returns application/json.

How comes the same response is returned with different content-type depending on where it is called in the code?

May it come from $validator->fails() (even if nothing seems to alter the headers or to print something in the Laravel's Validator.php code)?

Comments:
2023-01-21 23:01:21
Did you display the response of the request somewhere? If you have an error it is possible that the error is displayed instead of what your expect. In this case PHP override the Content-Type header to text/html
2023-01-21 23:01:21
Please re-check the $data contents that you are providing to your faulty controller.
2023-01-21 23:01:21
@Maskime it's a XHR so I get the response in Firebug. I log the response in Laravel before it's being sent back, and I get it after in Firebug (see both responses). @justrohu I replaced my original data with $data for reading purpose, but actual data array is passed raw to Response::json() so I don't think the problem comes from this.
2023-01-21 23:01:21
I edited the post, with new informations and tests.
2023-01-21 23:01:21
tests the content type from the response object before returning it in the controller. If it's ok it means something changes it after the return, could be an after filter in your routes.php targeting that route.
2023-01-21 23:01:21
@elfif I did (see the first post). The logged content-type is application/json, and there's no after filter in my routes.php. The validator must somehow send something, do you agree? Also, no relevant log in PHP/Apache.
2023-01-21 23:01:21
I don't really know, for your tests looks like the response returned from the controller is fine but it's not anymore when it hits your browser .... you should check that : laravel-recipes.com/recipes/54 , it tells you how to intercept the response in a callback named App::after() and check his content type. Plus check also this : laravel-recipes.com/recipes/52, the running steps show you everything that happens after the controller action is returned
2023-01-21 23:01:21
Just saw that there's a space (" ") before the actual json data returned by the faulty action. I'm pretty sure that it's what is causing the trouble. Now I have to find where it comes from.
2023-01-21 23:01:21
try that : >$response = Response::make($contents, $statusCode); $response->header('Content-Type', $value); return $response;
Answers(2) :

Hi basically using Response::json() with an array of data as parameter is enough. Defining again the Content-type header is useless, Response::json is supposed to set it right by default.

I'm creating json responses on my project right now and so far it went fine. Just to be clear the faulty controller returns the good value with a wrong content-type header, right ? Can you try again using just Response::json and can you tell which exact laravel version are you using ?

Hi again i'm editing my first answer based on your replies : you should test something like this :

$json = json_encode($yourArray);

// first check the $json variable with var_dump() oro Log::info() to see if you have the space problem. Then you can create the response.

$response = Response::make($json, 200);

$response->header('Content-Type', 'application/json');

return $response;

Hope it helps...

Comments:
2023-01-21 23:01:21
Yes it should work — and it does for one controller, but not the second. That's why I tried to force the content-type. I tried to directly return Response::json(array('foo' => 'bar')) at the beginning of the faulty controller action but it still sends the response as text/html, and no error is logged by Laravel. Framework version is 4.2.9.
2023-01-21 23:01:21
I think I located the problem, but can't find out why it acts like that. See my edit in the first post.

It may not be the case for OP, but this is a best search result for json response sent as text/html. I just spent considerable amount of time debugging this issue in Laravel 9 with php 8.2

One possible reason for wrong content type is hidden somewhere in php magic goo. In another words, how it handles sending response headers.

If you have whitespace anywhere before or after php opening or closing tag, respectively, the headers will be sent and you cant modify them anymore. This is why it is recommended to not use php closing tag in multi file applications, but this does not help for whitespace before opening tag:


 <?php

Now if your framework, like Laravel, checks with headers_sent(), before modifying headers for you, the result is, drumm roll: text/html, with no errors anywhere.

To identify the problem, try setting headers yourself, with header(), somewhere at the end of call stack. It should then fail and tell you in which file the headers are already sent. For me the culprit was hidden in one of the lang files.