Bad Request: `url` is required but missing - php

Hi Restler/Swagger friends,
I'm facing a problem when i trying to post a url (ex. /home/ahmad/) as follow:
{
"error": {
"code": 400,
"message": "Bad Request: `url` is required but missing."
},
"debug": {
"source": "Validator.php:26 at validate stage",
"stages": {
"success": [
"get",
"route",
"negotiate"
],
"failure": [
"validate",
"message"
]
}
}
}
my code for test is:
/**
* POST url
*
* #param string $url {#from url} url for test
*
* #return string
*/
function post_url($url) {
return $url;
}
I tried debugging the problem and discovered that url value is received as NULL before the Validator is applied
How i can solve such this problem?

I can see few problems with your approach
First, if you want to map a parameter to url you have to use {#from path} not {#from url}
Then if your variable is going to contain slashes they should ideally be mapped to query string or body as the slashes in the url path will be understood as many parameters by Restler
If you must accept it part of the url, you can use the wildcard routing as shown below
/**
* POST url
*
* #return string
*
* #url POST url/*
*/
function postUrl() {
return implode(',', func_get_args());
}

Related

OpenApi Annotations causing double slash

I'm using zircote/swagger-php to generated API documentation based on OpenApi annotations that I have added to existing code.
For the most part it's working fine, but I've noticed one strange bit. I have a method, indexAction(), that is supposed to return a list of items:
/**
* #OA\Get(
* path="\location",
* description="Retrieves a list of locations for the current organisation",
* method="indexAction",
* #OA\Response(
* response="200",
* description="List of locations",
* #OA\JsonContent(
* type="array",
* #OA\Items(ref="#/components/schemas/Location")
* ),
* #OA\XmlContent(
* type="array",
* #OA\Items(ref="#/components/schemas/Location")
* )
* )
* )
*/
public function indexAction()
{
After compiling the documentation json file, I open it and this action is listed as follows:
"paths": {
"\\location": {
"get": {
"summary": "The index action handles index/list requests; it should respond with a\nlist of the requested resources.",
"description": "Retrieves a list of locations for the current organisation",
"operationId": "2b36ea7d6a6e350fd6c7564bc908a25b",
"responses": {
"200": {
"description": "List of locations",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Location"
}
}
},
"application/xml": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Location"
}
}
}
}
}
}
}
},
For some reason the "path" is listed as \\location instead of, as I expect, \location - does anyone know why this is happening? I hope this is enough, if more code is required I can supply it.
\\ is just an escaped version of the \ character in JSON strings. That is, the actual value of the "\\location" string is \location.
However, URL paths use / forward slashes, and OpenAPI also requires that paths start with /. So you need to replace
path="\location",
with
path="/location",

API Platform: Can't get using a json data type with MySQL to work

