14
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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
12
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
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?