Laravel forcing json response for api - php

I'm building an api at my company using laravel.
The problem I'm encountering is that if you send an api request without defining the correct header with the request you will get html back if there is a failure e.g. authorization failure or findOrFail() failure.
My thinking is that you never want to return html (even if the user has the wrong header).
I have a couple of solutions. In BeforeMiddleware.php I can manually insert a header into the request such as:
// Check if we are on an api route
$apiRoute = strncmp($uri, '/api/', 5) == 0;
// Insert the request header to force json response
if ($apiRoute){
$language = $request->header->add('Accept', 'application/json');
}
The 2nd solutions would be to throw an error if they don't have the correct header.
What would be the best way to enforce a json response, what is a good practice for handling api responses in laravel?

Once you detected that you are on your api path you are out of the woods and can indeed tackle your problem in the app\Exceptions\Handler.php file like suggested on How do you force a JSON response on every response in Laravel?.
For an open source project I created JSON exception objects by Microsoft format as output, but you can choose the jsonapi format (http://jsonapi.org/examples/#error-objects-basics) as you like:
https://github.com/StadGent/laravel_site_opening-hours/blob/develop/app/Exceptions/Handler.php
(note that on this implementation it is indeed depending from the headers, but you can use your path detection I think)

Related

What's the right way to handle chunked json response with swagger and autogenerated php client?

I'm a bit lost between definitions, generated code and many things that are a bit of a black box, even after debugging.
But let's start at the beginning. I have an API written with node using NestJS as framework. NestJS automatically creates the swagger/openapi json file. Using swagger-codegen I create a PHP class to access the API from another server. Works like a charm for simple API request.
Now the problem are API request returning a bigger response, i.e. >1000 rows from one or more DBs. Do make the client not wait a long time and create a big JSON response on the server I've switched to NDJSON, which splits the reponse in chunks of smaller JSON parts, each on it's own line. This also works when I create my request by hand with curl or a HTTP wrapper in PHP using fopen and fread. The response type is application/x-ndjson.
But the code generated by swagger-codegen is always waiting until the whole response has been received. It's even worse, because it fails to decode NDJSON with json_decode() and just returns null. Underneath Guzzle is used, which uses PSR7 streams for the response.
Now I could just skip the autogenerated code for the NDJSON endpoints. But I'd prefer not to add special handling and lose all the useful generated checks.
So is it somehow possible to make swagger-codegen give access to the stream of the response? Am I missing a parameter to codegen or something in the swagger JSON? It does have a produce with application/x-ndjson.
Do answer my own question it is possible, but not easily - meaning there is no option or parameter.
First create a class that extends the autogenerated API class. There you have access to all of the protected methods. We assume the endpoint or api method is called testMethodGet. The only thing that can be reused is the request method, i.e. testMethodGetRequest, but it does all of the client side validation and transforming of input data, so that's already a big win. Do also get some of the boilerplate result validation you can copy the "http info method", i.e. testMethodGetWithHttpInfo (use async version if you wish). Remove the if/else block after $responseBody = $response->getBody(); and in the return replace the ObjectSearializer line with just $responseBody.
What you are now getting back is the body as PSR7 stream, but there is one last catch. By default the whole response is drained into a temporary file. To really stream the result body from the server in the created method add an option to $options: $options['stream'] = true; Now the method returns as soon as the body is started. No need to wait for the whole body content.
One more thing. As the result is a PSR7 stream you can use $stream->detach() to get the PHP stream resource if you prefer to use the normal file and stream methods.

Symfony | Authenticate request

I'm trying to create a "service" like application, which can be able to receive API calls from another services. (These services will be built, for different purposes). And also able to send API calls to an another one.
Each request that they send, and accept has to have the following format.
{
header : {
// some header information, like locale, currency code etc.
signature : "some-hashed-data-using-the-whole-request"
},
request : {
// the usable business data
}
}
To each request I want to append a hash, that is generated from the actual request or anyhow (salted with password or any kind of magic added). Its not that important at the moment. I gave the name signature to this field. So for each received request, I want to reproduce this signature from the request. If the signature I received is matching with the one I generated, I let the application run otherwise showing some error message.
I already read a few articles, but most of them is for user-pass combinations.
My question is not about that if it's a good solution or not. I just want to know how can implement a middleware like functionality - like in laravel - in Symfony 4?
Instead of putting headers into a JSON object the HTTP body, use HTTP headers directly. That’s what they are for. When you’re using non-standard headers, prefix them with X- and maybe a prefix for your application, for example X-YourApp-Signature. The request goes into the body, i.e. the value of the request property in your example.
The server side is pretty simple with Symfony:
public function someAction(Request $request)
{
$signature = $request->headers->get("X-YourApp-Signature");
$data = json_decode($request->getContent());
// ... go on processing the received values (validation etc.)
}
If you want to write a HTTP client application in PHP, I would recommend using the Guzzle library. Here’s an example:
$headers = ["X-YourApp-Signature" => "your_signature_string"];
$data = json_encode(["foo" => "bar"]);
$request = new \GuzzleHttp\Psr7\Request("POST", "https://example.com", $headers, $data);
$client = new \GuzzleHttp\Client();
$response = $client->send($request, ["timeout" => 10]);
var_dump($response);
Of course, you’ll also want to implement some error handling etc. (HTTP status >= 400), so the code will be a bit more complex in a real application.
As k0pernikus mentioned, the before after filters solves my issue.

How to pass SOAP Request Properties in PHP (as shown in SoapUI)

In SOAP UI I am able to pass a few request properties, specifically Username, Password and WSS-Password Type. They are marked in the screenshot below by a red box:
I've tried passing these values in PHP as the second parameter of the SoapClient function like so:
$soap = new SoapClient('https://rev-int.api.us.fleetmatics.com/Vehicle/SageQuest/VehicleService.svc?wsdl',
array("Username" => "blah#example.com",
"Password" => "notarealpassword",
"WSS-Password Type" => "PasswordText"));
But I get the security error: An error occurred when verifying security for the message.
The WSDL I'm calling itself is here, though my question is about passing the request properties this way in general, really: https://rev-int.api.us.fleetmatics.com/Vehicle/SageQuest/VehicleService.svc?wsdl
It is likely that a security header with username and password is sent along with the message itself, but you can not see this header in the normal window in SoapUI.
Click "http log" in the bottom panel of SoapUI to see the whole communication. Look thoroughly for Header ithems.
If any header ithems, they must be set before your function SOAP call in PHP, but after you've constructed the client.
Use __ setSoapHeaders() to set headers in PHP
I was also getting that security error.
I used the soapui tool as well. But i had better success with the Chrome App called Boomerang.
You need to choose WSS PasswordText under Auth and then everything just seems to work.
Then you can even copy the xml that Boomerang generates into PHP and it works as well.

Supporting both POST data and JSON formatted requests in slim 2

I'm maintaining a backend API written in slim 2. the api is mostly used by mobile apps, so I'm looking to switch from sending requests as POST data to sending JSON formatted strings. This will simplify the api, as the apps currently sending arrays, and even json strings as post data variables.
While it's simple to release an update to the mobile apps and switch to json formatted POST requests, the backend api will have to keep supporting both formats for some time until all users will update their apps.
I'm looking to have a replacement function to $app->request->post() that will either get the request param from the post data or from the json data, depending on the content type that was sent by the client.
For example, im using $app->request->post('user_id', 0); to get the variable from the post data.
I got installed into my api and it does convert the json body into an array. the problem is that now each of my api functions have to check either $app->json_body array or the $app->request->post function to retrieve the request params.
For example:
$app->get('/settings', function () use($app, $freeze)
{
if ($app->json_body != null) {
$user_id = $app->json_body['user_id'];
} else {
$user_id = $app->request->post('user_id', 0);
}
}
It's pretty straigtward creating a global function that does that, but I'm looking to have that function on the slim $app instance.
You could try using https://github.com/slimphp/Slim-Middleware and ensuring that the requests with a JSON body contain the proper Content-Type: application/json header.

laravel 4 why send content-type when sending json?

In the book of laravel I read, and also my co-worker who has experience with laravel said that generating JSON I should in laravel way.
Why do I need to do this:
Route::get('markdown/response', function()
{
$data = array('iron', 'man', 'rocks');
return Response::json($data);
});
As I read it sends also content-type header when using this.
When I was using codeigniter I used to do simply this:
echo json_endode($data);
and never ever ever had any problems. Even if it is not set content type. Actually I dont know maybe php sets it automatically, but since I did not have problems, I did not care.
And when using 'new' technology I really want to know why it is better than good old one.
With respect, by not providing a content-type header, you were doing it "wrong" when coding in CodeIgniter.
Most clients (browsers, ajax requests, especially jQuery) can still can guess how to handle the response correctly and so probably "just worked" for you. You were likely always implicitly returning a Content-Type: text/html with your response, which is a default header in CodeIgniter.
You should always return a proper content type with your HTTP responses so the consuming client knows how to treat this content. Note that this is a mechanism of HTTP as defined in specification, not specific to any framework or even a language.
Response::json()
The above code is just a convenience function, where Laravel will automatically set the application/json header for you, as well as convert an array of data into JSON format. The only effective difference from your CodeIgniter code is the setting of the header, as you've pointed out.
It's worth noting that the Response object extends Symfony's response object, which is very "powerful" - in other words, it's a very good implementation of the HTTP protocol.
The response object returned from Response::json (and other Response static methods) are highly modifiable.
$response = Response::json($data);
$response->header('Content-Type', 'application/json');
return $response;
You can check for more available methods in the Laravel and Symfony code API.
http://laravel.com/api/class-Illuminate.Http.Response.html
http://api.symfony.com/2.1/Symfony/Component/HttpFoundation/Response.html
Just because it worked doesn't mean it wasn't wrong. JSON isn't HTML, so text/html is an inaccurate Content-Type for it.
Sending the correct header means libraries like jQuery understand what sort of data they're getting back, and thus are able to handle it on their own without guidance. Browsers may also do things like pretty-printing the JSON data or making it otherwise easier to read.
Depends what you are trying to do with the route. if you only want to return json data you can just return json_encode($data) and that will work, To actually return a json response for use with something like an ajax request you need the headers set properly or the accepting route just thinks its getting a string. Response::json is for setting the response which sets the headers appropriately.

Categories