Laravel APIがアクセスできなくなるとき

筋肉をバキバキにしてもコロナ禍だと海にいく予定がありませんよね〜。(そもそもぷよぷよ)
マエダです。

みんな大好きPHP LaravelでAPIを開発した際に急にアクセスできなくなることがありました。
これはLaravelが「429 (Too many requests)」のエラーを出していたためでした。

なぜこのエラーが発生するかと調査したところ1ThrottleRequestsという機能、スロットル、つまりリクエストの絞り機能がLaravelにはあり、デフォルトで1分間に同一IPアドレス・同一ドメインに対して60リクエストとなるように設定されていたためでした。

app/Http/Kernel.php

protected $middlewareGroups = [
'web' => [

],
'api' => [

'throttle:60,1',
],
];

1分間60リクエストを緩和するか、コメントアウトして制限しないようにするとエラーは解消されます。
そもそも制限にかからないようリクエスト回数を減らすことができないか見直すことが重要です。

みんな大好きAWSにて、ELB + EC2なシステム構成ではIPアドレスについても考慮が必要です。
ThrottleRequestsは、上述の通りで同一IPアドレスを以下のようにハッシュ値でカウントアップしています。

sha1($route->getDomain().'|'.$request->ip())

そのため正しくLaravelでIPアドレスが確認できること($request->ip())が必要です。
Nginx + php-fpmの構成などではNginxで以下設定をしましょう。

/etc/nginx/conf.d/realip.conf

set_real_ip_from 172.30.0.0/16; # ELBのIPアドレス
real_ip_header X-Forwarded-For;

※ 設定変更後は、設定反映のためNginxをreloadしましょう。

Nginxの設定は弊社メンバーの以下記事も参照してみてください。

知らないと損をするNginx設定


WEBブラウザでFigmaのデザインを確認する方法

今回はFigmaのちょっとした小技です。
おそらく知ってる人も多いかと思いますが、よく使う便利な機能なのでご紹介します。

デザイン確認をする際のストレス

Figmaでwebサイトなどをデザインする際に、どうしても実際にブラウザで表示するのとサイズ感や見え方が変わってしまい、実際の仕上がりイメージを把握しづらいことがあるかと思います。
今までは作成したデザインを一度画像としてエクスポートして、その画像をブラウザで表示することで表示確認をしていました。
この方法ではデザインを変更する度に画像をエクスポートする必要があり、毎回この作業をしなければならず地味にストレスになっていました。

ライブプレビュー機能の活用

そこでFigmaのライブビュー機能を利用します。使い方は非常に簡単です。

(1)デザインワークスペースの右上にある [▷] ボタンをクリックします。

(2)Figma上でプレビュー画面が新しいタブで開きます。
(3)タブ名を右クリックして[Copy Link]を選択。
(4)Google Chromeなどのブラウザでコピーしたリンクにアクセスします。


これでブラウザでFigmaでデザインしているデザインの表示確認が、リアルタイムに可能になります。
デュアルディスプレイ環境で作業している場合だと、右をFigma、左をブラウザなどにしておけばシームレスにデザインをしながらブラウザでの表示確認が可能となり、効率的にデザイン作業を行うことができます。

おわりに

今までは画像をエクスポートしてブラウザに表示させて確認していたのですが、この方法を使えばその手間が省けて、作業の効率化を図ることができるようになりました。
また「ライブプレビュー」なので、Figmaでの変更がリアルタイムにブラウザでも確認できます。オンラインMTGなどでデザインを複数人で確認する場合などでも、プレビュー画面のURLを参加者に共有するだけで最新のデザインを確認・共有することができます。
その場でデザインの修正やアップデートができるので、メモを取ってあとからそれを見直しながらデザインを修正するという工程がなくなり、さらに修正漏れなどのミスも減らすことができます。

Figmaでデザインをしている方は、ぜひ使ってみてください!


LaravelのEloquentとコードの共通化に便利なTrait

ここ数年Laravelを使っているのですがとても味わい深いフレームワークです。普通に使う分には意識せずコードはかけますしこういった機能はないのか?と思って探してみると大体見つかりどんどん書き方が変わってきます。

今回は開発を続けていく上で同じようなコードが分散していかないようにする為の方法の1つを書き留めておきます。リレーションがたくさんできていくと同じコードを書いてしまうことがあります。最初はコピペで良くても後から振る舞いが少し変えるためには同じようなコードを全部修正する必要がでてきますし確認も大変です。

下記はブログとブログの記事の作成者の名前を取得する例のコードです。

class Blog extends Model
{
    use UserTrait;
}

class BlogEntry extends Model
{
    use UserTrait;
}

trait UserTrait
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function getUserNameAttribute()
    {
        if ($this->user) {
            // Userが存在するならユーザー名を返却
            return $this->user->name;
        }
        // Userが存在しない場合
        return '';
    }
}

こちら作成者が存在すれば名前をなければブランクを返すという簡単な流れです。Blog,BlogEntryともにuser_idで連結されており他にも同じようにuser_idによる連結がされる場合traitをuseするだけでいいので修正が楽です。こちらのいい所は途中でユーザーがいない場合ブランクではなく「退会されました」の文字列に連携するなどが容易なのに加えあとでユーザーに属性が追加されても呼び出す元は変更せずUserのTraitさえ変更すれば容易に追加できる点です。他にもAttribute以外にももちろんscopeやbootなど色々使えるでしょうしおすすめです。


AWS SESからS3へ受信してからのメール転送

東京オリンピックでの選手の活躍すごかったですね。(まだ開催されてないときに書いてます。)
マエダです。

みんな大好きAWSでメール受信はしたりしますよね。
https://aws.amazon.com/jp/premiumsupport/knowledge-center/ses-receive-inbound-emails/

S3にメールファイルが保存されたことをトリガーにpythonでメール転送する君を作成しました。
過去にどこからか(覚えておらず。。すみません。)参照させていただいたコードがpython2.7で動作するものだったのですが「AWS Lambda end of support for Python 2.7」なんてお知らせいただいたのでpython3.8で動作するように修正してみました。

import boto3
import email import re ORIGIN_TO = "(受信メールアドレス)" FORWARD_TO = "(転送先メールアドレス)" SES_REGION = "(SESリージョン)" S3_BUCKET = "(S3バケット名)" def parse_mail(raw_message): replaced_message = raw_message.replace(ORIGIN_TO, FORWARD_TO) replaced_message = re.sub("From:.+?\n", "From: %s\r\n" % ORIGIN_TO, replaced_message) replaced_message = re.sub("Return-Path:.+?\n", "Return-Path: %s\r\n" % ORIGIN_TO, replaced_message) return replaced_message def send_mail(message): ses = boto3.client('ses', region_name=SES_REGION) ses.send_raw_email( Source = ORIGIN_TO, Destinations=[ FORWARD_TO ], RawMessage={ 'Data': message } ) def lambda_handler(event, context): try: s3_key = event['Records'][0]['s3']['object']['key'] s3 = boto3.client('s3') response = s3.get_object( Bucket = S3_BUCKET, Key = s3_key ) raw_message = response['Body'].read().decode('utf-8') message = parse_mail(raw_message) send_mail(message) except Exception as e: print(e)

AWSをたのしもう。