Customizing JSON response with additional index keys - php

I use the response() helper on HTTP Response as documented. The straightforward use:
response()->json(
$this->response,
$this->status
)->withHeaders([]);
This will output:
{
"key" : "desired response"
}
However, I wanted to add a key on the response:
$return['message'] = 'Request successful';
$return['data'] = response()->json(
$this->response,
$this->status
)->withHeaders([]);
But the response resulted to:
{
"message": "Request successful",
"data": {
"headers": {},
"original": {
"key" : "desired response"
},
"exception": null
}
}
There are extra keys on the response: headers, original & exception. How can I get rid of that in order to achieve this desired format:
{
"message": "Request successful",
"data": {
"key" : "desired response"
}
}

You can you Laravel Provider
php artisan make:provider ResponseMacroServiceProvider
<?php
namespace App\Providers;
use Response;
use Illuminate\Support\ServiceProvider;
class ResponseMacroServiceProvider extends ServiceProvider
{
/**
* Bootrap the application service
*/
public function boot() {
Response::macro('success', function ($headers, $originals) {
return Response::json([
'message' => "Request successful",
'data' => [
'headers' => $headers,
'original' => $originals,
],
'error' => [
'code' => 0 ,
'message' => []
]
]);
});
Response::macro('error', function ($message, $status = 400) {
if(is_array($message))
$message_repsonse = $message;
else
$message_repsonse = [$message];
return Response::json([
'message' => "Request failed",
'data' => [
'headers' => null,
'original' => null,
]
'error' => [
'code' => $status,
'message' => $message_repsonse
]
]);
});
}
/**
* Register application service
* #override
*/
public function register() {
}
}
Edit your config/app.php
/*
* Application Service Providers...
*/
App\Providers\ResponseMacroServiceProvider::class,
And try to handle at your Controller
$headers = 'Your header';
$originals = Your_Model::find(1);
return response()->success($headers, $originals);
return response()->error($validator->errors()->all(), 300);

you can achieve this by using this code:
$return =[] ;
$return['message'] = 'Request successful'; // your message
$return['data'] = $this->response; // your data
return response()->json( // return json
$return, // your data here
$this->status // status here
);

Related

Argument 1 passed to Illuminate\Routing\Middleware\ThrottleRequests::addHeaders() must be an instance of please sir

have Created a new middleware for checking the user token I have create middleware then adeded to kernal.php and but when i tried to access $request in middleware i am getting the error
Here is my is my middleware code
please help
public function handle($request, Closure $next, $guard = null)
{
$token = $request->header('Authorization');
$verify = explode(" ", $token);
if ($verify[0] !== "petani") {
return [
'code' => 401,
'error' => 'Token not provided.'
];
}
if (!$token) {
return [
'code' => 400,
'error' => 'Provided token is expired.'
];
}
try {
$credentials = JWT::decode($verify[1], env('JWT_SECRET'), ['HS256']);
} catch(ExpiredException $e) {
return [
'code' => 400,
'error' => 'Token is expired. '
];
} catch(Exception $e) {
return [
'code' => 400,
'error' => 'An error while decoding token.'
];
}
return $next($request);
}
thanks to this answer:
you should return the response in this way:
public function handle($request, Closure $next, $guard = null)
{
$token = $request->header('Authorization');
$verify = explode(" ", $token);
if ($verify[0] !== "petani") {
$response= [
'code' => 401,
'error' => 'Token not provided.'
];
return response()->json($response, 401);
}
if (!$token) {
$response= [
'code' => 400,
'error' => 'Provided token is expired.'
];
return response()->json($response, 400);
}
try {
$credentials = JWT::decode($verify[1], env('JWT_SECRET'), ['HS256']);
} catch(ExpiredException $e) {
$response= [
'code' => 400,
'error' => 'Token is expired. '
];
return response()->json($response, 400);
} catch(Exception $e) {
$response= [
'code' => 400,
'error' => 'An error while decoding token.'
];
return response()->json($response, 400);
}
return $next($request);
}

Override response of Rest authentication(HttpBearerAuth) in yii2

