ユーザーの初期画像に便利なAPIを作りました

こんにちは。beaglesoftの真鍋です。

いま構築しているシステムで欲しい機能だけれどもなかったから作ってみたAPIにユーザー名に該当する文字列を設定すると特定のサイズの画像を生成するAPIを作りました。今回のAPIは引数に設定された文字を埋め込んで適当な背景の画像を作成します。

f:id:beaglesoft:20180519094745p:plain

Auth0とかどこかのOAuthとかを利用しているなら特に自分で用意する必要はないのですが、組み込みで認証を自分で用意しているとちょっと欲しいけど作るには面倒だなぁというものではないでしょうか。(私はASP.NET Securityを利用しています。)

利用するには

以下の通り実行すれば画像を取得できます。(Windowsの方はWSLなどでcurlを利用できるようにして実行してみてください。)

$ curl "https://api.camello.info/uc-ri/img?s=BS" -H 'x-api-key:gR8u8SlQj71huEjxn1RmF9PmsbVyrDhf4gzVDURC' -o ~/sample.png

f:id:beaglesoft:20180519092015p:plain

日本語もURLエンコードすると取得できます。

$ curl "https://api.camello.info/uc-ri/img?s=%E7%9C%9F%E9%8D%8B" -H 'x-api-key:gR8u8SlQj71huEjxn1RmF9PmsbVyrDhf4gzVDURC' -v -o ~/sample.png

f:id:beaglesoft:20180519094745p:plain

パラメーター

パラメーターは以下の通りとなります。注意点として、日本語はURLエンコードするようにしてください。

引数名 必須 データ型 内容 備考
s String 設定する文字 2文字程度のアルファベットまたは日本語を想定しています。

APIキー

APIGatewayを利用して公開していますが、APIキーは以下の通りとなります。

gR8u8SlQj71huEjxn1RmF9PmsbVyrDhf4gzVDURC

このキーは利用者全体で共有していますが、月間リクエストが10,000件まで利用可能としています。

画像サイズなど

画像サイズは今のところ150x150に固定しています。また、フォントはipag.ttfを利用して48ポイント固定です。背景色は以下の通り10種類作成してその中からランダムに利用しています。

def get_color_hash():
    color_list = [
        {'font': (255, 255, 255), 'bg': (248, 4, 6)},
        {'font': (255, 255, 255), 'bg': (204, 0, 10)},
        {'font': (255, 255, 255), 'bg': (229, 72, 0)},  # 緋色
        {'font': (255, 255, 255), 'bg': (39, 38, 114)},  # 藍色
        {'font': (255, 255, 255), 'bg': (57, 3, 124)},  # 紺藍
        {'font': (255, 255, 255), 'bg': (11, 43, 21)},  # セルリアンブルー
        {'font': (255, 255, 255), 'bg': (251, 231, 9)},  # 卵色
        {'font': (255, 255, 255), 'bg': (228, 176, 55)},  # ブロンド
        {'font': (255, 255, 255), 'bg': (228, 162, 11)},  # 山吹色
        {'font': (255, 255, 255), 'bg': (51, 96, 69)},  # 深緑
    ]

    r = random.randrange(10)
    print(f"r:{r}")
    return color_list[r]

システムの構成

このAPIはAWS LambdaでPythonを利用してバックエンドを作成し、API GatewayでWebAPIとして公開しています。Serverless Fraemworkを利用してます。

最後に

Serverlessというか、マイクロサービスというか、そういうものは仕組みが整っていると対象とする用途に強い言語で開発ができることはとても素晴らしいのではと思います。最初はとっつきづらい感じがあっても慣れれば便利なので是非試してみて楽しんでもらえればと思います。

追記

画像のフォントを変更しました。こちらを利用させていただきました。ありがとうございます。

jikasei.me

追記2

このAPIのソースコードや構成をまとめました!

blog.beaglesoft.net

Serverless FrameworkがAWS Lambdaを使いやすくしてくれる

Serverless Framework

Serverless Framework - Build applications on AWS Lambda, Google CloudFunctions, Azure Functions, AWS Flourish and moreはあまたあるクラウドベンダーのサーバーレスアプリケーションを効率よく開発するためのスキャフォールド(足場)やワークフローの自動化などを提供するCLIツールです。イメージとしてはDockerHubのLambda版という感じだと思います。

serverless/serverless: Serverless Framework – Build web, mobile and IoT applications with serverless architectures using AWS Lambda, Azure Functions, Google CloudFunctions & more! –

