Coder Social home page Coder Social logo

zendframework / zend-problem-details Goto Github PK

View Code? Open in Web Editor NEW
50.0 15.0 18.0 1.86 MB

Provides Problem Details for HTTP APIs (RFC 7807) support for PSR-7 applications.

License: BSD 3-Clause "New" or "Revised" License

PHP 100.00%
rest problem-details api psr-7 psr-15 php zend-framework zend-expressive

zend-problem-details's Introduction

Problem Details for PSR-7 Applications

Repository abandoned 2019-12-31

This repository has moved to mezzio/mezzio-problem-details.

Build Status Coverage Status

This library provides a factory for generating Problem Details responses, error handling middleware for automatically generating Problem Details responses from errors and exceptions, and custom exception types for PSR-7 applications.

Installation

Run the following to install this library:

$ composer require zendframework/zend-problem-details

Documentation

Documentation is in the doc tree, and can be compiled using mkdocs:

$ mkdocs build

You may also browse the documentation online.

zend-problem-details's People

Contributors

acelaya avatar akrabat avatar basz avatar bladeofsteel avatar breiteseite avatar froschdesign avatar geerteltink avatar michalbundyra avatar ncou avatar orkin avatar oussous-hassan avatar samsonasik avatar snapshotpl avatar svycka avatar weierophinney avatar xerkus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

zend-problem-details's Issues

ApiProblem crashes when trying to render malformed UTF-8 sequences

Malformed UTF-8 sequences cause zend-problem-details to throw the following error:

TypeError: Argument 3 passed to Zend\ProblemDetails\ProblemDetailsResponseFactory::generateResponse() must be of the type string, boolean given, called in /code/src/ProblemDetailsResponseFactory.php on line 302

/code/src/ProblemDetailsResponseFactory.php:351
/code/src/ProblemDetailsResponseFactory.php:302
/code/src/ProblemDetailsResponseFactory.php:255
/code/src/ProblemDetailsResponseFactory.php:272

The issue is, that json_encode() will return an false when called without the option JSON_PARTIAL_OUTPUT_ON_ERROR but with malformed UTF-8 data to encode.

We found this error because someone randomly inserted characters in the base64 values of the HTTP Basic Authorization header which lead base64_decode() to return malformed UTF-8 which led to ProblemDetails crashing because the malformed UTF-8 sequence was in the stack trace of the error report.

Code to reproduce the issue

See db3d795.

Expected results

  • ApiProblem should still be rendered

Actual results

TypeError thrown.

JSON_PRETTY_PRINT as default?

Should we serve problem details in json with JSON_PRETTY_PRINT flag? For debugging maybe it's useful, but in normal working it's add additional data to transfer.

My proposition is to enable this flag in debug mode or remove it in default settings.

prevents ErrorHadler listeners to be executed

I am tried to add logging to expressive application as described in documentation

Code to reproduce the issue

code from docs
https://docs.zendframework.com/zend-expressive/features/error-handling/#listening-for-errors
and this module added to route then just throw any exception at middleare with Accept: application/json header

Expected results

listeners to be called

Actual results

no listeners were called

ignote

(ignore; issue accidentally opened via API)

Problem with last Zend Expressive version and middleware

Hello, I tried this package with the last version of Zend Expressive 3.0.0alpha9 and I have a problem with both middleware : ProblemDetailsNotFoundHandler and ProblemDetailsMiddleware

Code to reproduce the issue

// I just followed the documentation and add the both middleware in my pipeline.php file

Expected results

A beautiful error message in json ๐Ÿ˜„

Actual results

