Skip to main content

Request

Introduction​

Request wraps the incoming request for both AJAX and REST transports. Type-hint it on a controller method and the router injects it:

use YourPlugin\Deps\BitApps\WPKit\Http\Request\Request;

public function update(Request $request)
{
$id = $request->id;
}

Reading input​

Attributes are readable as properties, as array keys, or through accessor methods — Request implements ArrayAccess:

$request->title; // property access
$request['title']; // array access
$request->get('title'); // with an optional default
$request->input('title', 'untitled');

isset($request->title); // presence check
$request->has('title');

Bulk access:

$request->all(); // every attribute
$request->except('password'); // everything but the named keys
$request->files(); // uploaded files
$request->queryParams(); // query string only
$request->body(); // raw body

Request metadata:

$request->method(); // GET, POST, ...
$request->contentType();
$request->getRoute(); // the matched route
warning

Reading an attribute returns it unsanitized. Run input through validation — with a sanitize: rule — before storing it or echoing it back.

Client IP and device​

$request->ip(); // client address
$request->device(); // parsed user agent

ip() returns REMOTE_ADDR unless that address is a configured trusted proxy, in which case forwarded headers are honoured. Configure proxies once during boot:

use YourPlugin\Deps\BitApps\WPKit\Http\Request\Request;

Request::setTrustedProxies(['10.0.0.0/8', '2001:db8::/32']);
warning

Without this configuration X-Forwarded-For is ignored, which is the safe default — the header is trivially spoofed. Never add a proxy you do not control, and never rate-limit or allowlist on an unconfigured forwarded address.

The detection classes live in Http\Detection (ClientIpResolver, UserAgent) as of wp-kit 2.4.0; Request::ip() and device() are unchanged.

Validating​

validate() takes a rules array, and returns only the fields that passed, after sanitization:

$validated = $request->validate([
'id' => ['required', 'integer'],
'title' => ['required', 'string', 'sanitize:text'],
]);

On failure it answers immediately with a 422 and this body, so nothing after the call runs:

{
"status": "error",
"code": "VALIDATION",
"data": { "title": ["The title field is required."] }
}

Custom messages and attribute labels are the second and third arguments:

$validated = $request->validate(
['title' => ['required']],
['title.required' => 'Give the tag a name.'],
['title' => 'Tag name']
);

Uploaded files are merged into the attributes before validation, so file fields validate like any other field.

Form requests​

When a rule set is reused across actions — or is simply long — move it into a class under backend/app/HTTP/Requests. Extend Request and return the rules from rules():

namespace YourPlugin\HTTP\Requests;

if (!defined('ABSPATH')) {
exit;
}

use YourPlugin\Deps\BitApps\WPKit\Http\Request\Request;

class ConnectionStoreRequest extends Request
{
public function rules()
{
return [
'app_slug' => ['required', 'string', 'sanitize:text'],
'auth_type' => ['required', 'string', 'sanitize:text'],
'connection_name' => ['required', 'string', 'sanitize:text'],
'encrypt_keys' => ['nullable', 'array'],
'auth_details' => ['required', 'array'],
];
}
}

Type-hint the form request instead of Request on the controller method. The router builds it, runs authorize(), then validates rules() before the action executes — a failure short-circuits with an error response and your method never runs:

public function store(ConnectionStoreRequest $request)
{
$data = $request->all(); // already validated and sanitized
}

Add authorize() to gate the request on a capability:

class ConnectionStoreRequest extends Request
{
public function authorize()
{
return current_user_can('manage_options');
}

public function rules()
{
return [/* ... */];
}
}

Returning false answers with "You are not authorized to access this endpoint".

Name the class after the action it serves — ConnectionStoreRequest, ConnectionUpdateRequest, WebhookIndexRequest — so one request class maps to one route.