I'm new to the API Platform and trying to get a json data attribute to persist to MySQL as a json object. I've looked over the API Platform documentation and googled for an answer, but didn't find anything that answers this.
My setup:
MySQL column is defined as:
`document` json DEFAULT NULL
I've defined a "MyEntity" PHP object and the json attribute is described as follows:
/**
* #var json
*
* #ORM\Column(type="json")
*/
private $document;
/**
* Get document.
*
* #return json
*/
public function getDocument()
{
return $this->document;
}
/**
* Set document.
*
* #param json $document
*
* #return MyEntity
*/
public function setDocument($document)
{
$this->document = $document;
return $this;
}
Now when up that URL that shows the default interface for MyEntity and execute a POST to create it using this json-ld (simplifying to leave out other columns):
{
"document": "{"test": "value"}"
}
I get a 400 Bad Request Error
{
"#context": "/contexts/Error",
"#type": "hydra:Error",
"hydra:title": "An error occurred",
"hydra:description": "Syntax error",
"trace": [
{
"namespace": "",
"short_class": "",
"class": "",
"type": "",
"function": "",
"file": "/Library/WebServer/Documents/test/vendor/symfony/symfony/src/Symfony/Component/Serializer/Encoder/JsonDecode.php",
"line": 78,
"args": []
},
...
I escaped the double quotes (which seems strange to me that it you would need to do this since it is a json itself that is describing another json object) like this:
{
"document": "{\"test\": \"value\"}"
}
and execute a POST again:
I get a 400 Bad Request Error with a different internal error:
{
"#context": "/contexts/Error",
"#type": "hydra:Error",
"hydra:title": "An error occurred",
"hydra:description": "Could not denormalize object of type AppBundle\\Entity\\json, no supporting normalizer found.",
"trace": [
{
"namespace": "",
"short_class": "",
"class": "",
"type": "",
"function": "",
"file": "/Library/WebServer/Documents/test/vendor/symfony/symfony/src/Symfony/Component/Serializer/Serializer.php",
"line": 295,
"args": []
},
...
So it look like API Platform doesn't recognize the JSON data type and is expecting a custom entity for it...doesn't seem right to me.
I've also looked at types for doctrine, which in my understanding, API Platform uses and found this info:
Some vendors have a native JSON type and Doctrine will use it if
possible and otherwise silently fall back to the vendor’s text type to
ensure the most efficient storage requirements. If the vendor does not
have a native JSON type, this type requires an SQL column comment hint
so that it can be reverse engineered from the database. Doctrine
cannot map back this type properly on vendors not supporting column
comments and will fall back to text type instead.
But since MySQL does support a JSON data type, I thought this would just work, but apparently not. Any help on how API Platform works with a JSON data type using MYSQL would be much appreciated.
You are getting this error because of your PHPdoc type hints. Api-platform uses PHPdoc metadata during normalization.
There is no such things as "json" data type in PHP, so it will look for a class in the same namespace.
From Doctrine documentation:
Values retrieved from the database are always converted to PHP’s array or null types using PHP’s json_decode() function.
So you should change your PHPDoc typehint to array
/**
* #var array
*
* #ORM\Column(type="json")
*/
private $document;
/**
* Get document.
*
* #return array
*/
public function getDocument()
{
return $this->document;
}
/**
* Set document.
*
* #param array $document
*
* #return MyEntity
*/
public function setDocument($document)
{
$this->document = $document;
return $this;
}

Symfony Api Rest which validator process

My question concerns various process of json payload validations.
I have recensed :
- deserialization on a model, calling validator service and validate the hydrated object.
- using FormType (even if there are no forms...just json feeds) and validate the form builder after injecting $datas.
Which one do you prefer ?
Have you a better solution ? Such as maybe a middleware (unique bundle ou app that deals with all in/out-coming payloads - request/response)
Thank You
I validate/deserialize with the native listeners/tools FOSRestBundle provides.
Making use of the bundle you can have native form-validation ... or automatically deserialized and validated models plus a list of validation errors injected as controller arguments.
# app/config/config.yml
# You need SensioFrameworkExtraBundle for body converters to work
sensio_framework_extra:
request: { converters: true }
fos_rest:
zone:
- path: '^/api/(.*)+$'
# [..]
body_listener:
enabled: true
default_format: json
decoders:
json: fos_rest.decoder.jsontoform
# automatically injects query parameters into controller Actions
# see #FOSRest\QueryParam in the example below
param_fetcher_listener: force
# https://symfony.com/doc/master/bundles/FOSRestBundle/request_body_converter_listener.html
body_converter:
enabled: true
validate: true
validation_errors_argument: validationErrors
The body converter can deserialize and validate models automatically for you (without using any forms or manual steps). Example:
/**
* #ParamConverter(
* "post",
* converter = "fos_rest.request_body",
* options = {
* "validator" = {
* "groups" = {
* "validation-group-one",
* "validation-group-two",
* }
* },
* "deserializationContext" = {
* "groups" = {
* "serializer-group-one",
* "serializer-group-two"
* },
* "version"="1.0"
* }
* }
* )
*/
public function putPostAction(Post $post, ConstraintViolationListInterface $validationErrors)
{
if (!empty($validationErrors)) {
// return some 4xx reponse
}
// Do something with your deserialized and valid Post model
The bundle can serialize forms (and form-errors) to JSON, too.
i.e. a form with invalid fields will be rendered as:
{
"code": 400,
"message": "Validation Failed",
"errors": {
"errors": [
"This is a global form error."
],
"children": {
"oldPassword": {
"errors": [
"The old password is not correct."
]
},
"newPassword": [],
"submit": []
}
}
}
FOSRestBundle provides a request body listener that automatically decodes Content-Type: application/json to Content: application/x-www-form-urlencoded within the Request object so you can bind the request to the form with handleRequest as you'd do with normal HTML forms.
Quick tip: if you just want to validate your data asynchronously ... you can send the request with a query param (?validate=true in the following example) and return an early response with HTTP 200 (OK) / 202 (Accepted) before performing any business logic.
The following example shows an endpoint that accepts requests of the form:
{
"oldPassword": "xxxxxxx",
"newPassword": "yyyyyyy"
}
Corresponding controller action:
/**
* #FOSRest\Route(
* "/profile/change-password",
* name="api_put_password",
* methods={
* Request::METHOD_PUT
* }
* )
*
* #FOSRest\QueryParam(
* name="validate",
* allowBlank=false,
* default="false",
* strict=true,
* nullable=true,
* requirements="^(true|false)$"
* )
*/
public function putPasswordAction(Request $request, string $validate = 'false')
{
$validate = filter_var($validate, FILTER_VALIDATE_BOOLEAN);
$form = $this->formFactory->createNamed(null, ChangePasswordType::class, null, [
'action' => $this->router->generateUrl('api_put_password'),
'method' => $request->getMethod(),
]);
$form->handleRequest($request);
if (!$form->isValid()) {
$view = new View();
$view->setStatusCode(Response::HTTP_BAD_REQUEST);
$view->setData($form);
return $view;
}
if ($validate) {
$view = new View();
$responseCode = Response::HTTP_ACCEPTED;
$view->setStatusCode($responseCode);
$view->setData([
'code' => $responseCode,
'message' => 'Data is valid.',
'data' => null
]);
return $view;
}
$user = $this->securityContext->getToken()->getUser();
/** #var PasswordChangeRequest $passwordChangeRequest */
$passwordChangeRequest = $form->getData();
$user->setPassword($this->passwordEncoder->encodePassword($user, $passwordChangeRequest->getNewPassword()));
$this->userManager->persist($user);
$view = new View();
$view->setStatusCode(Response::HTTP_OK);
$view->setData([
'code' => Response::HTTP_OK,
'message' => 'Password changed successfully.',
'data' => $user
]);
$context = new Context();
$context->setGroups([
'profile'
]);
$view->setContext($context);
return $view;
}

Swagger doctrine/zircote bearer Authorization code

i'm trying to use swagger zircote to create the swagger ui json.
for my application i use JWT and i need the following swagger code in my json:
"securityDefinitions": {
"Bearer": {
"in": "header",
"type": "accessToken",
"name": "Authorization"
}
},
But i dont know how i create that code with swaggers zircote. I've tried the following code:
* #SWG\Swagger(
* schemes={"https"},
* #SWG\SecurityDefinitions(
* bearer={
* type="apiKey",
* name="Authorization",
* in="header"
* }
* ),
but this results in the following error:
The annotation "#Swagger\Annotations\SecurityDefinitions" in
.\index.php on line 2 does not exist, or could not be auto-loaded.
Can someone help me, i cant find any good documentation about this, maybe its to specific, but i hope someone can help me.
Thanks!
Issued this also on the github... https://github.com/zircote/swagger-php/issues/366
Use #SWG\SecurityScheme instead of #SWG\SecurityDefinitions.
* #SWG\Swagger(
* schemes={"https"},
* #SWG\SecurityScheme(
* securityDefinition="Bearer",
* type="apiKey",
* name="Authorization",
* in="header"
* ),
A list of available annotations can be found list in vendor/zircote/swagger-php/src/Annotations
In my case worked as follows:
In the src dir a following class (some general settings):
#[OA\Info(version: "0.1", title: "My First API")]
#[OA\Server(url: 'http://localhost:8080/api')]
#[OA\SecurityScheme(
securityScheme: 'bearerAuth',
type: 'http',
in: 'header',
scheme: 'bearer',
)]
class OpenApi
{
}
and endpoint:
#[OA\Get(
path: '/someUrl',
summary: 'some description',
security: [['bearerAuth' => []]],
tags: ['some tag'],
)
]
#[OA\Response(response: '200', description: 'The data 1')]
#[OA\Response(response: '401', description: 'Unauthorized')]
public function getSomeData($request, $response, $args)

JSON datetime "array to string conversion" error

I'm trying to send a datetime field in JSON.
Here my JSON:
"mydate": "2015-07-07T22:00:00.000Z"
Here the PHP/Doctrine declaration:
/**
* #var \DateTime
*
* #ORM\Column(name="mydate", type="datetime")
*/
private $mydate;
But when I send the JSON in POST method, I got that error:
{
"code": 500,
"message": "Notice: Array to string conversion"
}
After debugging, I found that the error occur when my REST API Bundle (LemonRestBundle) calls the dezerialize() JMS method.
I just don't know why JMS can't deserialize my date to a PHP \DateTime... (if this is the problem...)
I've also tried this format:
"mydate": {
"lastErrors": {
"warning_count":0,
"warnings":[],
"error_count":0,
"errors":[]
},
"timezone": {
"name":"Europe\/Warsaw",
"location": {
"country_code":"PL",
"latitude":52.25,
"longitude":21,
"comments":""
}
},
"offset":7200,
"timestamp":1399413600
}
And thing like that:
"mydate": "2015/07/12"
Can someone help me on this ? Thanks! ;)

Categories