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を見てみる。

まだなんとなくわかる程度です。


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") 
        } 
    }) 
} 
Cargo.tomlに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! 
で、https://github.com/awslabs/aws-lambda-rust-runtime/blob/master/lambda-http/src/ext.rs にexampleがあったのでそれを参考にいじってみた結果、とりあえず動いた。

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 
おっけー。json読めてる。

Rustはまだわかってないので見よう見まねですが。


Hello worldしてみる

そんなわけでgoogle homeに適当なサンプル作ってjson投げてみる事にします。

このへん書いてると大変なのでサックリとでお許しください。


actions on google

  • サンプルアプリ作る


58.png


カテゴリは深く考えず、Games & funにしときました。

Display nameはとりあえずRust Sample。また後でちゃんと作ります。

  • Custom intent
buildメニューではCustom intentを選択し、Dialog Flowを開きます。



59.png



dialog flow

  • Enable Webhook
アプリのネームと同じprojectを作ったのち、おもむろにDefault Welcome Intentを開いて、Enable webhook call for this intentを有効にし、いきなりwebhookに投げる設定にします。

なお、defaultのレスポンスはややこしいから消しました。



60.png


  • webhook url
fullfillmentを開いて、webhookの所にurlを入力します。



61.png



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": [] 
} 
ざっくりいうと、queryResult.queryTextを受け取ってfulfillmentTextを返せればおっけー。

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() 
    ) 
  ) 
} 


62.png


でもアプリ側で日本語が文字化けしてる。。

こういう感じの問題でしょうか。
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") 
  ) 
Response返すとこでContent-Type指定してやったら直りました。



63.png


ちなみに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から見よう見まねしてるだけの段階なので何もわかっていません。作りながら調べつつ覚えていく感じなので生暖かく見守っていてくださいませ。

コメント

このブログの人気の投稿

投稿時間:2021-06-17 05:05:34 RSSフィード2021-06-17 05:00 分まとめ(1274件)

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2020-12-01 09:41:49 RSSフィード2020-12-01 09:00 分まとめ(69件)