設置
公式ドキュメントでは、事前に準備されたテンプレートからgiter8を使用してプロジェクトを作成することを提案しています。 ただし、不要なツールを使用せずに実行する場合は、次のようにsbtプロジェクトを簡単に作成できます。
プロジェクト\ plugins.sbt
addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "1.1.0")
このプラグインを使用すると、特別なsbtコマンドを使用してWebサービスを開始できます。
$ sbt > container:start
build.sbt
val scalatraVersion = "2.4.0-RC2-2" resolvers += "Scalaz Bintray Repo" at "https://dl.bintray.com/scalaz/releases" lazy val root = (project in file(".")).settings( organization := "com.example", name := "scalatra-auth-example", version := "0.1.0-SNAPSHOT", scalaVersion := "2.11.6", scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature"), libraryDependencies ++= Seq( "org.scalatra" %% "scalatra-auth" % scalatraVersion, "org.scalatra" %% "scalatra" % scalatraVersion, "org.scalatra" %% "scalatra-json" % scalatraVersion, "org.scalatra" %% "scalatra-specs2" % scalatraVersion % "test", "org.json4s" %% "json4s-jackson" % "3.3.0.RC2", "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided" ) ).settings(jetty(): _*)
追加されたライブラリの目的はその名前から理解できます。jsonや認証が必要ない場合は、不要なライブラリを安全に削除できます。
ルーティング
サービスが要求への応答を開始するには、最初に要求に応答するコントローラーを指定する必要があります。 このために次のファイルを作成しましょう。
src \ main \ webapp \ WEB-INF \ web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <servlet> <servlet-name>user</servlet-name> <servlet-class> org.scalatra.example.UserController </servlet-class> </servlet> <servlet-mapping> <servlet-name>user</servlet-name> <url-pattern>/user/*</url-pattern> </servlet-mapping> </web-app>
xmlを嫌う場合は、この方法で同じことをよりコンパクトに記述できます。
src / main / scala / ScalatraBootstrap.scala
import org.scalatra.example._ import org.scalatra._ import javax.servlet.ServletContext class ScalatraBootstrap extends LifeCycle { override def init(context: ServletContext) { context.mount(new UserController, "/user") } }
ここでは、 org.scalatra.example.UserControllerがパスyoursite.example / userで始まるリクエストに応答することを決定しました 。 このファイルの仕組みを見てみましょう。
src \ main \ scala \ org \ scalatra \ example \ UserController.scala
package org.scalatra.example import org.json4s.{DefaultFormats, Formats} import org.scalatra._ import org.scalatra.json.JacksonJsonSupport import scala.util.{Failure, Success, Try} class UserController extends ScalatraServlet with AuthenticationSupport with JacksonJsonSupport { protected implicit lazy val jsonFormats: Formats = DefaultFormats before() { contentType = formats("json") basicAuth() } get("/") { DB.getAllUsers } get("/:id") { Try { params("id").toInt } match { case Success(id) => DB.getUserById(id) case Failure(ex) => pass() } } }
このコードをさらに詳しく分析してみましょう。 まず、 ScalatraのすべてのコントローラーはScalatraServletを継承する必要があります。 サーブレットが応答するパスを決定するには、たとえば、要求のタイプに応じて、get、post、put、またはdeleteブロックを追加します。
get("/") { /*...*/ }
yoursite.example / userへのリクエストに応答します。 パラメータのいずれかがURLの一部である場合、次のようにパラメータを記述する必要があります。
get("/:id") { params("id") }
その結果、 params()メソッドを使用してgetブロック内でidパラメーターを使用できます。 同様に、残りのクエリパラメータを取得できます。 / user / 52のように、同じ名前の複数のパラメーターを渡したい場合は、 Foo = uno&bar = dos&baz = three&foo = anotherfoo (fooパラメーターが2回使用されることに注意してください)、 multiParams()関数を使用して、均一に処理できますパラメーター、例えば:
multiParams("id") // => Seq("52") multiParams("foo") // => Seq("uno", "anotherfoo") multiParams("unknown") // => an empty Seq
Pass()メソッドはUserControllerで使用されることに注意してください。 特定のルートでの処理をスキップして、次のルートに進むことができます(この場合、この方法に該当するハンドラーはこれ以上ありません)。 要求の処理を中断し、ユーザーにエラーページを表示する場合は、 halt()メソッドを使用します。このメソッドは、たとえば、リターンコードやエラーテキストなどのさまざまなパラメーターを取ることができます。
フレームワークによって提供される別の可能性は、たとえば次のように書くことで、プリハンドラーとポストハンドラーを設定することです。
before() { contentType = formats("json") basicAuth() }
応答のタイプ(この場合はjson )を指定し、ユーザーからの認証を要求できます(認証とjsonの操作については、以下のセクションで説明します)。
ルーティングの詳細については、 公式ドキュメントを参照してください 。
DBを使用する
前のセクションでは、BDクラスから取得したオブジェクトをコントローラーの応答として使用します。 ただし、 Scalatraには、データベースを操作するための組み込みフレームワークがありません。これに関連して、データベースを操作する模倣を残しました。
src \ main \ scala \ org \ scalatra \ example \ DB.scala
package org.scalatra.example import org.scalatra.example.models.User object DB { private var users = List( User(1, "scalatra", "scalatra"), User(2, "admin", "admin")) def getAllUsers: List[User] = users def getUserById(id: Int): Option[User] = users.find(_.id == id) def getUserByLogin(login: String): Option[User] = users.find(_.login == login) }
src \ main \ scala \ org \ scalatra \ example \ models \ User.scala
package org.scalatra.example.models case class User(id: Int, login:String, password: String)
ただし、これに問題があるとは思わないでください-公式ドキュメントには、 Scala 、 MongoDB 、 Squeryl 、 Riakなどの最も一般的なデータベースとORMでScalatraを友達にする方法が記載されています。
ジョンソン
コントローラーは、 ケースクラスUserを直接返すか、オプション[ユーザー]およびリスト[ユーザー]でさえ返すことに注意してください。 デフォルトでは、 Scalatraは戻り値を文字列に変換し、リクエストへの応答として使用します。たとえば、/ユーザーリクエストへの応答は次のようになります。
List(User(1,scalatra,scalatra), User(2,admin,admin)).
サーブレットがjsonとの連携を開始するには、次のことを行う必要があります。
- JacksonJsonSupport特性を追加します
- jsonへの変換形式を指定します。 Scalatraはjson4sを使用してjsonを操作します。これにより、jsonのカスタム変換ルールを作成したり、その逆を行うことができます。 私たちの場合、デフォルトのフォーマットで十分です:
protected implicit lazy val jsonFormats: Formats = DefaultFormats
- 戻りタイプのヘッダーを追加します。
contentType = formats("json")
これらの簡単な手順を実行すると、同じ/ユーザーリクエストに対する応答は次のようになります。
[{"id":1,"login":"scalatra","password":"scalatra"},{"id":2,"login":"admin","password":"admin"}]
認証
最後に、ユーザー認証などのトピックに触れたいと思います。 これを行うには、 Scentに移植されたWardenフレームワークであるScentryフレームワークを使用することを提案します。これは、Rubyに精通している人々にとっても生活を楽にします。
UserControllerクラスをよく見ると、認証がすでに実装されていることがわかります。 これを行うために、 AuthenticationSupport特性がクラスに追加され、 basicAuth()メソッドがbefore()フィルターで呼び出されます。 AuthenticationSupportの実装を見てください。
src \ main \ scala \ org \ scalatra \ example \ AuthenticationSupport.scala
package org.scalatra.example import org.scalatra.auth.strategy.{BasicAuthStrategy, BasicAuthSupport} import org.scalatra.auth.{ScentrySupport, ScentryConfig} import org.scalatra.example.models.User import org.scalatra.ScalatraBase import javax.servlet.http.{HttpServletResponse, HttpServletRequest} class OurBasicAuthStrategy(protected override val app: ScalatraBase, realm: String) extends BasicAuthStrategy[User](app, realm) { protected def validate(userName: String, password: String)(implicit request: HttpServletRequest, response: HttpServletResponse): Option[User] = { DB.getUserByLogin(userName).filter(_.password == password) } protected def getUserId(user: User)(implicit request: HttpServletRequest, response: HttpServletResponse): String = user.id.toString } trait AuthenticationSupport extends ScentrySupport[User] with BasicAuthSupport[User] { self: ScalatraBase => val realm = "Scalatra Basic Auth Example" protected def fromSession = { case id: String => DB.getUserById(id.toInt).get } protected def toSession = { case usr: User => usr.id.toString } protected val scentryConfig = new ScentryConfig {}.asInstanceOf[ScentryConfiguration] override protected def configureScentry() = { scentry.unauthenticated { scentry.strategies("Basic").unauthenticated() } } override protected def registerAuthStrategies() = { scentry.register("Basic", app => new OurBasicAuthStrategy(app, realm)) } }
最初に行うことは、認証戦略( ScentryStrategyインターフェイスを実装するクラス)を定義することです。 この場合、いくつかの標準メソッドを実装するBasicAuthStrategy [User]プリフォームを使用しました 。 その後、2つのメソッドを定義する必要があります-validate() 、ログインが成功した場合にSome [User]を返すか、不正なデータの場合にNoneとgetUserId() 、応答ヘッダーにさらに追加するための文字列を返す必要があります。
次に行うことは、 OurBasicAuthStrategyとScentrySupportをAuthenticationSupportトレイトに組み合わせることです 。これをコントローラーと混合します。 その中で、認証戦略を登録し、(最も簡単な方法で)セッションからユーザーオブジェクトを取得し、逆にそのIDをセッションに追加する方法を実装しました。
その結果、ログインしていないユーザーがUserControllerが処理するページにアクセスする場合、ユーザーは最初にユーザー名とパスワードを入力する必要があります。
おわりに
この記事では、 Scalatraの選択的な機能の一部のみを示しました。 このフレームワークはロシア語圏のコミュニティではあまり人気がありませんが、実装された機能の範囲が広く、開発が容易なため、小さなWebサービスと大規模なサイトの両方を作成するのに非常に有望です。
記事を読んでもまだ質問がある場合は、コメントまたは次の記事で回答する準備ができています。
すべてのソースコードはgithubで入手できます。
素敵な勉強をしてください!