使用Zend Framework的Android RESTful Web应用程序

时间:2021-07-14 16:24:59

I have written a web application which is based on the Zend Framework (Version 1.11.11) and I want to use the SAME backend code for coding the mobile version of this application (Android). To achieve this, I want to get the response for each of the actions in the controllers in XML and JSON - for mobile-based app.

我编写了一个基于Zend Framework(版本1.11.11)的Web应用程序,我想使用SAME后端代码来编写此应用程序的移动版本(Android)。为实现这一目标,我希望获得XML和JSON控制器中每个操作的响应 - 基于移动的应用程序。

But the problem I am facing is:

但我面临的问题是:

Each of the actions in my controllers will return a view variable which will then be interpreted by the view script. But I want each of the actions to return a JSON array in case of a mobile application and the regular/usual thing (view variables) for the browser based web application.

我的控制器中的每个操作都将返回一个视图变量,然后由视图脚本解释。但是我希望每个动作在移动应用程序的情况下返回JSON数组,并且为基于浏览器的Web应用程序返回常规/常规事物(视图变量)。

Can anyone of you give me an example of how it can be achieved for a loginAction() in UsersController.

你们中的任何人都可以举例说明如何在UsersController中实现loginAction()。

The URL would look like:

URL看起来像:

http://{servername}/service/login

To do this, I want some insight and advice on how to do it in the most efficient and CORRECT way. I googled for answers but I did not find any good code samples or implementation samples on how to achieve this. I appreciate any help and guidance.

要做到这一点,我需要一些有关如何以最有效和最正确的方式做到这一点的见解和建议。我搜索了答案,但我没有找到任何好的代码示例或实现示例如何实现这一点。我感谢任何帮助和指导。

The way I have done it is: Have an API which is called with parameters which would parse the call and then off load it to the controller. But unsuccessful in coding it.

我这样做的方法是:使用参数调用API,该参数将解析调用,然后将其卸载到控制器。但编码不成功。

The code which I have until now:

我到目前为止的代码:

A UserController with loginAction() (for users logging in):

带有loginAction()的UserController(对于登录的用户):

According to me, I should be using the same logic or rather the same function as the loginAction in UsersController (for web-based and mobile-based app) as follows:

据我所知,我应该使用与UsersController中的loginAction相同的逻辑或相同的功能(对于基于Web和基于移动的应用程序),如下所示:

public function loginAction()
  {
// Already logged in
if( Engine_Api::_()->user()->getViewer()->getIdentity() ) {
  $this->view->status = false;
  $this->view->error = Zend_Registry::get('Zend_Translate')->_('You are already signed in.');
  if( null === $this->_helper->contextSwitch->getCurrentContext() ) {
    $this->_helper->redirector->gotoRoute(array(), 'default', true);
  }
  return;
}

// Make form
$this->view->form = $form = new User_Form_Login();
$form->setAction($this->view->url(array('return_url' => null)));
$form->populate(array(
  'return_url' => $this->_getParam('return_url'),
));

// Render
$this->_helper->content
    //->setNoRender()
    ->setEnabled()
    ;

// Not a post
if( !$this->getRequest()->isPost() ) {
  $this->view->status = false;
  $this->view->error = Zend_Registry::get('Zend_Translate')->_('No action taken');
  return;
}

// Form not valid
if( !$form->isValid($this->getRequest()->getPost()) ) {
  $this->view->status = false;
  $this->view->error = Zend_Registry::get('Zend_Translate')->_('Invalid data');
  return;
}

// Check login creds
extract($form->getValues()); // $email, $password, $remember
$user_table = Engine_Api::_()->getDbtable('users', 'user');
$user_select = $user_table->select()
  ->where('email = ?', $email);          // If post exists
$user = $user_table->fetchRow($user_select);

// Get ip address
$db = Engine_Db_Table::getDefaultAdapter();
$ipObj = new Engine_IP();
$ipExpr = new Zend_Db_Expr($db->quoteInto('UNHEX(?)', bin2hex($ipObj->toBinary())));

// Check if user exists
if( empty($user) ) {
  $this->view->status = false;
  $this->view->error = Zend_Registry::get('Zend_Translate')->_('No record of a member with that email was found.');
  $form->addError(Zend_Registry::get('Zend_Translate')->_('No record of a member with that email was found.'));

// Code
  return;
}

// Check if user is verified and enabled
if( !$user->enabled ) {
  if( !$user->verified ) {

   // Code here.
    // End Version 3 authentication

  } else {
    $form->addError('There appears to be a problem logging in. Please reset your password with the Forgot Password link.');

   // Code

    return;
  }
} else { // Normal authentication
  $authResult = Engine_Api::_()->user()->authenticate($email, $password);
  $authCode = $authResult->getCode();
  Engine_Api::_()->user()->setViewer();

  if( $authCode != Zend_Auth_Result::SUCCESS ) {
    $this->view->status = false;
    $this->view->error = Zend_Registry::get('Zend_Translate')->_('Invalid credentials');
    $form->addError(Zend_Registry::get('Zend_Translate')->_('Invalid credentials supplied'));

   //Code
    return;
  }
}

// -- Success! --

// Register login
$loginTable = Engine_Api::_()->getDbtable('logins', 'user');
$loginTable->insert(array(
  'user_id' => $user->getIdentity(),
  'email' => $email,
  'ip' => $ipExpr,
  'timestamp' => new Zend_Db_Expr('NOW()'),
  'state' => 'success',
  'active' => true,
));
$_SESSION['login_id'] = $login_id = $loginTable->getAdapter()->lastInsertId();
$_SESSION['user_id'] = $user->getIdentity();

// Some code.

// Do redirection only if normal context
if( null === $this->_helper->contextSwitch->getCurrentContext() ) {
  // Redirect by form
  $uri = $form->getValue('return_url');
  if( $uri ) {
    if( substr($uri, 0, 3) == '64-' ) {
      $uri = base64_decode(substr($uri, 3));
    }
    if($viewer->is_vendor) {
        return $this->_helper->redirector->gotoRoute(array('module' => 'user' ,'controller' => 'vendors', 'action' => 'mydeals'), 'vendor_mydeals', true);
    } else {
        return $this->_helper->redirector->gotoRoute(array('action' => 'index'), 'user_searchquery', true);
    }
    //return $this->_redirect($uri, array('prependBase' => false));
  }

  return $this->_helper->redirector->gotoRoute(array('action' => 'index'), 'user_searchquery', true);
}

}

