В ноябре прошлого года я отправился в Антверпен, чтобы выступить в 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 <jaliss@gmail.com> 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 <jaliss@gmail.com> 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