TypeError raised in file /var/www/vendor/zendframework/zend-problem-details/src/ProblemDetailsResponseFactory.php line 197:
Message: Argument 3 passed to Zend\ProblemDetails\ProblemDetailsResponseFactory::__construct() must implement interface Psr\Http\Message\ResponseInterface or be null, instance of Closure given, called in /var/www/vendor/zendframework/zend-problem-details/src/ProblemDetailsResponseFactoryFactory.php on line 38
Stack Trace:
#0 /var/www/vendor/zendframework/zend-problem-details/src/ProblemDetailsResponseFactoryFactory.php(38): Zend\ProblemDetails\ProblemDetailsResponseFactory->__construct(true, NULL, Object(Closure), NULL, true)
#1 /var/www/vendor/zendframework/zend-servicemanager/src/ServiceManager.php(764): Zend\ProblemDetails\ProblemDetailsResponseFactoryFactory->__invoke(Object(Zend\ServiceManager\ServiceManager), 'Zend\\ProblemDet...', NULL)
#2 /var/www/vendor/zendframework/zend-servicemanager/src/ServiceManager.php(200): Zend\ServiceManager\ServiceManager->doCreate('Zend\\ProblemDet...')
#3 [internal function]: Zend\ServiceManager\ServiceManager->get('Zend\\ProblemDet...')
#4 /var/www/vendor/zendframework/zend-servicemanager/src/AbstractFactory/ConfigAbstractFactory.php(68): array_map(Array, Array)
#5 /var/www/vendor/zendframework/zend-servicemanager/src/ServiceManager.php(764): Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory->__invoke(Object(Zend\ServiceManager\ServiceManager), 'User\\Presentati...', NULL)
#6 /var/www/vendor/zendframework/zend-servicemanager/src/ServiceManager.php(200): Zend\ServiceManager\ServiceManager->doCreate('User\\Presentati...')
#7 /var/www/vendor/zendframework/zend-expressive/src/MiddlewareContainer.php(57): Zend\ServiceManager\ServiceManager->get('User\\Presentati...')
#8 /var/www/vendor/zendframework/zend-expressive/src/Middleware/LazyLoadingMiddleware.php(45): Zend\Expressive\MiddlewareContainer->get('User\\Presentati...')
#9 /var/www/vendor/zendframework/zend-stratigility/src/Next.php(52): Zend\Expressive\Middleware\LazyLoadingMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\Next))
#10 /var/www/src/OAuth/src/Middleware/ResourceServerMiddleware.php(56): Zend\Stratigility\Next->handle(Object(Zend\Diactoros\ServerRequest))
#11 /var/www/vendor/zendframework/zend-expressive/src/Middleware/LazyLoadingMiddleware.php(46): OAuth\Middleware\ResourceServerMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\Next))
#12 /var/www/vendor/zendframework/zend-stratigility/src/Next.php(52): Zend\Expressive\Middleware\LazyLoadingMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\Next))
#13 /var/www/vendor/zendframework/zend-stratigility/src/MiddlewarePipe.php(91): Zend\Stratigility\Next->handle(Object(Zend\Diactoros\ServerRequest))
#14 /var/www/vendor/zendframework/zend-expressive-router/src/Route.php(109): Zend\Stratigility\MiddlewarePipe->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#15 /var/www/vendor/zendframework/zend-expressive-router/src/RouteResult.php(122): Zend\Expressive\Router\Route->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#16 /var/www/vendor/zendframework/zend-expressive-router/src/Middleware/DispatchMiddleware.php(35): Zend\Expressive\Router\RouteResult->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#17 /var/www/vendor/zendframework/zend-expressive/src/Middleware/LazyLoadingMiddleware.php(46): Zend\Expressive\Router\Middleware\DispatchMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#18 /var/www/vendor/zendframework/zend-stratigility/src/MiddlewarePipe.php(78): Zend\Expressive\Middleware\LazyLoadingMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#19 /var/www/vendor/zendframework/zend-expressive-helpers/src/UrlHelperMiddleware.php(45): Zend\Stratigility\MiddlewarePipe->handle(Object(Zend\Diactoros\ServerRequest))
#20 /var/www/vendor/zendframework/zend-expressive/src/Middleware/LazyLoadingMiddleware.php(46): Zend\Expressive\Helper\UrlHelperMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#21 /var/www/vendor/zendframework/zend-stratigility/src/MiddlewarePipe.php(78): Zend\Expressive\Middleware\LazyLoadingMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#22 /var/www/vendor/zendframework/zend-expressive-helpers/src/BodyParams/BodyParamsMiddleware.php(72): Zend\Stratigility\MiddlewarePipe->handle(Object(Zend\Diactoros\ServerRequest))
#23 /var/www/vendor/zendframework/zend-expressive/src/Middleware/LazyLoadingMiddleware.php(46): Zend\Expressive\Helper\BodyParams\BodyParamsMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#24 /var/www/vendor/zendframework/zend-stratigility/src/MiddlewarePipe.php(78): Zend\Expressive\Middleware\LazyLoadingMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#25 /var/www/vendor/zendframework/zend-expressive-router/src/Middleware/ImplicitOptionsMiddleware.php(68): Zend\Stratigility\MiddlewarePipe->handle(Object(Zend\Diactoros\ServerRequest))
#26 /var/www/vendor/zendframework/zend-expressive/src/Middleware/LazyLoadingMiddleware.php(46): Zend\Expressive\Router\Middleware\ImplicitOptionsMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#27 /var/www/vendor/zendframework/zend-stratigility/src/MiddlewarePipe.php(78): Zend\Expressive\Middleware\LazyLoadingMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#28 /var/www/vendor/zendframework/zend-expressive-router/src/Middleware/ImplicitHeadMiddleware.php(87): Zend\Stratigility\MiddlewarePipe->handle(Object(Zend\Diactoros\ServerRequest))
#29 /var/www/vendor/zendframework/zend-expressive/src/Middleware/LazyLoadingMiddleware.php(46): Zend\Expressive\Router\Middleware\ImplicitHeadMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#30 /var/www/vendor/zendframework/zend-stratigility/src/MiddlewarePipe.php(78): Zend\Expressive\Middleware\LazyLoadingMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#31 /var/www/vendor/zendframework/zend-expressive-router/src/Middleware/MethodNotAllowedMiddleware.php(49): Zend\Stratigility\MiddlewarePipe->handle(Object(Zend\Diactoros\ServerRequest))
#32 /var/www/vendor/zendframework/zend-expressive/src/Middleware/LazyLoadingMiddleware.php(46): Zend\Expressive\Router\Middleware\MethodNotAllowedMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#33 /var/www/vendor/zendframework/zend-stratigility/src/MiddlewarePipe.php(78): Zend\Expressive\Middleware\LazyLoadingMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#34 /var/www/vendor/zendframework/zend-expressive-router/src/Middleware/RouteMiddleware.php(54): Zend\Stratigility\MiddlewarePipe->handle(Object(Zend\Diactoros\ServerRequest))
#35 /var/www/vendor/zendframework/zend-expressive/src/Middleware/LazyLoadingMiddleware.php(46): Zend\Expressive\Router\Middleware\RouteMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#36 /var/www/vendor/zendframework/zend-stratigility/src/MiddlewarePipe.php(78): Zend\Expressive\Middleware\LazyLoadingMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#37 /var/www/vendor/zendframework/zend-expressive-helpers/src/ServerUrlMiddleware.php(37): Zend\Stratigility\MiddlewarePipe->handle(Object(Zend\Diactoros\ServerRequest))
#38 /var/www/vendor/zendframework/zend-expressive/src/Middleware/LazyLoadingMiddleware.php(46): Zend\Expressive\Helper\ServerUrlMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#39 /var/www/vendor/zendframework/zend-stratigility/src/MiddlewarePipe.php(78): Zend\Expressive\Middleware\LazyLoadingMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#40 /var/www/vendor/zendframework/zend-stratigility/src/Middleware/ErrorHandler.php(137): Zend\Stratigility\MiddlewarePipe->handle(Object(Zend\Diactoros\ServerRequest))
#41 /var/www/vendor/zendframework/zend-expressive/src/Middleware/LazyLoadingMiddleware.php(46): Zend\Stratigility\Middleware\ErrorHandler->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#42 /var/www/vendor/zendframework/zend-stratigility/src/MiddlewarePipe.php(78): Zend\Expressive\Middleware\LazyLoadingMiddleware->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#43 /var/www/vendor/zendframework/zend-stratigility/src/Middleware/OriginalMessages.php(41): Zend\Stratigility\MiddlewarePipe->handle(Object(Zend\Diactoros\ServerRequest))
#44 /var/www/vendor/zendframework/zend-expressive/src/Middleware/LazyLoadingMiddleware.php(46): Zend\Stratigility\Middleware\OriginalMessages->process(Object(Zend\Diactoros\ServerRequest), Object(Zend\Stratigility\MiddlewarePipe))
#45 /var/www/vendor/zendframework/zend-stratigility/src/MiddlewarePipe.php(78):

