I am developing rest web services using zend 2.
For error handling I am using below mentioned code in Module.php
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
$eventManager->attach(MvcEvent::EVENT_DISPATCH_ERROR, array(
$this,
'onDispatchError'
), 0);
$eventManager->attach(MvcEvent::EVENT_RENDER_ERROR, array(
$this,
'onRenderError'
), 0);
}
public function onDispatchError($e)
{
return $this->getJsonModelError($e);
}
public function onRenderError($e)
{
return $this->getJsonModelError($e);
}
/*
* Manages error's and return response_code and error message
*
* #return Zend\View\Model\JsonModel
*/
public function getJsonModelError($e)
{
$error = $e->getError();
if (! $error) {
return;
}
$response = $e->getResponse();
$sm = StaticServiceManager::getDefaultServiceManager();
$config = $sm->get('config');
$err_code = $response->getStatusCode();
$err_msg = $response->getReasonPhrase();
$response_code = $config['HTTP_ERROR'][$err_code]['response_code'];
$errorJson = array(
'response_code' => $response_code,
// 'status' => $err_code,
'message' => $err_msg
);
$model = new JsonModel($errorJson);
$e->setResult($model);
return $model;
}
However there are still cases which are not tracked by this approach. For e.g. Invalid HTTP method passed. Does there any way I can check final response and manipulate it e.g. I can check final response content-type & http status code to finally change the response to send some meaning full json message.
Related
I am brand new to PSR standards, and I am not sure if I adapted my code to PSR-7, PSR-15 correctly.
My code is handling a POST request to delete a group of products by receiving an array of ids.
Is that a correct adaptation? Thanks.
<?php
require_once 'DataBase.php';
require_once 'config.php';
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
class DeleteRequest implements RequestHandlerInterface
{
private $DB;
public function __construct(DataBase $DB)
{
$this->DB = $DB;
}
//Delete each product from the database using the ID
public function handle(ServerRequestInterface $request): ResponseInterface
{
// Make sure it is a POST request
if ($request->getMethod() !== 'POST') {
throw new Exception('Incorrect REQUEST_METHOD. '.
'Only POST requests are allowed.');
}
// Extract the 'ids' array from the request data
MyLogV($request->getBody()->getContents());
$data = json_decode($request->getBody()->getContents(), true);
// Make sure the 'ids' array is present in the data
if (!isset($data['ids'])) {
throw new Exception('Missing required parameter: ids');
}
$ids = $data['ids'];
foreach ($ids as $id) {
myLog("DeleteRequest->handle","id",$id);
$result = $this->DB->deleteProduct($id);
if ($result['status'] != 'success') break;
}
// Generate the response: 200 => OK, 400 => Bad request
$status = $result['status'] == 'success' ? 200 : 400;
$response = new JsonResponse($result, $status);
myLogV($result['status']);
return $response;
}
}
try {
$serverRequest = ServerRequestFactory::fromGlobals();
$DB = new DataBase();
$deleteRequest = new DeleteRequest($DB);
$response = $deleteRequest->handle($serverRequest);
$response->send();
} catch (Exception $e) {
myLog("delete.php","Exception",$e->getMessage());
$result = ['status' => 'error','message'=> $e->getMessage()];
$response = new JsonResponse($result, 400);
$response->send();
}
exit();
?>
I tried to understand the PSR standards.
I have 'sendsms' function which i used it in one of my controllers and worked fine. now what i need to know how i can make class reference of this code to use it in other controllers, instead of copy/paste whole code in all controllers.
In other Q/A they mentioned about only creating reference but i wanted to do it properly like using constructor or etc, not just doing things work, i want to do it like real-world project.
Here's the code in controller :
public function store(Request $request)
{
$this->validate($request,[
'title' => 'required|string|min:6',
'gametype' => 'required|string|min:3',
'description' => 'required|string|min:1|max:180',
'price' => 'required|numeric|min:4',
'buyyer_id' => 'required|numeric|min:1'
// 'seller_id' => 'required|numeric|min:1'
]);
// return RequestModel::create([
// 'title' => $request['title'],
// 'description' => $request['description'],
// 'gametype' => $request['gametype'],
// 'price' => $request['price'],
// 'buyyer_id' => $request['buyyer_id'],
// 'seller_id' => Auth::user()->id,
// ]);
//
$requestModel = new RequestModel;
// store
$requestModel->title = $request['title'];
$requestModel->description = $request['description'];
$requestModel->gametype = $request['gametype'];
$requestModel->price = $request['price'];
$requestModel->buyyer_id = $request['buyyer_id'];
$requestModel->seller_id = Auth::user()->id;
$requestModel->save();
return $this->sendSms($request['title'], $request['gametype']);
}
// I want to use this code in another class to use it in all controllers without copy/paste it.
function sendSms($reqid, $recgametype) {
//Send sms to getway
//implement later.
$otp_prefix = ':';
$response_type = 'json';
$textMSGATLAS = iconv("UTF-8", 'UTF-8//TRANSLIT',"req : ( " .$reqid. " ) for ( " .$recgametype. " ) submitted ");
ini_set("soap.wsdl_cache_enabled", "0");
try {
$client = new SoapClient("http://xxxx");
$user = "user";
$pass = "pass";
$fromNum = "+xxx";
$toNum = "+xxxx";
$messageContent = $textMSGATLAS;
$op = "send";
$client->SendSMS($fromNum,$toNum,$messageContent,$user,$pass,$op);
} catch (SoapFault $ex) {
echo $ex->faultstring;
}
}
I'm right now learning and I'm beginner at this so help to understand how to make it work properly. Thanks.
You can create a separate SMS class like :
<?php
namespace App;
class SMS {
private $reqid;
private $recgametype;
public function __construct($reqid, $recgametype)
{
$this->reqid = $reqid;
$this->recgametype = $recgametype;
}
public function send()
{
$otp_prefix = ':';
$response_type = 'json';
$textMSGATLAS = iconv("UTF-8", 'UTF-8//TRANSLIT',"req : ( " .$this->reqid. " ) for ( " .$this->recgametype. " ) submitted ");
ini_set("soap.wsdl_cache_enabled", "0");
try {
$client = new SoapClient("http://xxxx");
$user = "user";
$pass = "pass";
$fromNum = "+xxx";
$toNum = "+xxxx";
$messageContent = $textMSGATLAS;
$op = "send";
return $client->SendSMS($fromNum,$toNum,$messageContent,$user,$pass,$op);
} catch (SoapFault $ex) {
throw new \Exception('SMS sending failed')
}
}
}
And then inside controller or wherever you would need :
public function sendSms($reqid, $recgametype) {
$sms = new \App\SMS($reqid, $recgametype);
$sms->send();
}
You can also create custom exception like SMSSendingFailedException and throw it instead of standard \Exception inside send() function.
That will help you to send appropriate response in controller like :
public function sendSms($reqid, $recgametype) {
try{
$sms = new \App\SMS($reqid, $recgametype);
$sms->send();
return response()->json('message' => 'SMS sent successfully', 200);
}
catch(SMSSendingFailedException $e){
return response()->json('message' => 'SMS sending failed', 500);
}
}
Then to go one step further, you can use concept of laravel facade if you need it all over the project with a quick class alias.
I'm trying to figure out why my ApiException is still returning a text/html response instead of a json response as denoted in ApiException render method. It is giving me the correct error message however its not rendering it as json.
/**
* Get the checklist (depending on type - send from Vue model)
*/
public function fetchChecklist(Request $request)
{
$id = $request->input('projectId');
$type = $request->input('type');
if (empty($id)) {
throw new ApiException('Project was not provided.');
}
if (! $project = RoofingProject::find($id)) {
throw new ApiException('Project not found.');
}
if (empty($type)) {
throw new ApiException('No checklist type was provided.');
}
switch ($request->input('type')) {
case 'permitting':
$items = $project->permittingChecklist;
break;
case 'permit':
$items = $project->permitReqChecklist;
break;
default:
throw new ApiException('Checklist not found.');
break;
}
return [
'status' => 'success',
'message' => '',
'items' => $items
];
}
App\Exceptions\ApiException.php
<?php
namespace App\Exceptions;
class ApiException extends \Exception
{
public function render($request)
{
return response()->json(['status' => 'error', 'error' => $this->message]);
}
}
In your request to the API you can try to add the following to your head/curl call to specify the datatype:
"Accept: application/json"
The laravel application is looking for if the requests expects json.
It worked for me with setting the following header as so
"x-requested-with": "XMLHttpRequest"
I'm still trying to add a recaptcha to my website, I want try the recaptcha from Google but I can't use it properly. Checked or not, my email is still sent.
I tried to understand the code of How to validate Google reCaptcha v2 using phalcon/volt forms?.
But i don't understand where are my problems and more over how can you create an element like
$recaptcha = new Check('recaptcha');
My controller implementation :
<?php
/**
* ContactController
*
* Allows to contact the staff using a contact form
*/
class ContactController extends ControllerBase
{
public function initialize()
{
$this->tag->setTitle('Contact');
parent::initialize();
}
public function indexAction()
{
$this->view->form = new ContactForm;
}
/**
* Saves the contact information in the database
*/
public function sendAction()
{
if ($this->request->isPost() != true) {
return $this->forward('contact/index');
}
$form = new ContactForm;
$contact = new Contact();
// Validate the form
$data = $this->request->getPost();
if (!$form->isValid($data, $contact)) {
foreach ($form->getMessages() as $message) {
$this->flash->error($message);
}
return $this->forward('contact/index');
}
if ($contact->save() == false) {
foreach ($contact->getMessages() as $message) {
$this->flash->error($message);
}
return $this->forward('contact/index');
}
$this->flash->success('Merci, nous vous contacterons très rapidement');
return $this->forward('index/index');
}
}
In my view i added :
<div class="g-recaptcha" data-sitekey="mypublickey0123456789"></div>
{{ form.messages('recaptcha') }}
But my problem is after : i create a new validator for the recaptcha like in How to validate Google reCaptcha v2 using phalcon/volt forms? :
use \Phalcon\Validation\Validator;
use \Phalcon\Validation\ValidatorInterface;
use \Phalcon\Validation\Message;
class RecaptchaValidator extends Validator implements ValidatorInterface
{
public function validate(\Phalcon\Validation $validation, $attribute)
{
if (!$this->isValid($validation)) {
$message = $this->getOption('message');
if ($message) {
$validation->appendMessage(new Message($message, $attribute, 'Recaptcha'));
}
return false;
}
return true;
}
public function isValid($validation)
{
try {
$value = $validation->getValue('g-recaptcha-response');
$ip = $validation->request->getClientAddress();
$url = $config->'https://www.google.com/recaptcha/api/siteverify'
$data = ['secret' => $config->mysecretkey123456789
'response' => $value,
'remoteip' => $ip,
];
// Prepare POST request
$options = [
'http' => [
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($data),
],
];
// Make POST request and evaluate the response
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
return json_decode($result)->success;
}
catch (Exception $e) {
return null;
}
}
}
So i don't know if tjis code is correct anyway, i have a problem too after that : how to create an object "recaptcha" in my form add
$recaptcha = new ?????('recaptcha');
$recaptcha->addValidator(new RecaptchaValidator([
'message' => 'Please confirm that you are human'
]));
$this->add($recaptcha);
PS: I apologize because i'm a noob here and my mother tongue is not english, so if you don't understand me or want give me some advices to create a proper question, don't hesitate ^^
I've made a custom form element for recaptcha. Used it for many projects so far.
The form element class:
class Recaptcha extends \Phalcon\Forms\Element
{
public function render($attributes = null)
{
$html = '<script src="https://www.google.com/recaptcha/api.js?hl=en"></script>';
$html.= '<div class="g-recaptcha" data-sitekey="YOUR_PUBLIC_KEY"></div>';
return $html;
}
}
The recaptcha validator class:
use Phalcon\Validation\Validator;
use Phalcon\Validation\ValidatorInterface;
use Phalcon\Validation\Message;
class RecaptchaValidator extends Validator implements ValidatorInterface
{
public function validate(\Phalcon\Validation $validation, $attribute)
{
$value = $validation->getValue('g-recaptcha-response');
$ip = $validation->request->getClientAddress();
if (!$this->verify($value, $ip)) {
$validation->appendMessage(new Message($this->getOption('message'), $attribute, 'Recaptcha'));
return false;
}
return true;
}
protected function verify($value, $ip)
{
$params = [
'secret' => 'YOUR_PRIVATE_KEY',
'response' => $value,
'remoteip' => $ip
];
$response = json_decode(file_get_contents('https://www.google.com/recaptcha/api/siteverify?' . http_build_query($params)));
return (bool)$response->success;
}
}
Using in your form class:
$recaptcha = new Recaptcha($name);
$recaptcha->addValidator(new RecaptchaValidator([
'message' => 'YOUR_RECAPTCHA_ERROR_MESSAGE'
]));
Note 1: You were almost there, you just missed to create custom form element (the first and last code piece from my example);
Note 2: Also there is a library in Github: https://github.com/fizzka/phalcon-recaptcha I have not used it, but few peeps at phalcon forum recommended it.
I am trying to get a soap response in php. It keeps coming as an object onto my web browser but not as xml. WSDL shows as XML but not the response received. Below is my server side code. The soap server is Zend Soap
ini_set("soap.wsdl_cache_enabled", 0);
if (isset($_GET['wsdl'])){
$wsdl = 'http://localhost/webservice/soap';
$autoDiscover = new AutoDiscover();
$autoDiscover->setOperationBodyStyle(
array('use' => 'literal',
'namespace' => 'http://localhost/webservice/soap')
);
$autoDiscover->setBindingStyle(
array('style' => 'rpc',
'transport' => 'http://schemas.xmlsoap.org/soap/http')
);
$autoDiscover->setComplexTypeStrategy(new ArrayOfTypeComplex());
// $service is the class that does the handling of functions
$autoDiscover->setClass($service);
$autoDiscover->setUri($wsdl);
$response->getHeaders()->addHeaderLine('Content-Type', 'text/xml');
$response->setContent($autoDiscover->toXml());
} else {
$server = new Server('http://localhost/webservice/soap?wsdl'
);
// $service is the class that does the handling of functions
$server->setObject($service);
$response->setContent($server->handle());
}
return $response;
}
Service class
class service
{
/**
*
* #param string $Email
* #return int $Credit
*/
public function checkCredits($Email)
{
$validator = new email();
if (!$validator->isValid($Email))
{
return new \SoapFault('5', 'Please Provide an Email');
}
$rowset = $this->tableGateway->select(array('EMAIL'=>$Email))
$row = $rowset->current();
$credits = $row->CREDITS;
return $credits;
}
}
Request is :
try{
$sClient = new SoapClient('http://localhost/webservice/soap?wsdl');
$params = "email";
$response = $sClient->checkCredits($params);
var_dump($response);
} catch(SoapFault $e){
var_dump($e);
}
This is an example of how I handle my functions with SoapClient:
$client = new SoapClient('http://url/Service.svc?wsdl');
$var = array('arg' => 10,
'VA' => 48);
$varresponse = $client->Function($var);
print_r( $varresponse->FunctionResult);
Hope this will help you out.
Your soapserver should look a bit like this:
<?php
if(!extension_loaded("soap")){
dl("php_soap.dll");
}
ini_set("soap.wsdl_cache_enabled","0");
$server = new SoapServer("hello.wsdl");
function doHello($yourName){
return "Hello, ".$yourName;
}
$server->AddFunction("doHello");
$server->handle();
?>
How did you set up yours? Do you return anything?
Now, your client should look like this:
<?php
try{
$sClient = new SoapClient('http://localhost/test/wsdl/hello.xml');
$params = "Name";
$response = $sClient->doHello($params);
var_dump($response);
} catch(SoapFault $e){
var_dump($e);
}
?>