AthenaeumAthenaeum
Packages
  • next
  • current
  • v9.x
  • v8.x
  • v7.x
  • v6.x
  • v5.x
  • v4.x
  • v3.x
  • v2.x
  • v1.x
Changelog
GitHub
Packages
  • next
  • current
  • v9.x
  • v8.x
  • v7.x
  • v6.x
  • v5.x
  • v4.x
  • v3.x
  • v2.x
  • v1.x
Changelog
GitHub
  • Version 6.x

    • Release Notes
    • Upgrade Guide
    • New to this...
    • Contribution Guide
    • Security Policy
    • Code of Conduct
    • Origin
  • ACL

    • Introduction
    • How to install
    • Setup
    • Permissions
    • Roles
    • Users
    • Cached Permissions
  • Audit

    • Audit
    • How to install
    • Setup
    • Recording
    • Events
  • Circuits

    • Circuits
    • How to install
    • Setup
    • Usage
    • Events
  • Collections

    • Collections
    • How to install
    • Summation

      • Summation Collection
      • Items Processor
  • Config

    • Configuration Loader
    • How to install
    • Setup
    • Load Configuration Files
    • Custom File Parsers
  • Console

    • Command and Schedule Registration
    • How to install
    • Setup
    • Commands
    • Schedules
  • Container

    • IoC Service Container
    • How to install
    • Container
    • List Resolver
  • Core

    • Athenaeum Core Application
    • How to install
    • Setup
    • Usage

      • Configuration
      • Service Providers
      • Service Container
      • Events
      • Caching
      • Logging
      • Console
      • Task Scheduling
      • Exception Handling
      • Extending Core Application
      • Testing
  • Database

    • Introduction
    • How to install
    • Models

      • Instantiatable
      • Sluggable
    • Query

      • Criteria (Query Filter)
  • Dto

    • Data Transfer Object (DTO)
    • How to install
    • Create Interface
    • Implement DTO
    • How to use
    • Populate
    • Export
    • Json
    • Serialization
    • Nested DTOs
    • Array DTO
  • ETags

    • ETags
    • How to install
    • Setup
    • Usage
    • Generators

      • Default Generator
      • Custom Generator
    • Eloquent Models
    • Macros
  • Events

    • Register Listeners and Subscribers
    • How to install
    • Setup
    • Listeners
    • Subscribers
  • Filters

    • Search Filter Utilities
    • Prerequisites
    • How to install
    • Setup
    • Processor
    • Filters Builder
    • Predefined Resources

      • Search Processor
      • Sorting Processor
      • Constraints Processor
      • Matching Processor
    • Tip: Create a base builder
  • Flysystem

    • Introduction
    • Database Adapter

      • Introduction
      • How to install
      • Setup
      • Data Deduplication
      • MIME-Type Detection
  • Http

    • Api

      • Http API
      • How to install
      • Setup
      • Resources

        • Introduction
        • Timestamps
        • Self-Link
        • Relations
        • Registrar
      • Middleware

        • Introduction
        • Request Must Be Json
        • Capture Fields To Select
    • Clients

      • Http Clients
      • How to install
      • Setup
      • Basic Usage
      • Available Methods

        • Fluent Api
        • Protocol Version
        • Base Uri
        • Http Method and Uri
        • Headers
        • Accept & Content-Type
        • Authentication
        • Http Query
        • Payload Format
        • Payload
        • Attachments
        • Cookies
        • Response Expectations
        • Middleware
        • Conditions
        • Criteria
        • Redirects
        • Timeout
        • Debugging
        • Logging
        • Driver Options
        • Driver
      • Http Query Builder

        • Introduction
        • Select
        • Where
        • Dates
        • Include
        • Pagination
        • Sorting
        • Raw Expressions
        • Custom Grammar
    • Cookies

      • Http Cookies
      • How to install
      • Usage
    • Messages

      • Http Messages
      • How to install
      • Serializers
  • Maintenance

    • Modes

      • Maintenance Modes
      • How to install
      • Setup
      • Basic Usage
      • Available Drivers
  • Mime Types

    • MIME-Types
    • How to install
    • Setup
    • Usage
    • Drivers

      • Available Drivers
      • File Info
  • Properties

    • Properties Overload
    • How to install
    • Usage
    • Naming Convention
    • Properties Visibility
  • Redmine

    • Redmine Api Client
    • How to install
    • Setup
    • General Usage

      • Supported Operations
      • Fetch list of resources
      • Find
      • Fetch
      • Create new record
      • Update existing record
      • Delete existing record
      • Relations
    • Available Resources

      • Predefined Resources
      • Attachments
      • Enumerations
      • Issue Relations
      • Users
      • User Groups
      • Roles
      • Project Memberships
      • Versions (Milestones)
      • Issue Categories
      • Trackers
  • Service

    • Service Registrar
    • How to install
    • How to use
  • Streams

    • Streams
    • How to install
    • Setup
    • How to use

      • Introduction
      • Open and Close
      • Raw Resource
      • Seeking
      • Reading
      • Writing
      • Size
      • Truncate
      • Flush
      • Hash
      • MIME-Type
      • Output
      • Locking
      • Transactions
      • Meta
      • Misc
  • Support

    • Introduction
    • How to install
    • Laravel Aware-of Helpers

      • How to use
      • Enforce Via Interface
      • Custom Default
      • Pros and Cons
      • Available Helpers
    • Aware-of Properties

      • Generator
      • Available Aware-of Helpers
    • Live Templates
  • Testing

    • Introduction
    • How to install
    • Test Cases
    • Testing Aware-of Helpers
  • Utils

    • Introduction
    • How to install
    • Array
    • Duration
    • Json
    • Math
    • Memory
    • Method Helper
    • Invoker
    • Populatable
    • String
    • Version
  • Validation

    • Introduction
    • How to install
    • Setup
    • Rules

      • Alpha-Dash-Dot
      • Semantic Version