May I have missed something ?

Being able to determine if only json or xml should be handled, instead of both.

I have run into this use case:

My application has a JSON API which is served under the /rest path, but also 3 or 4 more routes which are not part of this API context.

For the API I use "problem details" for the errors, but I also have some other error handling logic for the other routes.

Because of this, my middleware pipeline looks more or less like this:

// ...

$app->pipe(Zend\Stratigility\Middleware\ErrorHandler::class);
$app->pipe('/rest', Zend\ProblemDetails\ProblemDetailsMiddleware::class);

// More middlewares...

$app->pipe('/rest', Zend\ProblemDetails\ProblemDetailsNotFoundHandler::class);
$app->pipe(App\NotFoundHandler::class);

With this approach, most of the use cases work as expected, except when a request is performed to a not-found path which starts with /rest, proividing a non-JSON Accept header, but one that matches *+xml.

This usually happens when a request is performed from a browser, in which case the Accept has the value text/html,application/xhtml+xml,application/xml...

This is making the ProblemDetailsNotFoundHandler to generate an xml response, but I would expect/like it to be skipped and my custom NotFoundHandler (the second one) to be executed instead.

Proposal

In order to "solve" this, the first thing that comes to my mind is this approach:

Allowing to dynamically determine which "contexts" should be enabled for the module, as in "JSON only", "XML only" or "both".