So I want to use the above loginAction() even for mobile based application.

所以我想使用上面的loginAction()甚至是基于移动的应用程序。

Next, I have a class called Service_Api with a variety of functions. Below is a function I have now to get user based on id.

接下来,我有一个名为Service_Api的类,它具有各种功能。以下是我现在根据id获取用户的功能。

private function getUser(array $params)
{
    $userData = array();
    $usersTable = Engine_Api::_()->getDbtable('users', 'user'); 
    $select = $usersTable->select()->where('user_id = ?', $params['user']);

    $user = $usersTable->findOne($params['user']);
    if($user) {
        $userData = $user->exportToArray();
    }

    return Zend_Json_Encoder::encode($userData);
}

Similarly I want to have a loginAction for logging in. How will the loginAction() look and how will I get only JSON vlaues (say user values from db and success/failure for login success/failure) for mobile application.

同样地,我希望有一个loginAction用于登录.loginAction()的外观如何,以及如何仅为移动应用程序获取JSON vlaues(比如来自db的用户值以及登录成功/失败的成功/失败)。

I want to have a RESTful URL.

我想要一个RESTful URL。

So my URLs would look like:

所以我的网址看起来像:

http://{servername}/service/login
http://{servername}/service/groups/list etc.

I have a controller called ServiceController with loginAction as follows:

我有一个名为ServiceController的控制器,带有loginAction,如下所示:

public function loginAction()
{
    $this->_helper->viewRenderer->setNoRender();
    $this->_helper->layout->disableLayout(true);
    /*
     * Fetch Parameters and Parameter Keys
     * We don't need the controller or action!
     */
    $params = $this->_getAllParams();
    unset($params['controller']);
    unset($params['action']);
    unset($params['module']);
    unset($params['rewrite']);
    $paramKeys = array_keys($params);

    /*
     * Whitelist filter the Parameters
     */
    Zend_Loader::loadClass('Zend_Filter_Input');
    $filterParams = new Zend_Filter_Input($params);

    /*
     * Build a request array, with method name to call
     * on handler class for REST server indexed with
     * 'method' key.
     *
     * Method name is constructed based on valid parameters.
     */
    $paramKeysUc = array();
    foreach($paramKeys as $key)
    {
        $paramKeysUc[] = ucfirst($key);
    }

    $methodName = 'getBy' . implode('', $paramKeysUc);
    $request = array(
        'method'=>$methodName   
    );

    /*
     * Filter parameters as needed and add them all to the
     * $request array if valid.
     */
    foreach($paramKeys as $key)
    {
        switch($key)
        {
            case'tag':
                $request[$key] = $filterParams->testAlnum($key);
                break;
            default:
                $request[$key] = $params[$key];
        }
        if(!$request[$key])
        {
            // need better handling of filter errors for a real webservice…
            throw new Exception($request[$key] . ' contained invalid data');
        }
    }

    /*
     * Setup Zend_Rest_Server
     */
    require_once 'Zend/Rest/Server.php';

    $server = new Zend_Rest_Server;
    $server->setClass('Service_API');
    echo $server->handle($request);
}