You are viewing documentation for an outdated version. It is no longer supported!

Response Expectations

The Http Client offers the possibility to "assert" a response's Http Status Code, headers or payload, should you require it. In this section, the expect() method is introduced.

  • Status Code Expectations
    • PSR-18
    • Expect Http Status Code
    • Otherwise Callback
    • Range of Status Codes
  • Advanced Expectations
    • Validate Headers or Payload
    • Multiple Expectations
    • Custom Response Expectation Classes
    • Response Manipulation

Status Code Expectations

PSR-18

Caution

When applying response expectations, the PST-18: HTTP Client's is no longer upheld. The standard recommendation states the following:

"[...] A Client MUST NOT treat a well-formed HTTP request or HTTP response as an error condition. For example, response status codes in the 400 and 500 range MUST NOT cause an exception and MUST be returned to the Calling Library as normal. [...]"

This client offers you way to react to status codes, e.g. by throwing exceptions. This mechanism is entirely optional. In other words, as a developer you have to decide whether to make use of this mechanism, or not.

Expect Http Status Code

In order to assert that a received response has a specific Http Status Code, e.g. 200 OK, state your expected/desired status code, as the first argument for the expect() method.

use Teapot\StatusCode;

$response = $client
        ->expect(StatusCode::OK)
        ->get('/users');

If the received response's status code does not match, then an ExpectationNotMetException will be thrown.

Otherwise Callback

The ExpectationNotMetException is thrown when the expected status code does not match the received status code. However, this is only intended as a "boilerplate" exception. Most likely, you want to throw your own exception. Therefore, when you provide a callable as the second argument, then the ExpectationNotMetException is not thrown. The provided callback is invoked instead.

use Teapot\StatusCode;
use Aedart\Contracts\Http\Clients\Responses\Status;
use Acme\Exceptions\BadResponse;

$response = $client
        ->expect(StatusCode::OK, function(Status $status){
            throw new BadResponse('Bad response received: ' . (string) $status);
        })
        ->get('/users');

The callback argument is provided with the following arguments, in the given order:

  • Status: A wrapper for the received response's http status code and phrase.
  • ResponseInterface: The received response (PSR-7).
  • RequestInterface: The sent request (PSR-7).

Range of Status Codes

The expect() method also accepts a range of status codes. If the received status code matches one of the expected codes, then the response is considered valid. Should it not match, then either the ExpectationNotMetException is thrown, or the callback is invoked (if provided).