インストール

インストールはnpmコマンドで実行します。

npm install -g serverless

インストールが完了したらバージョンを確認します。

$ sls -v
1.27.2

createコマンド

Serverless Frameworkでは以下の通りServerlessアプリケーションの雛形を生成することができます。こうしてみるといろいろな雛形があります。

sls create --help
Plugin: Create
create ........................ Create new Serverless service
    --template / -t .................... Template for the service. Available templates: "aws-nodejs", "aws-nodejs-typescript", "aws-nodejs-ecma-script", "aws-python", "aws-python3", "aws-groovy-gradle", "aws-java-maven", "aws-java-gradle", "aws-kotlin-jvm-maven", "aws-kotlin-jvm-gradle", "aws-kotlin-nodejs-gradle", "aws-scala-sbt", "aws-csharp", "aws-fsharp", "aws-go", "aws-go-dep", "azure-nodejs", "fn-nodejs", "fn-go", "google-nodejs", "kubeless-python", "kubeless-nodejs", "openwhisk-java-maven", "openwhisk-nodejs", "openwhisk-php", "openwhisk-python", "openwhisk-swift", "spotinst-nodejs", "spotinst-python", "spotinst-ruby", "spotinst-java8", "webtasks-nodejs", "plugin" and "hello-world"
    --template-url / -u ................ Template URL for the service. Supports: GitHub, BitBucket
    --template-path .................... Template local path for the service.
    --path / -p ........................ The path where the service should be created (e.g. --path my-service)
    --name / -n ........................ Name for the service. Overwrites the default name of the created service.

最初のプロジェクト

今回は試しにAWS LabmdaへPython3.6を利用したサンプルを作成してみたいと思います。なお、利用する環境ではAWSのアクセスキーとアクセスキーIDはあらかじめ設定されていてaws cli でAWSへアクセスできることを確認してください。

sampleプロジェクトを作成するためcreateコマンドを利用します。

$ sls create -t aws-python3 -p sample

Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/Users/ymanabe/projects/sandbox/sample"
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v1.27.2
 -------'

Serverless: Successfully generated boilerplate for template: "aws-python3"

これでサンプルプロジェクトが作成されましたので、サンプルプロジェクトへ移動します。

$ cd sample
$ ls -al

total 24
drwxr-xr-x  5 ymanabe  staff   170  5 13 16:27 .
drwxr-xr-x  4 ymanabe  staff   136  5 13 16:27 ..
-rw-r--r--  1 ymanabe  staff   192  5 13 16:27 .gitignore
-rw-r--r--  1 ymanabe  staff   497  5 13 16:27 handler.py
-rw-r--r--  1 ymanabe  staff  2839  5 13 16:27 serverless.yml

このようにhandler.pyserverless.ymlというファイルが作成されています。

  • handler.py:Lambda関数として処理を記述するファイル
  • serverless.yml:Serverless FrameworkでLambdaを利用するための設定を記述するファイル

Lambda関数を作成する

ここまでで作成された内容をそのままAWS LambdaへLambda関数として作成してみたいと思いますが、このまま実行するとus-east-1にLambda関数が作成されます。できれば最初はap-northeast-1がいいという場合には以下の通り設定を変更してください。

@@ -23,7 +23,7 @@ provider:

 # you can overwrite defaults here
 #  stage: dev
-#  region: us-east-1
+  region: ap-northeast-1

 # you can add statements to the Lambda function's IAM Role here
 #  iamRoleStatements:

実際に実行してみます。実行はdeployコマンドで実行します。(下記はus-east-1で実行したログです。)

$ sls deploy -v -s dev
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
CloudFormation - CREATE_IN_PROGRESS - AWS::CloudFormation::Stack - sample-dev
CloudFormation - CREATE_IN_PROGRESS - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_IN_PROGRESS - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_COMPLETE - AWS::S3::Bucket - ServerlessDeploymentBucket
CloudFormation - CREATE_COMPLETE - AWS::CloudFormation::Stack - sample-dev
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (390 B)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
...
Serverless: Stack update finished...
Service Information
service: sample
stage: dev
region: us-east-1
stack: sample-dev
api keys:
  None
endpoints:
  None
functions:
  hello: sample-dev-hello

Stack Outputs
HelloLambdaFunctionQualifiedArn: arn:aws:lambda:us-east-1:123456789:function:sample-dev-hello:1
ServerlessDeploymentBucketName: sample-dev-serverlessdeploymentbucket-123456789

上記の通り

Serverless: Stack update finished...