But this is using a separate controller action.

但这是使用单独的控制器操作。

Any help is appreciated.

任何帮助表示赞赏。

Thanks. Abhilash

1 个解决方案

#1


5  

Disabling layouts works for JSON, but it doesn't allow you to redirect the request to the good controller according to the format requested (XML, JSON, etc.).

禁用布局适用于JSON,但它不允许您根据请求的格式(XML,JSON等)将请求重定向到良好的控制器。

From there, how to decides what actions to call according to the requested format?

从那里,如何根据请求的格式决定要调用的操作?

Ajax Context

Use AjaxContext in your controller _init() method:

在控制器_init()方法中使用AjaxContext:

$ajaxContext = $this->_helper->getHelper('AjaxContext');
$ajaxContext->addActionContext('login', 'json')
            ->addActionContext('login', 'xml')
            ->initContext();

This will have for effect to redirect your XML request to the same action that your JSON request.

这将使您的XML请求重定向到与JSON请求相同的操作。

How to make tell which format should be used? Simply add ?format=xml or /format/xml (or json) to the URL parameters. You URL would rather look like this: http://{servername}/service/login/format/json.

如何判断应该使用哪种格式?只需将?format = xml或/ format / xml(或json)添加到URL参数即可。您的URL看起来像这样:http:// {servername} / service / login / format / json。

From your action, how to know which format has been requested? You don't have anything to do, AjaxContext takes care of everything already.

从您的行动,如何知道请求的格式?你没有任何事情要做,AjaxContext已经完成了所有工作。

In case of a JSON request:

如果是JSON请求:

JSON. The JSON context sets the 'Content-Type' response header to 'application/json', and the view script suffix to 'json.phtml'.

JSON。 JSON上下文将“Content-Type”响应头设置为“application / json”,将视图脚本后缀设置为“json.phtml”。

By default, however, no view script is required. It will simply serialize all view variables, and emit the JSON response immediately.

但是,默认情况下,不需要查看脚本。它将简单地序列化所有视图变量,并立即发出JSON响应。

In case of an XML request:

如果是XML请求:

Change the view suffix to 'xml.phtml' (or, if you use an alternate view suffix, 'xml.[your suffix]').

将视图后缀更改为“xml.phtml”(或者,如果使用备用视图后缀,则为“xml。[your suffix]”)。

Note that using AjaxContext, response headers are automatically going to be set according to the response format requested.

请注意,使用AjaxContext时,将根据请求的响应格式自动设置响应头。

Aware of that, you shouldn't need to use Zend_Json_Encoder anymore.

意识到这一点,你不应该再使用Zend_Json_Encoder了。

If you want to know more about RESTful API, I've read a very interesting ppt slide written by Matthew Weier O'Phinney (currently Project Lead of ZF), I definitely recommended it.

如果您想了解更多关于RESTful API的信息,我已经阅读了Matthew Weier O'Phinney(现为ZF项目负责人)撰写的一篇非常有趣的ppt幻灯片,我绝对推荐它。

One more thing, your application doesn't seem to respect the Skinny controller and Fat model convention recommended by Zend Framework, I believed that if you're following this principle it would make things way more clearer to you. And also, your loginAction() would only get a success or failure message from your model, which would be easy to convert to JSON or XML using the method I described above.

还有一件事,你的应用程序似乎并不尊重Zend Framework推荐的Skinny控制器和Fat模型约定,我相信如果你遵循这个原则,它会让事情变得更加清晰。而且,您的loginAction()只会从您的模型中获取成功或失败消息,这将很容易使用上述方法转换为JSON或XML。

RESTful API

In order to know if the request is a GET request or a POST request, use these methods in your controllers:

要知道请求是GET请求还是POST请求,请在控制器中使用以下方法:

  • $this->_getAllParams(); or $this->getRequest()->getParams();` will catch all parameters, POST and GET.
  • $这 - > _ getAllParams();或$ this-> getRequest() - > getParams();`将捕获所有参数,POST和GET。

  • $this->getRequest()->getPost() retrieves POST parameters.
  • $ this-> getRequest() - > getPost()检索POST参数。

  • $this->getRequest()->getQuery() retrieves GET parameters.
  • $ this-> getRequest() - > getQuery()检索GET参数。

