RustとLambdaでなんか作る 中編
RustとLambdaでなんか作る 中編:
前回の続きです。
Rustがlambdaで動いてapi gateway経由でHello Worldするとこまでやりました。
本日は、ひとまずhttps://github.com/awslabs/aws-lambda-rust-runtime/tree/master/lambda-http/examples に乗ってるbasic.rsを見てみる。
まだなんとなくわかる程度です。
Cargo.tomlに
実行してみると・・
なるほどー。
lambda_httpのrequest.rsみると・・
って書いてあったりするから、たぶん
少しいじって動作検証です。
まずPOSTにapi gateway対応するようにしてjson投げてみました。
で、https://github.com/awslabs/aws-lambda-rust-runtime/blob/master/lambda-http/src/ext.rs にexampleがあったのでそれを参考にいじってみた結果、とりあえず動いた。
おっけー。json読めてる。
Rustはまだわかってないので見よう見まねですが。
そんなわけでgoogle homeに適当なサンプル作ってjson投げてみる事にします。
このへん書いてると大変なのでサックリとでお許しください。
カテゴリは深く考えず、Games & funにしときました。
Display nameはとりあえずRust Sample。また後でちゃんと作ります。
なお、defaultのレスポンスはややこしいから消しました。
Dialog flowから投げられるRequestと期待されてるResponseを確認しておきます。
Welcomeの例
ざっくりいうと、queryResult.queryTextを受け取ってfulfillmentTextを返せればおっけー。
jsonは一つネストしてるなー。どうやって取り出すんだろ。
なんか適当にやってみたらとりあえずできました。
でもアプリ側で日本語が文字化けしてる。。
こういう感じの問題でしょうか。
https://qiita.com/aoriso/items/a16a981d00d041c8f6a2
Response返すとこでContent-Type指定してやったら直りました。
ちなみにCargo.tomlはこんな感じになってます。
そんなわけで中編はここまで。
もう中編だけど何作るかは決まっておりません。
しかし、C言語、C++に代わる言語って話だったけど、触ってみた感じはnodeとかの方が似てるかなー。と感じる。
でもってRustはまだexampleから見よう見まねしてるだけの段階なので何もわかっていません。作りながら調べつつ覚えていく感じなので生暖かく見守っていてくださいませ。
前回の続きです。
Rustがlambdaで動いてapi gateway経由でHello Worldするとこまでやりました。
本日は、ひとまずhttps://github.com/awslabs/aws-lambda-rust-runtime/tree/master/lambda-http/examples に乗ってるbasic.rsを見てみる。
まだなんとなくわかる程度です。
basic.rsをdeployしてみる
basic.rs
use std::error::Error; use lambda_http::{lambda, IntoResponse, Request, RequestExt, Response}; use lambda_runtime::{error::HandlerError, Context}; use log::{self, error}; use simple_logger; fn main() -> Result<(), Box<dyn Error>> { simple_logger::init_with_level(log::Level::Debug).unwrap(); lambda!(my_handler); Ok(()) } fn my_handler(e: Request, c: Context) -> Result<impl IntoResponse, HandlerError> { Ok(match e.query_string_parameters().get("first_name") { Some(first_name) => format!("Hello, {}!", first_name).into_response(), _ => { error!("Empty first name in request {}", c.aws_request_id); Response::builder() .status(400) .body("Empty first name".into()) .expect("failed to render response") } }) }
log = "^0.4"
とsimple_logger = "^1"
を追記してます。実行してみると・・
$ curl -XGET "https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello?aaa" Empty first name $ curl -XGET "https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello?first_name=bob" Hello, bob!
lambda_httpのrequest.rsみると・・
/// Internal representation of an Lambda http event from both /// both ALB and API Gateway proxy event perspectives #[doc(hidden)] #[derive(Deserialize, Debug, Default)] #[serde(rename_all = "camelCase")] pub(crate) struct LambdaRequest<'a> { pub(crate) path: Cow<'a, str>, #[serde(deserialize_with = "deserialize_method")] pub(crate) http_method: Method, #[serde(deserialize_with = "deserialize_headers")] pub(crate) headers: HeaderMap<HeaderValue>, /// For alb events these are only present when /// the `lambda.multi_value_headers.enabled` target group setting turned on #[serde(default, deserialize_with = "deserialize_multi_value_headers")] pub(crate) multi_value_headers: HeaderMap<HeaderValue>, #[serde(deserialize_with = "nullable_default")] pub(crate) query_string_parameters: StrMap, /// For alb events these are only present when /// the `lambda.multi_value_headers.enabled` target group setting turned on #[serde(default, deserialize_with = "nullable_default")] pub(crate) multi_value_query_string_parameters: StrMap, /// ALB events do not have path parameters. #[serde(default, deserialize_with = "nullable_default")] pub(crate) path_parameters: StrMap, /// ALB events do not have stage variables. #[serde(default, deserialize_with = "nullable_default")] pub(crate) stage_variables: StrMap, pub(crate) body: Option<Cow<'a, str>>, #[serde(default)] pub(crate) is_base64_encoded: bool, pub(crate) request_context: RequestContext, }
e
のオブジェクトからはbodyもとれる。たぶん。
いじる
少しいじって動作検証です。まずPOSTにapi gateway対応するようにしてjson投げてみました。
curl -XPOST "https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello?first_name=bob" -d @package.json Hello, bob!
xy.json
{ "name": "bob", "x": 1, "y": 2 }
main.rs
use std::error::Error; use lambda_http::{lambda, Request, Response, Body}; use lambda_runtime::{error::HandlerError, Context}; use log::{self, info}; use simple_logger; #[macro_use] extern crate serde_derive; extern crate serde; extern crate serde_json; #[derive(Debug,Deserialize,Default)] struct Args { #[serde(default)] x: usize, #[serde(default)] y: usize, #[serde(default)] name: String } fn main() -> Result<(), Box<dyn Error>> { simple_logger::init_with_level(log::Level::Info).unwrap(); lambda!(my_handler); Ok(()) } fn my_handler(e: Request, c: Context) -> Result<Response<Body>, HandlerError> { let args: Args = serde_json::from_slice(e.body().as_ref()).unwrap(); info!("{:#?}", args); Ok( Response::new( format!( "{}: {} + {} = {}", args.name, args.x, args.y, args.x + args.y ).into() ) ) }
$ curl -XPOST -H "Accept: application/json" -H "Content-type: application/json" "https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello?first_name=bob" -d @xy.json bob: 1 + 2 = 3
Rustはまだわかってないので見よう見まねですが。
Hello worldしてみる
そんなわけでgoogle homeに適当なサンプル作ってjson投げてみる事にします。このへん書いてると大変なのでサックリとでお許しください。
actions on google
- サンプルアプリ作る
カテゴリは深く考えず、Games & funにしときました。
Display nameはとりあえずRust Sample。また後でちゃんと作ります。
- Custom intent
dialog flow
- Enable Webhook
なお、defaultのレスポンスはややこしいから消しました。
- webhook url
RequestとResponse
Dialog flowから投げられるRequestと期待されてるResponseを確認しておきます。Welcomeの例
{ "responseId": "e7036551-378f-4f7c-a177-3a715936c011", "queryResult": { "queryText": "こんにちは", "action": "input.welcome", "parameters": {}, "allRequiredParamsPresent": true, "intent": { "name": "projects/rust-sample-f3e72/agent/intents/xxxxxxxx-523d-4aa6-a864-49a6ce3236c3", "displayName": "Default Welcome Intent" }, "intentDetectionConfidence": 1, "languageCode": "ja" }, "originalDetectIntentRequest": { "payload": {} }, "session": "projects/rust-sample-f3e72/agent/sessions/xxxxxxxx-14f5-22d0-e2c2-0831282d5ce2" }
{ "fulfillmentText": "Welcome to my agent!", "outputContexts": [] }
jsonは一つネストしてるなー。どうやって取り出すんだろ。
やってみる
なんか適当にやってみたらとりあえずできました。use std::error::Error; use lambda_http::{lambda, Request, Response, Body}; use lambda_runtime::{error::HandlerError, Context}; use log::{self, info}; use simple_logger; #[macro_use] extern crate serde_derive; extern crate serde; extern crate serde_json; #[derive(Debug,Deserialize,Default)] struct QueryResult { #[serde(default)] queryText: String } #[derive(Debug,Deserialize,Default)] struct Req { #[serde(default)] queryResult: QueryResult } fn main() -> Result<(), Box<dyn Error>> { simple_logger::init_with_level(log::Level::Info).unwrap(); lambda!(my_handler); Ok(()) } fn my_handler(e: Request, c: Context) -> Result<Response<Body>, HandlerError> { let query: Req = serde_json::from_slice(e.body().as_ref()).unwrap(); info!("{:#?}", query); Ok( Response::new( format!( r#"{{"fulfillmentText": "you said {}"}}"#, query.queryResult.queryText ).into() ) ) }
でもアプリ側で日本語が文字化けしてる。。
こういう感じの問題でしょうか。
https://qiita.com/aoriso/items/a16a981d00d041c8f6a2
Lambdaで設定するレスポンスのヘッダに"charset=UTF8"を追加したら解決したとありますので、
Ok( Response::builder() .status(200) .header("Content-Type", "application/json; charset=UTF-8") .body( format!( r#"{{"fulfillmentText": "you said {}"}}"#, query.queryResult.queryText ).into() ) .expect("none") )
ちなみにCargo.tomlはこんな感じになってます。
[package] name = "hello" version = "0.1.0" edition = "2018" [dependencies] lambda_runtime = { git = "https://github.com/awslabs/aws-lambda-rust-runtime.git" } lambda_http = { git = "https://github.com/awslabs/aws-lambda-rust-runtime.git" } log = "^0.4" simple_logger = "^1" serde = "^1.0" serde_derive = "^1.0" serde_json = "^1.0"
もう中編だけど何作るかは決まっておりません。
しかし、C言語、C++に代わる言語って話だったけど、触ってみた感じはnodeとかの方が似てるかなー。と感じる。
でもってRustはまだexampleから見よう見まねしてるだけの段階なので何もわかっていません。作りながら調べつつ覚えていく感じなので生暖かく見守っていてくださいませ。
コメント
コメントを投稿