LoginSignup
14
11

More than 5 years have passed since last update.

AWS LambdaでHTML→XML→JSON変換をやってみた

Last updated at Posted at 2016-08-05

「AWSLambdaがなんかよいらしい」、ということをこの前勉強会で聞いたので試そうと思ったのがきっかけで、ついでにQiita初投稿

AWS自体初めてで、サーバーレスアーキテクチャだとかマイクロサービスだとかも全然理解できていないので変なところがあったら突っ込み歓迎です。

やりたかったこと

  • AWS Lambdaの使い方を覚える
  • 汎用的に使えそうななにかを作ってみる

やったこと

WebAPIと聞いて最初の思いついたのがデータフォーマットのコンバーターだったのでXMLからJSON変換をやってみようと思った。

データ作るのも面倒なのでその辺にころがってるHTMLをXMLに変換してそれをJSONに変えてみようと思い立つ

Lambdaという名前から安直に高階関数をイメージしたので

HTMLtoXML = (text/htmlを返すURL) ー> application/xml
XMLtoJson = (application/xmlを返すURL) ー> application/json
XMLtoJson(HTMLtoXML(HTMLを返すURL)) → JSon化されたHTML

といった仕様のラムダAPIを作ってみた

まずは

AWSの契約
1年無料らしいのでとりあえず登録
AWS.GIF
サービスいっぱいあってすごい

AWS Lambda

2016年8月時点、function実装に使える言語はNode.js、Python、Javaとのこと
Nodeだと簡単そうだったけどせっかくなので普段使ってるScalaでやってみることにした

functionの実装

たいしたことしてないので全ソースのせる
(https://github.com/bakenezumi/aws-converter)

AWSLambdaは1jar(fat jar)でデプロイしないといけないのでactivator assemblyでfat jarが作れるプラグインを追加

project/plugins.sbt
resolvers += "sbt-assembly-resolver-0" at "http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-releases"

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.13.0")

build.sbtにAWS関係やパース関係のライブラリを追加
(実際はいろいろ試したけど最終形)

build.sbt
name := """aws-converter"""

version := "1.0"

scalaVersion := "2.11.7"

libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.4" % "test"

libraryDependencies += "com.amazonaws" % "aws-lambda-java-core" % "1.0.0"

libraryDependencies += "org.scala-lang.modules" %% "scala-xml" %  "1.0.1"

libraryDependencies += "nu.validator.htmlparser" % "htmlparser" % "1.4"

libraryDependencies += "net.liftweb" %% "lift-json" % "2.6+"

functionはこんなかんじ
ほぼLiftからもってきた

src/main/scala/Converter.scala
package converter

trait ConverterBase {
  import com.amazonaws.services.lambda.runtime.Context
  import io.Source

  // テスト時はオーバーライドできるように(ファイルからとかプロキシ使うときとか)
  def fromURL(url: String, encode: String = "UTF-8") = Source.fromURL(url, encode)

  def htmlToXml(url: String, context: Context) = {
    import java.io.StringReader
    import scala.collection.JavaConverters._
    import scala.xml.Node
    import scala.xml.parsing.NoBindingFactoryAdapter
    import nu.validator.htmlparser.sax.HtmlParser
    import nu.validator.htmlparser.common.XmlViolationPolicy
    import org.xml.sax.InputSource

    val hp = new HtmlParser
    hp.setNamePolicy(XmlViolationPolicy.ALLOW)
    val saxer = new NoBindingFactoryAdapter
    hp.setContentHandler(saxer)
    hp.parse(new InputSource(new StringReader(fromURL(url).getLines.mkString)))

    saxer.rootElem.toString

  }

  def xmlToJson(url: String, context: Context) = {
    import scala.xml.XML
    import net.liftweb.json.{Xml, pretty, render}
    val source = fromURL(url).getLines.mkString
    val xml = XML.loadString(source)
    pretty(render(Xml.toJson(xml)))
  }
}

class Converter extends ConverterBase

やる気のないテストコード

src/test/scala/ConverterSpec.scala
import org.scalatest._
import converter._

class ConverterSpec extends FlatSpec with Matchers with ConverterBase{
  import io.Source

  override def fromURL(url: String, encode: String = "UTF-8") = Source.fromFile(url, encode)

  "Converter.htmlToXml" should "ok" in {
    val url = "src/test/scala/test.html"
    val ret = htmlToXml(url, null)
    println(ret)
    ret != null
  }

  "Converter.xmlToJson" should "ok" in {
    val url = "src/test/scala/test.xml"
    val ret = xmlToJson(url, null)
    println(ret)
    ret != null
  }

}

おもむろにactivator assembly
target/scala-2.11/の下にaws-converter-assembly-1.0.jarができた!

functionののデプロイ

コマンドでもできるらしいけどコンソールでやった
やり方は割愛

Web公開(AWS API Gateway)

ここからが本題
AWSすごい
Triger.GIF
TriggersからAddtriggerを選ぶとかっこいい画面がでてくる
addtrigger.GIF

左側にAPI Gatewayをえらぶ
addtrigger.2GIF.GIF

Lambda名と同一のResource名が初期表示される
SecurityだけOpenにしてSubmitするとひとまずOK

ただこのままだと動かない(LambdaにURLをGETで渡せない)ので、ごにょごにょする必要がある
API Gatewayの管理画面に飛んで

Gateway1.GIF

まずhtmlToXMLの「GETのメソッドリクエスト」の「URL クエリ文字列パラメータ」を追加
Gateway2.GIF

次に「統合リクエスト」を下記編集
Gateway3.GIF

こうすることでGETで渡したパラメータをラムダに渡すようになる
で、テストしてみると
Gateway4.GIF

成功!のようで実は失敗
AWS Lambdaでは返りがJSONなので、文字列を返した場合""で囲まれてしまう

なのでもう一個編集「統合レスポンス」

image

レスポンスのContent-Typeapplication/xmlにして、$input.path('$')というテンプレートに流すと生データがレスポンスされるようになる

image

できた!
んでprodにデプロイするとサービス公開完了!
https://hogehoge.execute-api.ap-northeast-1.amazonaws.com/prod/htmlToXml?url=http://yahoo.co.jp
(hogehogeは内緒)にアクセスするとyahooがXMLになった!

同様にxmlToJsonを公開すると、、、
https://hogehoge.execute-api.ap-northeast-1.amazonaws.com/prod/xmlToJson?url=https://hogehoge.execute-api.ap-northeast-1.amazonaws.com/prod/htmlToXml?url=http://yahoo.co.jp

image

できた!!

APIの統合(2016/8/8追記)

HTML→XML→JSONを一発でやってくれるAPIはいままで作ったAPIの組み合わせで実装できた

  • API-Gatewayに/htmlToJsonのGETメソッドを作成(リンク先はxmlToJsonのラムダ)

  • 「メソッドリクエスト」の「URLクエリ文字列パラメータ」に"url"を追加

  • 「統合リクエスト」の「マッピングテンプレート」を"https://hogehoge.execute-api.ap-northe1.ast-amazonaws.com/prod/htmlToXml?url=$input.params('url')"

image

  • 「統合レスポンス」は今までと同様生データを返すようにする

prodにデプロイするとhttps://hogehoge.execute-api.ap-northeast-1.amazonaws.com/prod/htmlToJson?url=http://yahoo.co.jpで期待した結果が得られた

所感

基本的な使い方は理解できた
サーバー立てずにサービス公開できるって素敵
あとScalaは重かった(約20MByte)

14
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
11