And to determine the request type, you can uses these methods:

要确定请求类型,您可以使用以下方法:

  • isGet()
  • isPost()
  • isPut()
  • isDelete()

More information here in the manual.

更多信息请参见手册。

#1


5  

Disabling layouts works for JSON, but it doesn't allow you to redirect the request to the good controller according to the format requested (XML, JSON, etc.).

禁用布局适用于JSON,但它不允许您根据请求的格式(XML,JSON等)将请求重定向到良好的控制器。

From there, how to decides what actions to call according to the requested format?

从那里,如何根据请求的格式决定要调用的操作?

Ajax Context

Use AjaxContext in your controller _init() method:

在控制器_init()方法中使用AjaxContext:

$ajaxContext = $this->_helper->getHelper('AjaxContext');
$ajaxContext->addActionContext('login', 'json')
            ->addActionContext('login', 'xml')
            ->initContext();

This will have for effect to redirect your XML request to the same action that your JSON request.

这将使您的XML请求重定向到与JSON请求相同的操作。

How to make tell which format should be used? Simply add ?format=xml or /format/xml (or json) to the URL parameters. You URL would rather look like this: http://{servername}/service/login/format/json.

如何判断应该使用哪种格式?只需将?format = xml或/ format / xml(或json)添加到URL参数即可。您的URL看起来像这样:http:// {servername} / service / login / format / json。

From your action, how to know which format has been requested? You don't have anything to do, AjaxContext takes care of everything already.

从您的行动,如何知道请求的格式?你没有任何事情要做,AjaxContext已经完成了所有工作。

In case of a JSON request:

如果是JSON请求:

JSON. The JSON context sets the 'Content-Type' response header to 'application/json', and the view script suffix to 'json.phtml'.

JSON。 JSON上下文将“Content-Type”响应头设置为“application / json”,将视图脚本后缀设置为“json.phtml”。

By default, however, no view script is required. It will simply serialize all view variables, and emit the JSON response immediately.

但是,默认情况下,不需要查看脚本。它将简单地序列化所有视图变量,并立即发出JSON响应。

In case of an XML request:

如果是XML请求:

Change the view suffix to 'xml.phtml' (or, if you use an alternate view suffix, 'xml.[your suffix]').

将视图后缀更改为“xml.phtml”(或者,如果使用备用视图后缀,则为“xml。[your suffix]”)。

Note that using AjaxContext, response headers are automatically going to be set according to the response format requested.

请注意,使用AjaxContext时,将根据请求的响应格式自动设置响应头。

Aware of that, you shouldn't need to use Zend_Json_Encoder anymore.

意识到这一点,你不应该再使用Zend_Json_Encoder了。

If you want to know more about RESTful API, I've read a very interesting ppt slide written by Matthew Weier O'Phinney (currently Project Lead of ZF), I definitely recommended it.

如果您想了解更多关于RESTful API的信息,我已经阅读了Matthew Weier O'Phinney(现为ZF项目负责人)撰写的一篇非常有趣的ppt幻灯片,我绝对推荐它。

One more thing, your application doesn't seem to respect the Skinny controller and Fat model convention recommended by Zend Framework, I believed that if you're following this principle it would make things way more clearer to you. And also, your loginAction() would only get a success or failure message from your model, which would be easy to convert to JSON or XML using the method I described above.

还有一件事,你的应用程序似乎并不尊重Zend Framework推荐的Skinny控制器和Fat模型约定,我相信如果你遵循这个原则,它会让事情变得更加清晰。而且,您的loginAction()只会从您的模型中获取成功或失败消息,这将很容易使用上述方法转换为JSON或XML。

RESTful API

In order to know if the request is a GET request or a POST request, use these methods in your controllers:

要知道请求是GET请求还是POST请求,请在控制器中使用以下方法:

  • $this->_getAllParams(); or $this->getRequest()->getParams();` will catch all parameters, POST and GET.
  • $这 - > _ getAllParams();或$ this-> getRequest() - > getParams();`将捕获所有参数,POST和GET。

  • $this->getRequest()->getPost() retrieves POST parameters.
  • $ this-> getRequest() - > getPost()检索POST参数。

  • $this->getRequest()->getQuery() retrieves GET parameters.
  • $ this-> getRequest() - > getQuery()检索GET参数。

And to determine the request type, you can uses these methods:

要确定请求类型,您可以使用以下方法:

  • isGet()
  • isPost()
  • isPut()
  • isDelete()

More information here in the manual.

更多信息请参见手册。