I see that currently, the headers that determine if the ProblemDetailsMiddleware and the ProblemDetailsNotFoundHandler should act as error handlers, are hardcoded on ProblemDetailsResponseFactory::NEGOTIATION_PRIORITIES.

Since the three classes make use of that constant to call a Negotiator instance, maybe the whole logic could be wrapped into a helper which is injected in the three of them.

This helper could then get the "context" configuration or fallback to "both", keeping current behavior.

Let me know if you think this makes sense, or if you think there's any simpler way to achieve the same result.

In any case, I'm open to contribute a PR with the required changes.

Line return and cleaning function. Bug ?

Hi,

Very nice piece of code. I am using a part of your code, and i think i found a bug during one of my tests.

I tried to add a line return (chr(10) => \n) in the array key name to check if the function "cleanKeysForXml()" work correctly and i got an error.

I think the regex is using at the end the "." char for "Matches any character" but it doesn't take in consideration the lines terminators. So the Chr(10) is not replaced by an underscore in this cleaning function.

if you remove any line return character before calling the regex it seems to me it's more bulletproof.

Is this a bug or am i missing something ?

Allow to configure a default type map

Currently, when the ProblemDetailsResponseFactory::createResponse method is called without a type, it tries to infer it from the status, generating values like https://httpstatus.es/404 or https://httpstatus.es/500.

This mainly happens when the ProblemDetailsNotFoundHandler is hit, or the ProblemDetailsMiddleware catches an exception not implementing ProblemDetailsExceptionInterface.

If your API has a specific pattern when generating the types for other errors, it's a bit inconsistent that these two errors follow a different pattern.

It would be nice to be able to define a config map where you set the default type to be used for "every" status code when a value for type was not provided. Something like this:

<?php

return [

    'zend_problem_details' => [
        'default_type_fallbacks' => [
            404 => '/my-app/errors/resource-not-found',
            500 => '/my-app/errors/unknown',
        ],
    ],

];

Then, by injecting this map in the ProblemDetailsResponseFactory, the createTypeFromStatus method could be refactored to look like this:

private function createTypeFromStatus(int $status) : string
{
    return $this->defaultTypeFallbacks[$status] ?? sprintf('https://httpstatus.es/%s', $status);
}

So it would continue working the same when the config map is not defined, but every user would have control over the values generated for the type.

I'm open to implement this if you think it's useful.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.