I have token based authorization, for which i have did below changes.
In User model, override findIdentityByAccessToken() method as below.
public static function findIdentityByAccessToken($token, $type = null)
{
$userlogin = Userdevices::find()->where(['access_token' => $token])->one();
if ($userlogin == array()) {
return null;
} else {
$User = Users::findOne(['id' => $userlogin->user_id]);
if (!count($User))
{
return null;
}
else {
$dbUser = [
'id' => $User->id,
];
return new static($dbUser);
}
}
}
In Controller, I add behaviors() as below.
public function behaviors()
{
$behaviors[] = [
'class' => \yii\filters\ContentNegotiator::className(),
'formats' => [
'application/json' => \yii\web\Response::FORMAT_JSON,
],
];
$behaviors['authenticator'] = [
'class' => HttpBearerAuth::className(),
];
return $behaviors;
}
When API does not get token or token is not valid it gives below response
{
"name": "Unauthorized",
"message": "You are requesting with an invalid credential.",
"code": 0,
"status": 401,
"type": "yii\\web\\UnauthorizedHttpException"
}
I want to change response as per my requirement as below.
{
"code": 401,
"name": "Unauthorized",
"is_logout": "Y",
"status": "error",
"message": "logout"
}
You can change format of response using beforeSend event of yii\web\Response.
For example add following methods in your api controller:
public function init()
{
parent::init();
\Yii::$app->response->on(
\yii\web\Response::EVENT_BEFORE_SEND,
[$this, 'beforeResponseSend']
);
}
public function beforeResponseSend(\yii\base\Event $event)
{
/**
* #var \yii\web\Response $response
*/
$response = $event->sender;
if ($response->data['status'] == 401) {
$response->data = [
'code' => 401,
'name' => 'Unauthorized',
'is_logout' => 'Y',
'status' => 'error',
'message' => 'logout',
];
}
}
The init method of controller registers the beforeSend event. The beforeResponseSend method handles the event and changes the response format.
If you want to format response in multiple controller it might be better to put the event handler into own class for example
namespace app\components;
class ErrorResponseHelper
{
public static function beforeResponseSend(Event $event)
{
// ... formating code ...
}
}
And register the event in config/web.php
return [
// ...
'components' => [
'response' => [
'class' => 'yii\web\Response',
'on beforeSend' => [
\app\components\ErrorResponseHelper::class,
'beforeResponseSend',
],
],
],
];
But be careful with this solution because this way the \app\components\ErrorResponseHelper::beforeResponseSend will be called during each request.

Yii2 request does not redirect to actionError