use Teapot\StatusCode;
use Aedart\Contracts\Http\Clients\Responses\Status;
use Acme\Exceptions\BadResponse;

$response = $client
        ->expect([StatusCode::OK, StatusCode::NO_CONTENT], function(Status $status){
            throw new BadResponse('Bad response received: ' . (string) $status);
        })
        ->get('/users');

Advanced Expectations

Validate Headers or Payload

In situations when you need to assert more than the received status code, then you can provide a callable as the first argument. Doing so, will allow you to perform whatever validation logic you may require, upon the received response.

use Aedart\Contracts\Http\Clients\Responses\Status;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Acme\Exceptions\BadResponse;

$response = $client
        ->expect(function(
            Status $status,
            ResponseInterface $response,
            RequestInterface $request
        ){
            if( ! $response->hasHeader('user_id')){
                throw new BadResponse('Missing user id');
            }
            
            // ... other response validation - not shown...

            if( ! $status->isSuccessful()){
                throw new BadResponse('Bad response received: ' . (string) $status);
            }
        })
        ->get('/users');

Multiple Expectations

There is no limit to the amount of expectations that you may add for a response. Thus, you can add multiple expectations via the the expect() method. They will be executed in the same order as you add them.

use Aedart\Contracts\Http\Clients\Responses\Status;
use Psr\Http\Message\ResponseInterface;
use Teapot\StatusCode;
use Acme\Exceptions\BadResponse;

$response = $client
        // Expect status code...
        ->expect(StatusCode::OK, function(Status $status){
            throw new BadResponse('Bad response received: ' . (string) $status);
        })
    
        // Expect http header...
        ->expect(function(Status $status, ResponseInterface $response){
            if( ! $response->hasHeader('user_id')){
                throw new BadResponse('Missing user id');
            }
        })

        // Expect payload...
        ->expect(function(Status $status, ResponseInterface $response){
            $content = $response->getBody()->getContents();
            $response->getBody()->rewind();

            $decoded = json_decode($content);
            if(empty($decoded) || json_last_error() !== JSON_ERROR_NONE){
                throw new BadResponse('Payload is invalid');
            }
        })
        ->get('/users');

Array of Callbacks

If you expectations start to get bulky or lengthy, then you should extract them into their own methods. You can add them via an array, using the withExpectations() method. How you choose to extract expectation logic, is entirely up to you.

$response = $client
        ->withExpectations([
            [$this, 'expectOkStatus'],
            $expectUserIdCallback,
            // ...etc
        ])
        ->get('/users');

Custom Response Expectation Classes

You may also extract your expectation logic into a separate class, if you wish so. Simply extend the ResponseExpectation class and implement the expectation() method. The benefit of doing so, is that you can encapsulate more complex response validation logic. For instance, you can use Laravel's Validator to perform validation of a response's payload.

use Aedart\Http\Clients\Requests\Builders\Expectations\ResponseExpectation;
use Aedart\Contracts\Http\Clients\Responses\Status;
use Aedart\Utils\Json;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Teapot\StatusCode;
use Acme\Exceptions\UserWasNotCreatedException;
use Illuminate\Support\Facades\Validator;

class UserWasCreated extends ResponseExpectations
{
    public function expectation(
        Status $status,
        ResponseInterface $response,
        RequestInterface $request
    ): void {
        $payload = Json::decode($response->getBody()->getContents(), true);
        $validator = Validator::make($payload, [
            'uuid' => 'required|uuid',
            'name' => 'required|string'
        ]);    

        if ($validator->fails()) {
            throw new UserWasNotCreatedException();
        }
    }
}

// --------------------------------------- /
// Use expectation when you send your request
$response = $client
        ->expect(new UserWasCreated())
        ->post('/users', [ 'name' => 'John Snow' ]);

Response Manipulation

The expect() method is not design nor intended to manipulate the received response. This falls outside the scope of the given method. It's only purpose is to allow status code and response validation.

If you require a way to modify the incoming response or perhaps the outgoing request, then consider using custom middleware.

Edit page
Last Updated: 16/02/2023, 09:10
Contributors: Alin Eugen Deac, alin
Prev
Cookies
Next
Middleware