と出力されればリリースは完了となります。

コンソールで確認する

正しくLambda関数が作成されたかをAWS コンソールで確認してみます。

image.png (259.4 kB)

Pythonのライブラリも含められる

Pythonを利用するときにはたいていNumPy — NumPyPillow — Pillow (PIL Fork) 5.1.1 documentationを利用します。これらはAWS Lambdaの実行環境には含められていないため、Lambda関数を作成するときにパッケージとして含める必要があります。

このような処理についてもServerless Frameworkは対応していてDockerコンテナを利用してAWS Lambdaで動作するパッケージを作成してデプロイまで自動化することができます。

この辺はまた別な機会にまとめたいと思いますが、とても便利なツールです。

さいごに

マイクロサービスの一環としてServerlessなAPIを構築することが結構多くなってきました。対象とする機能ごとにそれぞれ得意な言語を利用してAPIから呼び出せる仕組みは適材適所でありより一層利用する機会が増えてくると思います。

そんな中でServerless FrameworkのようなCLIツールが有ることはとてもありがたいことです。このお陰で処理を開発することに注力できるようになります。素晴らしいことですね。

補足

AWS LambdaがよくわからないときはいきなりServerless Frameworkとか利用しないでこちらの書籍を読むといいと思います。

実践AWS Lambda ~「サーバレス」を実現する新しいアプリケーションのプラットフォーム~

実践AWS Lambda ~「サーバレス」を実現する新しいアプリケーションのプラットフォーム~

言語ごとの説明もいくつかあるので実際にはNodeかPythonのサンプルをみて試してみて、実際に幾つかコンソールへ貼り付けたりS3からデプロイしてみてからのほうがServerless Frameworkの便利さはわかると思います。いずれにしてもAWS LambdaやAzure Functionsはとてもおもしろいので食わず嫌いせずに試してみてください!

C#でRestSharpを利用する

HttpClientはいろいろあるらしい

外部のRestAPIを実行しようとしたときにC#であればHttpClientを利用するのがいいかと漠然と思っていたのですが、意外と罠が多いことを知りました。

.NETのHttpClientの取り扱いには要注意という話 - Qiita

確かにMSのサイトを見てもそのようにstaticで利用しています。

Create a REST client using .NET Core | Microsoft Docs

First you need an object that is capable to retrieve data from the web; you can use a HttpClient to do that. This object handles the request and the responses. Instantiate a single instance of that type in the Program class inside the Program.cs file.

もちろんDI側でSingletonにして渡せばいいのですが、WebClientを利用するためだけにそうするのもなぁと思いRestSharpを利用することにしました。

RestSharp - Simple REST and HTTP Client for .NET

最初はJavaでも利用していたUnirestを利用しようと思ったのですが、C#ではこちらのほうが人気なようでこちらにしました。

ちょっと引っかかった

このRestSharpはいい感じに利用できるのですが、少し困ったこともありました。RestAPIを実行するときにPostなどのメソッドではJsonをリクエストボディに設定すると思います。

このRestSharpはJsonにしたいオブジェクトを渡せばいい感じにJson文字列にして送信してくれるのですが、この時に利用しているSerealizer/DesirealizerがNewtonsoft.Json.JsonSerializerではないのです。

RestSharp/JsonDeserializer.cs at develop · restsharp/RestSharp

 private object FindRoot(string content)
 {
            object json = SimpleJson.SimpleJson.DeserializeObject(content);

            if (!RootElement.HasValue()) return json;

            if (!(json is IDictionary<string, object> dictionary)) return json;

            return dictionary.TryGetValue(RootElement, out var result) ? result : json;
}

その結果、プロパティ名が変換されず送信先で400エラーとなるという状況になりました。ただ、このJsonSerializerは変更可能なのでNewtonsoft.Json.JsonSerializerに変更してうまく利用できるようになりました。

このJsonSeralizerを変更する方法は下記の説明が分かりやすかったです。

Custom JSON Serializer and JSON Deserializer for RestSharp

おわりに

JavaでもC#でもデフォルトで提供されているライブラリをそのまま利用したいとは思うのですが、痒いところに手が届かないというかちょっとそのままでは大変なことがおおくて同じ機能を実現しているライブラリを探して利用するようにしています。

ただ、最初はどうしてもつまずくことが多くドキュメントに向き合いつつStackOverflowで同じような事象を探しつつすすめることでうまく行ってます。

速習ASP.NET Core 速習シリーズ

速習ASP.NET Core 速習シリーズ