Developers

Responses

Everything a hub sends back is attached to one object. The Response facade resolves Hubzero\Http\Response, which extends Symfony's Response and adds output compression and a chainable header(). The application creates it during boot, the template fills its content, and the application sends it — so in the ordinary case you never touch it.

When you actually need it

Four cases, and outside them you should not be touching the response:

  • An AJAX task that returns JSON instead of a page.
  • A feed or export — CSV, iCalendar, an XML dump — where you set the content type and write the body yourself.
  • A system plugin rewriting the finished page on onAfterRender.
  • Middleware that must send something before the application would.

Serving a file off disk is not one of them. Use Hubzero\Content\Server, which handles disposition, length and byte ranges.

Getting at it

use Response;

Response::header('Content-Type', 'application/json');

or, equivalently, from the container:

$response = App::get('response');
$response->header('Content-Type', 'application/json');

Headers

header($key, $values, $replace = true) sets a header and returns the response, so several can be chained:

$response = App::get('response');

$response->header('Content-Type', 'application/json')
         ->header('X-Total-Count', $total)
         ->header('Cache-Control', 'no-store');

The most common use by far is switching the content type for an AJAX task, then echoing the payload and ending the request:

public function statusTask()
{
    Response::header('Content-Type', 'application/json');

    echo json_encode(array('state' => $booking->get('state')));

    App::close();
}

App::close() calls exit(). Nothing after it runs, and nothing else gets appended to the body.

Content

setContent($output) replaces the body; it takes a string. getContent() reads it back. The site template's output is set here at the end of the render stage, so a system plugin listening on onAfterRender can read the finished page, alter it, and set it back:

public function onAfterRender()
{
    $body = App::get('response')->getContent();

    App::get('response')->setContent(str_replace($from, $to, $body));
}

Compression and sending

compress($value) marks the response for gzip. The application turns it on from the gzip global configuration option; when set, send() checks the browser's Accept-Encoding, confirms zlib is loaded and that headers have not gone out, and compresses the body on the way past. It is a no-op otherwise, so nothing breaks on a server without zlib.

send($flush = false) writes the headers and then the body. With $flush true it also closes the connection — fastcgi_finish_request() where that exists — so slow work can carry on after the browser has the page. The application calls this once, at the end of the request; a component calling it is almost always a mistake.

Serving a file off disk is a separate path with its own headers and byte ranges. Use Hubzero\Content\Server, not the response object.

Redirects

A redirect is a response too, of a different class. Hubzero\Http\RedirectResponse extends Symfony's, and its send() resolves the target first: an index.php-relative URL is prefixed with Request::base(), line breaks are stripped, and a URL with no scheme is made absolute against the current scheme, host and path. That is why a component can redirect to a route result without worrying whether it came back absolute.

App::redirect() builds one, attaches the request, optionally queues a message, sends it, and exits:

App::redirect(
    Route::url('index.php?option=com_support'),
    Lang::txt('COM_SUPPORT_TICKET_SAVED'),
    'success'
);

See redirect for the details, including the message types and what happens to the code after the call.

Status codes and errors

Do not set an error status on the response by hand. App::abort($code, $message) throws the exception the error handler is looking for, and the error template renders the right page:

Code Exception thrown
403 Hubzero\Error\Exception\NotAuthorizedException
404 Hubzero\Error\Exception\NotFoundException
405 Hubzero\Error\Exception\MethodNotAllowedException
anything else Hubzero\Error\Exception\RuntimeException with that code
if (!$row->get('id'))
{
    App::abort(404, Lang::txt('COM_BLOG_ERROR_ENTRY_NOT_FOUND'));
}

Throwing one of those exceptions yourself has the same effect; App::abort() is the shorter way to say it.

Rewritten and checked against 2.4-main @ 348f0057c2 on 2026-09-10.