I have setup my config to handle errors in the error function of ApiController. For example, when I go to a page that does not exist, it displays: message => page not found like defined in the action. However, when I make an unauthorized request the site does not display a message defined in the action but instead the following response is returned:
{
"name": "Unauthorized",
"message": "Your request was made with invalid credentials.",
"code": 0,
"status": 401,
"type": "yii\\web\\UnauthorizedHttpException"
}
What do I need to change to send a response as defined in actionError?
api/config/main.php:
'components' => [
'user' => [
'identityClass' => 'common\models\User',
'enableAutoLogin' => true,
'identityCookie' => ['name' => '_identity-api', 'httpOnly' => true],
],
'errorHandler' => [
'errorAction' => 'api/error',
],
...
api/controllers/ApiController.php:
<?php
namespace api\controllers;
use Yii;
use yii\web\BadRequestHttpException;
use yii\web\Controller;
use yii\web\Response;
class ApiController extends Controller
{
/**
* #param $action
* #return bool
* #throws BadRequestHttpException
*/
public function beforeAction($action)
{
Yii::$app->response->format = Response::FORMAT_JSON;
$this->enableCsrfValidation = false;
return parent::beforeAction($action);
}
public function afterAction($action, $result)
{
$session = Yii::$app->getSession();
if($session->getIsActive()) {
$session->destroy();
}
return parent::afterAction($action, $result);
}
/**
* #return array
*/
public function actionError()
{
$exception = Yii::$app->errorHandler->exception;
Yii::$app->response->format = Response::FORMAT_JSON;
Yii::$app->response->statusCode = $exception->statusCode;
switch($exception->statusCode)
{
case 401:
return [
'error' => 'Unauthorized',
'message' => $exception->getMessage()
];
case 404:
return [
'message' => 'Page not found'
];
default:
{
return [
'error' => 'Something went wrong',
'message' => $exception->getMessage()
];
}
}
}
}

Customize a Json object to follow a specific format

In my Laravel API, email validation error response comes like this...
{
"status": 409,
"message": {
"emailAddress": [
"A user with email: my#email.com already exists."
]
}
}
The message value comes from this : $validator->messages();
Now, what I want is to get the error response like this json format
{
"status": 409,
"message": "A user with email: my#email.com already exists"
}
How to get this done by going inside $validator->messages(); ?
If you only want to return the first error to your user, you can handled that by using the MessageBag as a Collection, like so:
$validator = Validator::make($request->input(), [
"email" => "...",
"password" => "..."
]);
if ($validator->fails()) {
$firstError = $validator->messages()->first();
return response()->json(["status" => 409, "message" => $firstError], 409);
}
For larval 5.6 and above you can use below solution.
Edit file under app/Exceptions/Handler.php and add following files
protected function convertValidationExceptionToResponse(ValidationException $e, $request) {
if ($e->response) {
return $e->response;
}
$errors = $e->validator->errors()->getMessages();
if ($request->expectsJson()) {
return response()->json(["errors" => $this->error_to_json($errors)], 422);
}
return redirect()->back()->withInput(
$request->input()
)->withErrors($errors);
}
protected function error_to_json($errors) {
$json_errors = $errors;
$new_errors = array();
foreach ($json_errors as $key => $value) {
$new_errors[$key] = $value[0];
}
return $new_errors;
}
Hope this help you:
$validator = Validator::make($request->all(), [
'email' => 'required|emails|exists:users',
]);
if ($validator->fails()) {
return response()->json(['status' => 409, 'message' => "A user with email: ' . $request->email . ' already exists"], 200);
}

Customizing The Flashed Error Format - Laravel 5.3

I want to customize the format of flashed error message, that is recieved after i do something like $this->validator($request->all())->validate();
For an ajax request, it responds with:
{
"name": [
"The name field is required."
],
"email": [
"The email field is required."
],
}
But i want it to look like
{
"status": "fail",
"errors": {
"name": [
"The name field is required."
],
"email": [
"The email field is required."
],
}
}
I read the documentation under Customizing The Flashed Error Format and added formatValidationErrors method in my Controller class, but it's not making any difference.
public function formatValidationErrors(Validator $validator)
{
return ['status' => 'fail', 'errors' => $validator->errors()->getMessages()];
}
It's not changing even if i change formatValidationErrors method in the original Illuminate\Foundation\Validation\ValidatesRequests trait
I'm forced have to build this format in all request calls and so there's code-duplication. It would be nice if i could just call $this->validator($request->all())->validate() and it automatically formats as per my requirement.
The way to change it is to create a class that extends Validator and override the addError method. Here's a sample code:
<?php
namespace App\Validators;
use Illuminate\Support\MessageBag;
use Illuminate\Validation\Validator;
class RestValidator extends Validator {
/**
* Add an error message to the validator's collection of messages.
*
* #param string $attribute
* #param string $rule
* #param array $parameters
* #return void
*/
protected function addError($attribute, $rule, $parameters)
{
$message = $this->getMessage($attribute, $rule);
$message = $this->doReplacements($message, $attribute, $rule, $parameters);
$customMessage = new MessageBag();
$customMessage->merge(['code' => strtolower($rule.'_rule_error')]);
$customMessage->merge(['message' => $message]);
$this->messages->add($attribute, $customMessage);
}
}
Now you can structure the validation in your controllers like so:
$rules = [
// Your rules here
];
$attributes = [
// The attributes you're checking here
];
$validator = Validator::make($attributes, $rules);
if ($validator->fails()) {
$errorMessage = [
'status' => 'fail',
'errors' => $validator->errors()
];
return $errorMessage;
}
// The rest of your code goes here
Try this in your model,
public function response(array $errors)
{
if (($this->ajax() && !$this->pjax()) || $this->wantsJson()) {
$errors = array('status' => 'fail', 'errors' => $errors);
return new JsonResponse($errors, 422);
}
return $this->redirector->to($this->getRedirectUrl())
->withInput($this->except($this->dontFlash))
->withErrors($errors, $this->errorBag);
}
It will result as you expected,
{
"status": "fail",
"error": {
"name": [
"The name field is required."
],
"email": [
"The email field is required"
]
}
}

Categories