25 мая 2014 в 19:13

Автодокументация мобильных веб-сервисов на примере Yii

Думаю, что многие, особенно небольшие, компании, при работе с одним и тем же фреймоворком постоянно пишут какие-то вещи/расширения и т.п., которые решают именно те задачи, с которыми они сталкиваются наиболее часто.

В нашем случае этим фреймворком является Yii, а одной из самых популярных проблем была одновременная разработка web-сервиса для приложений iOS/Android.

Сначала, как и всегда, просто разработчики договаривались между собой что и как, но если разработчик вдруг менялся — начинались проблемы. Далее — описание входных/выходных данных в wiki. При большом количестве мелких изменений возникала проблема синхронизации кода и форматов, описанных в wiki.

Как мы решили проблему — ниже.

Разработка контроллера web-сервиса

Как правило, мобильный сервис — это отдельный модуль проекта. Сделали базовый модуль и базовый контроллер для работы с web-сервисом.

Ключевые вещи — ниже.
class VMJsonServiceController extends CController
{
   public $documentationMode = false;
   protected $request = null;

   ...
   public function init()
    {
        if ($this->documentationMode) {
            return;
        }

        if (Yii::app()->request->isPostRequest) {
            $json = CJSON::decode(file_get_contents("php://input"), false);
            if (isset($json->request)) {
                $this->request = $json->request;
            } else {
                $this->respondWithError(VMServiceResponseCode::SERVICE_ERROR, 'There is no request node');
            }
        } else {
           ...
        }
    }
   ...
    public function checkInputParameters($params = array())
    {
        if ($this->documentationMode) {
            $exception = new VMDocumentationException();
            $exception->parameters = VMObjectUtils::fromArray($params);
            throw $exception;
        }
        $this->checkObjectParameter($params, $this->request);
    } 
   ...
}


Самая «соль» заключается именно в параметре $documentationMode, но об этом позже.
Теперь рассмотрим пример контроллера уже с реального проекта, а не из общей библиотеки.
class UsersController extends VMJsonServiceController
{
   ...
   public function actionSignUp()
	{
		$this->checkInputParameters(array(
			'user' => array(
				'email' => 'test@test.com',
				'password' => '12345',
				'phone' => array('optional', 'value' => '+7 999 998 76 54'),
				'photos' => array('array', 'value' => array(
					'file' => base64_encode('Image'),
				)
			)
		));
		$email = $this->request->user->email;
		...
       }
	...
}

Метод checkInputParameters проверяет, что данные пришли в нужном для этого метода формате (email и password — обязательные, phone — необязательный, а photos — массив данных. Далее мы уверены, что все необходимые данные у нас есть и с ними можно работать.

Теперь, собственно, о чем шла речь в начале статьи — о документации. В принципе, массив в методе checkInputPаrameters — и есть формат входных данных, и, собственно, на основе него мы и генерируем документацию, но постоянно генерировать её ручками — неинтересно и долго. Поэтому, заставим модуль генерировать документацию о себе самому.

1. Делаем еще один контроллер
При помощи расширения Metadata он обходит все контроллеры и Action'ы, а дальше рендерит наглядное представление параметров метода и ответы сервиса. Сделано это при помощи упоминавшегося метода checkInputParameters
class VMDocumentationController extends CController
{
   ...
   //Полный код можете посмотреть в репозитории, здесь покажу лишь один метод
   //На вход он принимает объект "контроллер" и название метода
   private function getDefinition($class, $method)
	{
		try {
			$class->{$method}();
		} catch (VMDocumentationException $e) {
			return (object) array(
				'parameters' => $e->parameters
			);
		}
	}
   ...
}


2. Правим код базового модуля
class VMJsonServiceModule extends CWebModule {
	public function init() {
                ...
		$this->controllerMap = array(
			'documentation' => array(
				'class' => 'VMDocumentationController'
			)
		);
	}
} 


Что нам это дает? А то, что по одному и тому же url программист iOS/Android имеет документацию для любого проекта, меняется только baseUrl.
Как выглядит документация?
Пара скриншотов ниже.

В верхнем навбаре — список всех контроллеров модулей, в левом — список всех экшнов контроллера. Необязательные параметры можно удалить из запроса, кнопка Call — отправит запрос и покажет ответ сервера. То есть вы можете не писать ни строчки кода, но протестировать работу веб-сервиса на любых входных данных.





Ну и напоследок — все исходные коды можете найти здесь. Местами код ужасен, местами очень ужасен, многое из того, что есть в нашей библиотеке кроме мобильного сервиса — нафиг никому не надо, но если будет что вдруг предложить — pull-request вам в помощь.

Спасибо за внимание
Колосов Никита @Anexroid
карма
5,0
рейтинг 3,2
PHP разработчик
Самое читаемое Разработка

Комментарии (3)

  • 0
    swagger, не?
    • 0
      Честно говоря, о данном решении узнали совсем недавно, буквально пару недель назад, но нам не особо понравилось, во всяком случае их демо. То есть, например, я так и не разобрался как можно именно послать разные параметры запроса. Да и к дизайну Boostrrap'овскому уже привыкли
  • 0
    Swagger генеринует json по которому можно потом сделать свой swagger-ui c блекджеком и бутстрапом.

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.