В ноябре прошлого года я отправился в Антверпен, чтобы выступить в Devoxx . После моего разговора по HTML5 с Play Scala ко мне подошел Маттиас Карлссон , и мы поговорили о том же выступлении на Jfokus в Стокгольме. Я согласился, и мы начали говорить подробности после того, как Триш и я вернулись в США.
Я написал эту статью на самолете между Денвером и Сиэтлом и буду прыгать через Северный полюс в Стокгольм через Исландию сегодня вечером. Последние пару недель я обновляю свой Play More! HTML5 / мобильное приложение, чтобы добавить некоторые новые функции. В частности, я хотел обновить до Play 2.0, создать службы JSON и добавить аутентификацию.
Обновление до Play 2.0
Моя попытка перейти на Play 2.0 заключалась в проверке исходного кода из GitHub , создании и установке снимка RC1. Когда я попытался обновить свое приложение и начал получать неудачные операции импорта, я обратился к Интернету (в частности, к StackOverflow), чтобы выяснить, хорошая ли это идея . Первый ответ на этот вопрос предложил мне остаться с 1.x.
Если это критически важный проект, который должен быть завершен до следующего марта 2012 года, я бы пошел с Play 1.x. Если это менее важный проект, который может быть отложен и который в любом случае не будет выпущен до марта 2012 года, попробуйте Play 2.0.
Пока я не планировал выпускать Play More! до Jfokus я решил, что обновление не добавило много к разговору. Кроме того, я не смог найти руководство по обновлению Play Scala 0.9.1 до Play 2.0, и у меня не было достаточно времени на его создание. Поэтому я решил придерживаться Play 1.2.4 и добавить несколько служб JSON для своего клиента iPhone.
Серверы JSON
Я нашел игру Мануэля Бернхардта ! Скала и JSON . Это привело меня к Джерксону , построенному теперь печально известной @coda . Я был в состоянии легко заставить вещи работать довольно быстро и написал следующий WorkoutService.scala:
package controllers.api import play.mvc.Controller import models._ import com.codahale.jerkson.Json._ object WorkoutService extends Controller { def workouts = { response.setContentTypeIfNotSet("application/json") generate(Workout.find().list()) } def edit(id: Long) = { generate(Workout.byIdWithAthleteAndComments(id)) } def create() = { var workout = params.get("workout", classOf[Workout]) Workout.create(workout) } def save(id: Option[Long]) = { var workout = params.get("workout", classOf[Workout]) Workout.update(workout) } def delete(id: Long) = { Workout.delete("id={id}").on("id" -> id).executeUpdate() } }
Затем я добавил маршруты для моего нового API в conf / routs :
GET /api/workouts api.WorkoutService.workouts GET /api/workout/{id} api.WorkoutService.edit POST /api/workout api.WorkoutService.create PUT /api/workout/{id} api.WorkoutService.save DELETE /api/workout/{id} api.WorkoutService.delete
Затем я создал класс ApiTest.scala, который проверяет, работает ли первый метод должным образом.
import play.test.FunctionalTest import play.test.FunctionalTest._ import org.junit._ class ApiTests extends FunctionalTest { @Test def testGetWorkouts() { var response = GET("/api/workouts"); assertStatus(200, response); assertContentType("application/json", response) println(response.out) } }
Я запустил «play test», открыл свой браузер на http: // localhost: 9000 / @ tests и щелкнул ApiTests -> Start, чтобы убедиться, что он работает. Все зеленые сделали меня счастливым.
Наконец, я написал несколько CoffeeScript и jQuery, чтобы пользователи могли удалять тренировки и убедиться, что функция удаления работает.
$('#delete').click -> $.ajax type: 'POST' url: $(this).attr('rel') error: -> alert('Delete failed, please try again.') success: (data) -> location.href = "/more"
Я был очень впечатлен тем, как легко Play смог создать сервисы JSON, и я улыбнулся, когда мои навыки CoffeeScript получили повышение.
В пятницу перед отъездом в Devoxx я увидел запрос на регистрацию модуля для SecureSocial .
SecureSocial позволяет добавить интерфейс аутентификации в ваше приложение, которое работает со службами на основе гибридных протоколов OAuth1, OAuth2, OpenID и OpenID + OAuth.
Он также предоставляет механизм имени пользователя и пароля для пользователей, которые не хотят использовать существующие учетные записи в других сетях.
В этом выпуске поддерживаются следующие службы:
- Twitter (OAuth1)
- Facebook (OAuth2)
- Google (OpenID + OAuth Hybrid)
- Yahoo (OpenID + OAuth Hybrid)
- LinkedIn (OAuth1)
- Foursquare (OAuth2)
- MyOpenID (OpenID)
- WordPress (OpenID)
- Имя пользователя и пароль
Другими словами, это звучало как мечта, и я решил попробовать, как только найду время. Это время застало меня прошлым вечером в понедельник, и я отправил прямое сообщение @jaliss (автору модуля) через Twitter.
Работает ли Secure Social с Play Scala? Я хотел бы использовать его в моей игре «Больше»! проект.
Хорхе ответил 16 минут спустя, сказав, что он не использовал Play Scala и ему нужно будет провести некоторое исследование. В 8 часов вечера (через 1,5 часа после моего оригинального DM) Хорхе сработал образец и отправил его мне по электронной почте. 10 минут спустя я добавлял черту Secure в свой проект.
package controllers import play.mvc._ import controllers.securesocial.SecureSocial /* * @author Jorge Aliss <[email protected]> of Secure Social fame. */ trait Secure { self: Controller => @Before def checkAccess() { SecureSocial.DeadboltHelper.beforeRoleCheck() } def currentUser = { SecureSocial.getCurrentUser } }
Я настроил Twitter и Username + Password в качестве моих провайдеров, добавив следующее в conf / application.conf .
securesocial.providers=twitter,userpass
Мне также пришлось настроить ряд свойств securesocial.twitter. *. Затем я удостоверился, что мои маршруты были осведомлены о SecureSocial, добавив следующее в начало conf / route :
* /auth module:securesocial
Затем я указал это как зависимость в conf / dependencies.yml и запустил «play deps».
- play -> securesocial 0.2.4
После добавления «with Secure» в мой контроллер Profile.scala я попытался получить доступ к его маршруту и получил приглашение войти в систему. Мне сразу же показали ошибку об отсутствующем файле jQuery 1.5.2 в моей папке «javascripts», поэтому я добавил его и обрадовался, когда мне показали экран входа в систему. Мне пришлось добавить приложение в Twitter, чтобы использовать его серверы OAuth, но я был закачан, когда работала аутентификация по имени пользователя и паролю (в комплекте с регистрацией!), А также по Twitter.
Единственная проблема, с которой я столкнулся с SecureSocial, заключалась в том, что он не нашел реализацию по умолчанию UserService.Service SecureSocial при работе в режиме prod. Я смог обойти это, добавив реализацию SecureService.scala в мой проект и написав ее для общения с моей моделью спортсмена. Я не удосужился создать нового пользователя, когда он вошел в систему из Twitter, но это то, что я хочу сделать в будущем. Я также был рад узнать, что настройка представлений SecureSocial была очень легкой. Я просто скопировал их из модуля в представления моего приложения и вуаля!
package services import play.db.anorm.NotAssigned import play.libs.Codec import collection.mutable.{SynchronizedMap, HashMap} import models.Athlete import securesocial.provider.{ProviderType, UserService, SocialUser, UserId} class SecureService extends UserService.Service { val activations = new HashMap[String, SocialUser] with SynchronizedMap[String, SocialUser] def find(userId: UserId): SocialUser = { val user = Athlete.find("email={email}").on("email" -> userId.id).first() user match { case Some(user) => { val socialUser = new SocialUser socialUser.id = userId socialUser.displayName = user.firstName socialUser.email = user.email socialUser.isEmailVerified = true socialUser.password = user.password socialUser } case None => { if (!userId.provider.eq(ProviderType.userpass)) { var socialUser = new SocialUser socialUser.id = userId socialUser } else { null } } } } def save(user: SocialUser) { if (find(user.id) == null) { val firstName = user.displayName val lastName = user.displayName Athlete.create(Athlete(NotAssigned, user.email, user.password, firstName, lastName)) } } def createActivation(user: SocialUser): String = { val uuid: String = Codec.UUID() activations.put(uuid, user) uuid } def activate(uuid: String): Boolean = { val user: SocialUser = activations.get(uuid).asInstanceOf[SocialUser] var result = false if (user != null) { user.isEmailVerified = true save(user) activations.remove(uuid) result = true } result } def deletePendingActivations() { activations.clear() } }
Хорхе оказал большую помощь в удовлетворении моих потребностей в аутентификации, и он даже написал черту BasicAuth.scala для реализации базовой аутентификации в моих службах JSON.
package controllers import _root_.securesocial.provider.{UserService, ProviderType, UserId} import play._ import play.mvc._ import play.libs.Crypto import controllers.securesocial.SecureSocial /* * @author Jorge Aliss <[email protected]> of Secure Social fame. */ trait BasicAuth { self: Controller => @Before def checkAccess = { if (currentUser != null) { // this allows SecureSocial.getCurrentUser() to work. renderArgs.put("user", currentUser) Continue } val realm = Play.configuration.getProperty("securesocial.basicAuth.realm", "Unauthorized") if (request.user == null || request.password == null) { Unauthorized(realm) } else { val userId = new UserId userId.id = request.user userId.provider = ProviderType.userpass val user = UserService.find(userId) if (user == null || !Crypto.passwordHash(request.password).equals(user.password)) { Unauthorized(realm) } else { // this allows SecureSocial.getCurrentUser() to work. renderArgs.put("user", user) Continue } } } def currentUser = { SecureSocial.getCurrentUser() } }
Резюме
Мой последний опыт разработки с использованием Scala и использования Play для создания моего приложения был очень увлекательным. Несмотря на то, что возникали проблемы с перезагрузкой классов, а также версий Scala и Scalate , я смог добавить нужные функции. Я не смог обновиться до Play 2.0, но я не старался изо всех сил и решил, что лучше подождать, пока не будет опубликовано руководство по обновлению.
Я рад рассказать о своем последнем опыте разработчикам из Jfokus на этой неделе. Кроме того, на конференции обсуждались вопросы Play 2.0 , CoffeeScript , HTML5 , Scala и Scalate . Я надеюсь посетить многие из них и научиться новым трюкам, чтобы улучшить свои навыки и мое приложение.
Обновление: разработчики Delving написали статью о
миграции в Play 2 . Хотя он не предоставляет конкретных деталей о том, что им нужно было изменить, у него есть хорошая информация о том, сколько времени это заняло и на что нужно обратить внимание.
От http://raibledesigns.com/rd/entry/secure_json_services_with_play