- 投稿日:2020-09-18T23:50:29+09:00
【Rails6.0】タグ検索機能(タグで絞り込み機能)を実装してみた【gem無し】
現在個人開発しているアプリにタグ検索機能を実装したのでここに備忘録としてまとめさせていただきます。
あえてgemを使っていないので本質的なところまで理解することができました。【環境】
- windows10 Pro
- Rails: 6.0.3.2
- ruby: 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-linux]
- Docker for windows
- MySQL 5.7
- nginx:1.15.8機能の概要
新規投稿フォームにタグを入力すると、入力したタグが投稿一覧とサイドバーに反映されます。
サイドバーに反映されたタグをクリックするとそのタグが付いた投稿を絞り込むことができるという機能です。↑で入力したタグがサイドバーと投稿一覧に表示される
サイドバーに表示されたタグをクリックするとそのタグを含む記事を絞り込むことができる。(今回はサンプルなので一件のみ)
実装手順
Itemモデル及びPostモデルなど作成済みという前提で話しをします。(ここではitemモデルとします。各自置き換えてください。)
まず、Tagモデル、Tagmapモデルをいつものように作成します。
$ rails g model Tagmap item:references tag:references $ rails g model tag tag_name:stringTagmapはItemモデルとTagモデルに紐づくのでデータ型にreferencesを付けてます。
これをするとRailsが良きにはからってくれますので詳しく知りたい方はググってみてください。マイグレーションファイル↓
class CreateTagmaps < ActiveRecord::Migration[6.0] def change create_table :tagmaps do |t| t.references :item, null: false, foreign_key: true t.references :tag, null: false, foreign_key: true t.timestamps end end endこちらはインデックスを付けます。
class CreateTags < ActiveRecord::Migration[6.0] def change create_table :tags do |t| t.string :tag_name, null: false t.timestamps end add_index :tags, :tag_name, unique: true end endその後いつものようにマイグレーションをします。
$ rails db:migrateアソシエーションを設定します。
item.rbhas_many :tagmaps, dependent: :destroy has_many :tags, through: :tagmapstag.rbclass Tag < ApplicationRecord has_many :tagmaps, dependent: :destroy has_many :items, through: :tagmaps endtagもtagmapsと関連付けさせています。↑
thoroughを使うことで、tagmaps経由でitemsにアクセスできるようになってます。
tagmap.rbclass Tagmap < ApplicationRecord belongs_to :item belongs_to :tag end↑references形にするとbelongs_toの部分をRailsが自動で記述してくれます!
items_controller.rbdef index if params[:search].present? items = Item.items_serach(params[:search]) elsif params[:tag_id].present? @tag = Tag.find(params[:tag_id]) items = @tag.items.order(created_at: :desc) else items = Item.all.order(created_at: :desc) end @tag_lists = Tag.all @items = Kaminari.paginate_array(items).page(params[:page]).per(10) endこちらのコードの解説をします。
indexアクションでif文を使い3パターン場合分けしていて、この結果によりitem変数に代入される値を変えています。
1.検索フォームに入力があった場合
2.paramsで:tag_idを受け取った場合(クリックしたときにパラメータでtag_idを受け取ったときに発動する。タグで絞込む機能)
3.普通にページを表示させた場合
ページネーション機能も設定しているので、item変数に格納される変数が3パターンになり、それぞれ最適化されたものが表示できるようになっています。
ページネーションではKaminariというgemを使っております。items_controllerdef create @item = Item.new(item_params) tag_list = params[:item][:tag_name].split(nil) @item.image.attach(params[:item][:image]) @item.user_id = current_user.id if @item.save @item.save_items(tag_list) redirect_to items_path else flash.now[:alert] = '投稿に失敗しました' render 'new' end enditemsコントローラーのcreateアクションでのポイントは3行目のtag_list変数への代入部分です。
rubyのString#split は第 1 引数(今回はnil)で指定されたセパレータによって文字列を分割し、結果を文字列の配列で返します。ブロックを指定すると、配列を返す代わりに分割した文字列でブロックを呼び出します。
また、1 バイトの空白文字 ' 'で区切られたタグデータをフォームから送ると先頭と末尾の空白を除いたうえで、空白文字列で分割します。今回はこれを利用します。新規投稿フォームから送られてきたパラメーターを空白文字(nil)でセパレイトして配列化し、Itemクラスで定義したsave_itemsクラスメソッドを使い、データベースに保存します。(フォームにはタグをスペース区切りで入力してもらうように使用者にお願いする。)
item.rb#検索メソッド、タイトルと内容をあいまい検索する def self.items_serach(search) Item.where(['title LIKE ? OR content LIKE ?', "%#{search}%", "%#{search}%"]) end def save_items(tags) current_tags = self.tags.pluck(:tag_name) unless self.tags.nil? old_tags = current_tags - tags new_tags = tags - current_tags # Destroy old_tags.each do |old_name| self.tags.delete Tag.find_by(tag_name:old_name) end # Create new_tags.each do |new_name| item_tag = Tag.find_or_create_by(tag_name:new_name) self.tags << item_tag end end↑タグデータを保存するとき、フォームから送られてきたタグデータのうち、すでに存在するタグネームがひとつでもあった場合はtagsテーブルのtag_nameカラムからpluckメソッドを使い一旦すべてのデータを引っ張ってきてcurrent_tagsに代入します。(すべて新しいものだった場合はnilになる。)
そしてすでに存在するタグデータの集合であるcurrent_tagsから、コントローラーから引数で渡ってきたtagsの配列を引くと古いタグ(old_tags)を定義することができます。
rubyでは配列の引き算をすると共通する要素を取り出すことができるためです。具体例:
tags(tag_list = params[:item][:tag_name].split(nil)でコントローラーから渡ってくる配列) = ["Rails" "ruby" "React"] current_tags(現在DBに存在しているタグデータ) = ["Rails" "ruby" "Vue.js"] old_tags = ["Rails" "ruby" "Vue.js" "Docker"] - ["Rails" "ruby" "React"] old_tags = ["Rails" "ruby"] ↑二つの配列の共通の要素が残る(すでに存在している古いタグを算出することができる)サイドバーのビュー一部抜粋(CSSフレームワークにUIkitというのを使ってます)
index.html.haml%li.search_friend_by_categorize .uk-text-secondary.uk-text-bold タグで探す %ul.uk-flex.uk-flex-wrap - @tag_lists.each do |list| %li = link_to list.tag_name, items_path(tag_id: list.id), class: 'tag_list'投稿記事一一覧へのタグの表示
index.html.haml%p.tag_list_box - item.tags.each do |tag| = link_to "##{tag.tag_name}", items_path(tag), class: 'smaller tag_list'viewはこのような感じになります。特に難しいことはないですね!
最後まで読んでいただきありがとうございます!
少し駆け足で書いたので間違いなどあるかもしれません。もしなにかご指摘や感想などあればコメントいただけると嬉しいです!!
- 投稿日:2020-09-18T22:48:23+09:00
コメント機能に削除機能を追加したい
【概要】
1.結論
2.どのように実装したのか
3.今後していきたいこと
1.結論
❶commentコントローラーのdestroyアクションに、投稿したidを紐づけて削除できるようにする。
❷ネスト化したpathにlink_toで削除ボタン押下できるようにする。
2.どのように実装したのか
<開発環境> Ruby: 2.6.5 Rails: 6.0.3.3controllers/comments_controller.rbdef destroy redirect_to root_path unless user_signed_in? || current_user.id == @time.user_id && current_user.id == comment.user_id #---(1) @comment = Comment.find(params[:time_id]) if @comment.destroy #---(2) redirect_to time_path #---(3) else redirect_to root_path #---(4) end end❶commentコントローラーのdestroyアクションに、投稿したidを紐づけて削除できるようにしました。
(1)ではログアウトしてる人や、自分以外の投稿者が自分の投稿のコメントを削除される(直接URLの入力して削除される)のを防ぐためです。
(2)は自分が投稿したコメントの削除を実行します。
(3)は成功すればコメント投稿画面に戻ります。
(4)はコメントの削除に失敗すればトップページに戻ります。view/time/show.html.erb<% if @comments %>#---(1) <% @comments.each do |comment| %>#---(2) <p> <%= comment.user.name %>:#---(3) <% if user_signed_in? && current_user.id == @time_report.user_id && current_user.id == comment.user_id %> <%= link_to '❌', time_report_comment_path(comment.id), method: :delete %> #---(4) <%= comment.content %> #---(5) <% end %> </p> <% end %> <% end %>❷ネスト化したpathにlink_toで削除ボタン押下できるようにしました。
(1)は"もしコメントがあれば"ということで、インスタンス変数でtimeコントローラーから持ってきています。
(2)User➡︎commentsはhas_manyなのでたくさんあるコメントを持ってきています。
(3)その中でも紐づけられたコメントしたユーザー名だけ表示しています。
(4)ルーティングでコメントをネスト化しているので自分で投稿した自分のコメントを自分で削除できるようにしています。
(5)コメントの内容を表示しています。
3.今後していきたいこと
SPAを実装したいです。吹き出しマークをクリックしたら、非同期的(ページ遷移がなく)にコメントが表示され、そこで編集や削除ができるようなアプリケーションにしたいです。そのためにはNuxt.js/Vue.jsの知識が必要になってくるのでコツコツ勉強していきます。
- 投稿日:2020-09-18T19:50:25+09:00
Git管理中のRailsプロジェクトをCloud9に環境構築
きっかけ
元々MacOS環境で作成していたRailsアプリをWindowsでも作業したいと思ったが、WindowsでやるならAWSでやってみたら簡単じゃないかということで久しぶりにCloud9で環境構築をした。
環境
AWS Cloud9
Ruby 2.7.0
Ruby on Rails 6.0.3
Chrome(ブラウザは何でもいい)Cloud9にインスタンスを作成
AWSへのIAMユーザーでサインインは済ませておく。(これがまずよくわからない問題。)
サービスの中の下の方にCloud9があるのでクリック。Nameに好きなアプリ名を入れる。すでにGit管理されているプロジェクトの名前でいいでしょう。
今回はSample_appとしました。その後Next step。
セッティングの中のPlatformはUbuntu Serverを選択。その他はデフォルトでOK。
Rails6を使うときはSQLite3のバージョンの都合でAmazon Linuxでは無理なため決して選ばないように。
そしてNext step。確認画面に移動するので、Crreate environment!
しばらくすると以下のような画面になってまずはプログラムを置く場所を作りました。このタブは不要なので消してOK。
プログラムをクローンする
+ボタンをクリックしてNew Terminalを選択。ターミナルが立ち上がります。
以下を行うことでドキュメントのダウンロードを行わないので、インストールが早まるそうです。$ echo "gem: --no-document" >> ~/.gemrcクローン!
$ git clone GitリポジトリのURLクローン出来ました。
では、クローンしたプログラムのディレクトリに移動します。$ cd プログラムのディレクトリ名そうすると、以下のように注意を促されます。
Required ruby-2.7.0 is not installed. To install do: 'rvm install "ruby-2.7.0"'Ruby 2.7.0が必要だよ!
Ruby 2.7.0のインストールを行う
今回使用したいRubyのバージョンは2.7.0、Railsは6.0.2。開発中のプログラムに合わせる。
デフォルトでインストールされているRubyとバージョンが違うので、この環境にインストールします。
素直に行きます。$ rvm install "ruby-2.7.0"YARNのダウンロードとインストール
$ source <(curl -sL https://cdn.learnenough.com/yarn_install) $ yarn install --check-files上記がうまくいったら下のコマンドはスキップしてBundlerのインストールへ。
これでうまくいかないこともあるので、以下でも可能。$ curl -o- -L https://yarnpkg.com/install.sh | bash以下のような画面が出て、インストールが完了。新しいターミナルを開いたら使えるよって書いてありますね。
では、新しいターミナルを+ボタンを押して開いてみます。
$ yarn --version1.22.5と出たので、インストールOK。
Railsなどのgemをインストール
まずはBundler のインストールから。プログラムのGemfileの内容によってちょっと時間かかります。
$ bundle installRailsがインストールされているか確認します。
$ rails --version6.0.3.1と出たのでOK。
Rails6 から特有のwebpackerをインストールします。
$ rails webpacker:installCloud9の開発環境で画面にアクセスできるようにする
以下のファイルに追記します。画面確認のためにはブラウザでCloud9の環境にアクセスする必要があるので。
config/environments/development.rb
一番下のendの一段上。config.hosts.clear既存のプロジェクトなので以下のコマンドを忘れずに。
rails db:migrateついにプログラム起動!
$ rails s画面を確認するには上の方のPreviewタブ>Preview Running Applicationをクリック。
その後赤枠部分をクリックするとブラウザが別タブで起動します。
よし!この画面が今回利用したプログラムのデフォルト画面です。これが出たので、この調子で開発が進められます!
以下のサイトを運営しています。
https://dokusyo-no-wa.com/
こちらの開発をMacとWindowsPCと両方でやれたら便利だなと思ったのがきっかけ。参考記事
https://skillhub.jp/courses/134/lessons/785 (Cloud9でRails6の準備)
https://happy-teeth.hatenablog.com/entry/2019/09/17/110208 (SQLite3を利用するにはAmazon Linuxではできない。)
https://qiita.com/8zca/items/175efb0612070530d186 (CentOS環境でRails6.0をSQLite3 (>=3.8)で動かす)
- 投稿日:2020-09-18T18:55:57+09:00
instagramのクローンアプリを作る④
はじめに
タイトルの通り、簡易版instagramのアプリを作っていきます。
下記の工程に分けて記事を執筆していきますので、順を追って読んでいただけたらなと思います。①アプリ作成〜ログイン機能の実装
②写真投稿機能の実装
③ユーザーページの実装
④フォロー機能の実装 ←イマココ
⑤投稿削除機能の実装モデル作成
※以下、アプリケーションのディレクトリで
ターミナルrails g model follow user:belongs_to target_user:belongs_toマイグレーションファイルを修正します。
target_user
のforeign_key
をfalseに。db/migrate/2020**********_create_follows.rbclass CreateFollows < ActiveRecord::Migration[6.0] def change create_table :follows do |t| t.belongs_to :user, null: false, foreign_key: true t.belongs_to :target_user, null: false, foreign_key: false t.timestamps end end end修正できたら
rails db:migrate
を実行します。リレーションの設定
followモデル
にリレーションを設定していきます。
target_user
にclass_name
とforeign_key
を指定します。app/models/follow.rbclass Follow < ApplicationRecord belongs_to :user belongs_to :target_user, class_name: 'User', foreign_key: 'target_user_id' end
userモデル
にもリレーションを設定していきます。
active_relationships
passive_relationships
を記述していきます。app/models/user.rbclass User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable has_many :active_relationships, class_name: 'Follow', foreign_key: 'user_id' has_many :passive_relationships, class_name: 'Follow', foreign_key: 'target_user_id' has_many :followings, through: :active_relationships, source: :target_user has_many :followers, through: :passive_relationships, source: :user has_many :photos endこれでリレーションは完成です。
followsコントローラ作成
ターミナルrails g controller followsルーティングの設定も忘れずに行います。
routes.rbRails.application.routes.draw do root 'homes#index' devise_for :users resources :photos resources :users do resource :follow # ←ここ end endこれでURLは
user/user_id/follow
という感じになると思います。次に、
followsコントローラ
を編集していきます。folows_controller.rbclass FollowsController < ApplicationController before_action :authenticate_user! def create current_user.active_relationships.create(target_user_id: params[:user_id]) redirect_to [:user, {id: params[:user_id] }] end def destroy current_user.active_relationships.find_by(target_user_id: params[:user_id]).destroy redirect_to [:user, {id: params[:user_id] }] end end
before_action :authenticate_user!
とすることで、
ログインしているユーザーのみ「フォロー/フォロー解除」することができます。
createアクション
でフォローの動きを設定します。
リダイレクト先は[:user, {id: params[:user_id] }]
として、
userのuser_id、つまり ユーザーの詳細ページに遷移するよう設定しています。
destroyアクション
でフォロー解除の動きを設定します。
create
ではなく、まずfind_by
でフォロー中のレコードを取得します。
そして、destroy
で取得してきたレコードを破壊します。
リダイレクト先は同じで大丈夫だと思います。viewファイルにフォロー/フォロー解除リンクを作成
app/views/users/show.html.erb<h3><%= @user.email %></h3> # ↓↓↓↓↓↓↓↓↓↓ ここから ↓↓↓↓↓↓↓↓↓↓ <% if current_user.active_relationships.exists?(target_user_id: @user.id) %> <%= link_to 'unfollow', [@user, :follow], method: :delete %> <% else %> <%= link_to 'follow', [@user, :follow], method: :post %> <% end %> # ↑↑↑↑↑↑↑↑↑↑ ここまで ↑↑↑↑↑↑↑↑↑↑ <div> <%= link_to 'followings', [@user, :followings] %> </div> <div> <%= link_to 'followers', [@user, :followers] %> </div> <% @user.photos.each do |photo| %> <div> <p><%= photo.caption %></p> <%= image_tag photo.image %> </div> <% end %>条件分岐で、
current_user
がユーザーを既にフォローしていた場合は
フォロー解除用のリンクを表示させます。→ method: delete
まだフォローしていない場合はフォロー用のリンクを表示させます。→ method: postここまでで一度ブラウザで確認してみます。
下記のようになっていれば成功です。
ユーザー詳細ページにとび、follow
を押すとフォロー完了。
そして、follow
がunfollow
に変わります。
unfollow
を押すとフォロー解除となり、follow
に戻ります。
ただ、この状態だとログインしていないユーザーが
URLに直接アクセスするとエラーが起きてしまいます。↓
これは条件分岐で
current_user
としているためです。
ですので、更に条件分岐を追加していきます。
先ほどの条件分岐を↓で挟みます。
<% if user_signed_in? && current_user != @user %>
<% end %>
app/views/users/show.html.erb<h3><%= @user.email %></h3> <% if user_signed_in? && current_user != @user %> # ←ここ <% if current_user.active_relationships.exists?(target_user_id: @user.id) %> <%= link_to 'unfollow', [@user, :follow], method: :delete %> <% else %> <%= link_to 'follow', [@user, :follow], method: :post %> <% end %> <% end %> # ←ここ <div> <%= link_to 'followings', [@user, :followings] %> </div> <div> <%= link_to 'followers', [@user, :followers] %> </div> <% @user.photos.each do |photo| %> <div> <p><%= photo.caption %></p> <%= image_tag photo.image %> </div> <% end %>
ユーザーがサインインしている
且
current_userがユーザー詳細ページのユーザーでないこと
という条件分岐を追記したことになります。
これで、サインインしていないと「フォロー/解除」のリンクが表示されないようになります。
また、自分の詳細ページに飛んだ時も表示されないようになります。これでフォロー機能はほとんど完成ですが、
最後に、フォローされているユーザーの一覧を表示させたいと思います。followings/followersコントローラの作成
ターミナルrails g controller followingsターミナルrails g controller followersルーティングも忘れずに設定します。
routes.rbRails.application.routes.draw do root 'homes#index' devise_for :users resources :photos resources :users do resource :follow resources :followings # ←ここ resources :followers # ←ここ end endコントローラにもそれぞれ記述していきます。
folowings_controller.rbclass FollowingsController < ApplicationController def index @followings = User.find(params[:user_id]).followings end endfolowers_controller.rbclass FollowersController < ApplicationController def index @followers = User.find(params[:user_id]).followers end endそしてviewを作成していきます。
それぞれlink_to
でfollowing/follower
の
ユーザー詳細ページへのリンクを作成しています。app/views/followings/index.html.erb<% @followings.each do |following| %> <div> <%= link_to following.email, [following] %> </div> <% end%>app/views/followers/index.html.erb<% @followers.each do |follower| %> <div> <%= link_to follower.email, [follower] %> </div> <% end%>pathの指定は
[following]
→user_followings_path
[follower]
→user_followers_path
でも良いのでわかりやすい方で。最後にユーザー詳細ページにリンクを作成します。
app/views/users/show.html.erb<h3><%= @user.email %></h3> <% if user_signed_in? && current_user != @user %> <% if current_user.active_relationships.exists?(target_user_id: @user.id) %> <%= link_to 'unfollow', [@user, :follow], method: :delete %> <% else %> <%= link_to 'follow', [@user, :follow], method: :post %> <% end %> <% end %> # ↓↓↓↓↓↓↓↓↓↓ ここから ↓↓↓↓↓↓↓↓↓↓ <div> <%= link_to 'followings', [@user, :followings] %> </div> <div> <%= link_to 'followers', [@user, :followers] %> </div> # ↑↑↑↑↑↑↑↑↑↑ ここまで ↑↑↑↑↑↑↑↑↑↑ <% @user.photos.each do |photo| %> <div> <p><%= photo.caption %></p> <%= image_tag photo.image %> </div> <% end %>表示させるページのpathは
@user
のfollowings
@user
のfollowers
となります。これで動作を確認してみます。
以下のようになっていれば成功です。
速くててわかりづらいんですが、sampleでログインしている状態から始まっています。
①sample
がexample
の詳細ページに飛びフォローする。
②sampleの詳細ページでfollowings
のリンクへ飛ぶと、今フォローしたexample
が確認できる。
③exampleの詳細ページでfollowers
のリンクへ飛ぶと、sample
が確認できる。
以上です。お疲れ様でした。
- 投稿日:2020-09-18T18:37:39+09:00
Grapeを使ってRESTfulAPIサービスを作成する
概要
grapeを使用してRESTfulなAPIサービスを作成したのでまとめる。
また作成後はPostmanを使用して挙動を確認した。環境
ruby 2.6.5 Rails 5.2.4実施したこと
・任意のUserをSeedsファイルに作成。gemはgrapeを使用。APIモードにて作成する。
・Postmanを使ってクライアントからアクセスし、データの登録、更新、削除。データの取得を行う
・アプリケーションには一般的なCRUD機能とデータの取得をJSONを用いて取得できるよう作成したまずアプリケーションを作成
console$ rails new sample_app今回はアプリ名「sample_app」とした。
まずはgemをインストール
下記を追記
Gemfilegem ‘grape’ # rails、sinatraなどのRackアプリケーションで、RESTfulAPIを簡単に構築できる gem 'grape-entity’ # grapeのみだと、値が全て表示されてしまう。grape-entityを使うと、表示項目の制限、整形をすることができる。 gem ‘grape_on_rails_routes’ #使用すると、Grape APIによって作成されたルートを簡単に読み書きできる。続い bundle instalする。
$ bundle install --path vendor/bundleDBの構成
Usermodelを生成する
console$ bundle exec rails g model User name:string email:string age:integer $ rails db:migrateサンプル用にデータを用意します。
db/seeds.rbを下記の内容で作成します。
db/seeds.rbUser.create(name: 'Sony', email: 'sony@sample.com', age: '40') User.create(name: 'Fredo', email: 'fred@sample.com', age: '38') User.create(name: 'Tom', email: 'tom@sample.com', age: '35') User.create(name: 'Michael', email: 'michael@sample.com', age: '33') User.create(name: 'Connie', email: 'connie@sample.com', age: '30')DBにファイルを読み込ませます。
$ bundle exec rake db:seedディレクトリ構成
下記のディレクトリ構成にします。
~/sample_api/ ├ app/api/ │ └ api.rb │ └ resources/ │ └ v1/ │ └ root.rb │ └ user.rb │ └ entities/ │ └ v1/ │ └ root_entity.rb │ └ user_entity.rbautoloadの設定
app/apiの下のファイルがautoloadされるようにします。
config/application.rbmodule SampleApi class Application < Rails::Application #.. # app/api以下のrbファイルをautoload + config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb') + config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')] end endAPIの作成
下記の図のような[親-子-孫-ひ孫]の関係でマウントさせます。
config/routes.rb # [親]ルーティングの設定 └ app/api/api.rb # [子]API親クラス └ app/api/resources/v1/root.rb # [孫]API v1の親クラス └ app/api/resources/v1/users.rb # [ひ孫]userAPIのクラス[親] config/routes.rb
ルーティングの設定をします。
config/routes.rbRails.application.routes.draw do # app/api/api.rbをマウント + mount API => ‘/' #この方法では、基本クラスがクラスの各アクションをポイントするため、各アクションにルートを追加する必要はありません。 end[子] app/api/api.rb
API親クラスを作成します。
app/api/api.rbclass API < Grape::API # urlの第1セグメント名 ex) http://localhost/api/ prefix 'api' # app/api/resources/v1/root.rbをマウント mount Resources::V1::Root end[孫] app/api/resources/v1/root.rb
API v1の親クラスを作成します。
app/api/resources/v1/root.rbmodule Resources module V1 class Root < Grape::API version 'v1' format :json content_type :json, 'application/json' # app/api/resources/v1/users.rbをマウント mount Resources::V1::Users end end end
続いてuserAPIのクラスを作成する前にEntityファイルを作成します。
これを作成しないとデータ取得を行った際に作成時間や更新時間など、表示したくないデータが表示されてしまいます。
*全てのデータを取得したい場合は作成不要。Entityの親クラスを作成
app/api/entities/v1/root_entity.rbmodule Entities module V1 class RootEntity < Grape::Entity end end enduserAPIのEntityファイルを作成
name, email, ageのみ表示するようにします。
(created_at、updated_atは表示させない)
app/api/entities/v1/user_entity.rbmodule Entities module V1 class UserEntity < RootEntity # name, email, ageのみ表示する expose :name, :email, :age end end enduserAPIのクラスを作成します。
app/api/resources/v1/user.rbmodule Resources module V1 class Users < Grape::API resource :users do desc "user list" get do present User.all, with: Entities::V1::UserEntity # with:〜〜とすることでentityファイルの内容を適用させます end desc "create new user" params do requires :name, type: String requires :email, type: String requires :age, type: Integer end post do User.create!({ name: params[:name], email: params[:email], age: params[:age] }) end desc "Update user" route_param :id do put do User.find(params[:id]).update({ name: params[:name], email: params[:email], age: params[:age] }) end end desc "show user" params do requires :id, type: Integer, desc: "user id" end get ":id" do present User.find(params[:id]), with: Entities::V1::UserEntity # こちらのデータ取得も同様です。 end desc "Delete user" route_param :id do delete do user = User.find(params[:id]) user.destroy end end end end end end$ rails grape:routes or rake grape:routes GET | /api/:version/users(.:format) | v1 | user list POST | /api/:version/users(.:format) | v1 | create new user PUT | /api/:version/users/:id(.:format) | v1 | Update user GET | /api/:version/users/:id(.:format) | v1 | user_show DELETE | /api/:version/users/:id(.:format) | v1 | Delete user上記のコマンドを実行すると、グレープAPIルートのリストを確認できます。
ここまででAPIを作成できました。ここからはPostmanを使用することで実際にCRUD機能、データ取得を正しく行えるか確認していきます。
- user情報を全て取得する
- 指定したidのuser情報を1件取得する
rails起動後上記URIを入力すれば取得の確認が行える。
Postmanを使ってリクエスト、レスポンスの確認
PostmanのURL
https://www.postman.com/
Postmanを起動
登録は指示に従っていけば簡単に行えます。ローカルサーバー起動
$ rails sデータを作成(Create)
Bodyに登録するデータをJSON形式で設定して、実行する
メソッド:Post
URL:http://localhost:3000/api/v1/users
Body: raw JSON(application/json)
Body{ “name”: “サンプルさん” “email”: “sample@example.com” “age”: 19 }下記欄に6番目のidで新たにuserが登録されていることが分かる。
データの編集(update)
登録した情報を更新する。
Body要素に変更する情報を設定する。メソッド:Put
URL:http://localhost:3000/api/v1/users/:id
(:idはdbに登録されているid値 ここでは6を設定)Body:raw JSON(application/json)
Body{ “name”: “サンプル” “email”: “sample1@example.com” “age”: 20 }updateが行われている。
データの削除(destroy)
登録している情報を削除する
メソッド:Delete
URL:http://localhost:3000/api/v1/users/:id
(:idはdbに登録されているid値 ここでは6を設定)
先程作成したデータを削除することに成功した。
データの取得(index)
登録した情報をすべて取得する。Seedsデータとして登録されている5つのデータを取得してみる。
メソッド:Get
URL:http://localhost:3000/api/v1/users
下記に登録されているデータが確認できる。
データをid別に取得(show)
登録した情報からidを指定して、特定の情報のみを取得する(削除される前のid:6のデータ情報を取得しています。)
メソッド:Get
URL:http://localhost:3000/api/v1/users/:id
CRUD機能の挙動については確認することができた。Postmanの機能についてはまだ勉強不足であるので深めていく。
またgrape gemについてもまだ様々な機能があるのでそこも学習し、使いこなせるようにしていく。
*参考 API開発・テスト便利ツール Postmanの使い方メモ
https://qiita.com/zaburo/items/16ac4189d0d1c35e26d1*参考 Postmanを使ってAPIの確認をしてみる
https://qiita.com/me-654393/items/b14824dd9b37de0da163*参考 RESTFUL API USING GRAPE IN RAILS
https://www.nascenia.com/restful-api-using-grape-in-rails/*参考 grapeのgithub
https://github.com/ruby-grape/grape
- 投稿日:2020-09-18T18:20:15+09:00
【Rails】複雑なルーティング構成まとめ
今回は、
Usersリソース
を中心に、追加構成としてAPIディレクトリ
を追加してみたり、新たなメソッドとしてsortメソッド
を追加したときのパスの状態などを設定するパターンで見ていく早見表
種類 パス クラス ファイルパス scope /api/users UsersController /users_controller.rb namespace /api/users Api::UsersController /api/users_controller.rb module /users Api::UsersController /api/users_controller.rb member /users/:id/sort collection /users/sort Controllersのディレクトリ構成と、ルーティング
scope
routes.rb# /api/users scope :api do resources :users endディレクトリ構成/controllers-- | |--application.rb | |--users_controller.rbcontroller.rbclass UsersController < ApplicationController endnamespace
routes.rb# /api/users namespace :api do resources :users endディレクトリ構成controllers/- | |--application.rb | |--api/- | |--users_controller.rbcontroller.rbclass Api::UsersController < ApplicationController endmodule
routes.rb# /users scope module: :api do resources :users endディレクトリ構成controllers/- | |--application.rb | |--api/- | |--users_controller.rbcontroller.rbclass Api::UsersController < ApplicationController endまったく新しいパスを作る
member
routes.rb# idを伴うパス /users/:id/sort resources :users do member do get :sort end end省略形
routes.rbresources :users do get :sort, on: :member endcollection
routes.rb# idを伴わないパス /users/sort resources :users do collection do get :sort end end省略形
routes.rbresources :users do get :sort, on: :collection end参考記事
- 投稿日:2020-09-18T16:23:02+09:00
SentryでRailsアプリのエラーをトラッキング
契約
以下より契約
https://sentry.io/signup/契約時にプロジェクトを作成することになり、プロジェクト作成後の画面にセットアップ方法が書かれているので、手順通りに進めていけば問題無いはず。ただ念のため自分ようにまとめなおします。
基本的な使い方
Gemfilegem "sentry-raven"$ bundle installconfig/application.rbmodule AppName class Application < Rails::Application # 省略 Raven.configure do |config| config.dsn = "https://#{ENV['SENTRY_KEY']}@#{ENV['SENTRY_SECRET']}.sentry.io/#{ENV['SENTRY_ID']}" end end endSentryのサイトで表示されるセットアップ方法には、ID等が全て直書きされているので、環境変数にしまいました。
パラメーターやsession情報を拾えるようにする
app/controllers/application.rbclass ApplicationController < ActionController::Base before_action :set_raven_context private def set_raven_context Raven.user_context(id: session[:current_user_id]) # or anything else in session Raven.extra_context(params: params.to_unsafe_h, url: request.url) end endパスワードを平文で記録しない
$ touch config/initializers/sentry.rbconfig/initializers/sentry.rbRaven.configure do |config| config.sanitize_fields = Rails.application.config.filter_parameters.map(&:to_s) end参考
その他
- 投稿日:2020-09-18T14:17:14+09:00
第4回 SmartHRのライブラリkijiを使ってe-Govを動かす(動作確認編)
ドライバーからSmartHRのライブラリkijiを呼び出し、署名付きxmlを生成します。さらにcurlコマンドで検証環境に署名付きxmlを送信して、e-Govのレスポンス等を確認します。
curlコマンドでは、以下のパラメタ、リクエストURIを指定して、コマンドプロンプトから起動します。
No パラメタ、リクエストURI 説明 1 -X GET又はPOST 2 -d @署名付きxmlファイル名 POSTコマンド時のみ指定 3 -H "x-eGovAPI-AccessKey アクセスキー" ユーザ認証で発行されたキーを指定 4 -H "x-eGovAPI-SoftwareID: ソフトウェアID" 5 -H "Authorization: Basic認証用情報" Basic認証用ID:パスワードのBase64形式でエンコードした値 6 リクエストURI 検証環境向けのhttps形式URI 最後にe-Govの問題点を定義した上で、マイナポータルAPIで改善できるか考察します。
1 ユーザ認証
署名付きxmlを生成した上で、ユーザ認証を行います。
1.1 署名付きxmlを生成する
ドライバーを実行して、register.xmlを生成します。
ユーザ認証向け署名付きxml生成>ruby make_register_xml.rb >dir register.xml 2020/08/23 15:30 2,682 register.xml 1 個のファイル 2,682 バイト 0 個のディレクトリ 631,570,432 バイトの空き領域1.2 ユーザ認証を行う
検証環境ではBasic認証が必要です。Basic認証用IDとBasic認証用パスワードを半角コロンで連結し、Base64でエンコードした値を用意し、その値をHTTPリクエストのヘッダ部のAuthorizationに設定します。
curlコマンドより、POSTコマンドでリクエストURIにアクセスすると、次の応答が返ってきます。
curl>curl -X POST -d @register.xml -H "x-eGovAPI-SoftwareID: ************" -H "Authorization: Basic ****************************" https://*****************/****/*/**************/login <?xml version="1.0" encoding="UTF-8"?> <DataRoot> <Result> <Code>0</Code> <Message></Message> </Result> <ApplData> <UserID>*********</UserID> <AccessKey> 78zTKYZQrWdLYgC5cP4oTmalTQE8T7uPF+UDzUEYKZnM4o+teUfNgBGtQsEj6OLsLJX8bW72/B1C me9RmoZLt/YjnLey59EVKzGKVmv/aHQ9ZCCh9zV6gP1h2B8A4/rQOdiXlS9bQ6YGoahaiJzoooW4 n7dF27OCt71+2yTgG6vMnTify8TYVX7ftazfNUfCPm/XPKTg+4PUnSlcXxPrN8dgRiuv6dSeTZFN 6juFvO+PsXIcxr//WtGvMP0V4cFP </AccessKey> <LastAuthenticationDate>20200702213554</LastAuthenticationDate> </ApplData> </DataRoot><AccessKey>タグにはアクセスキーが設定されています。以後、各種APIを呼び出す際、httpリクエストのヘッダにアクセスキーを必ず指定します。また、<LastAuthenticationDate>タグは前回の認証におけるタイムスタンプです。
1.3 署名付きxmlの内容を確認する
ドライバーで生成したregister.xmlを確認します。ハッシュ計算及び署名が行われていることが分かります。
register.xml<?xml version="1.0" encoding="UTF-8"?> <DataRoot> <ApplData Id="ApplData"> <UserID>*********</UserID> </ApplData> <Signature xmlns="http://www.w3.org/2000/09/xmldsig#" Id="20200721144359"> <SignedInfo> <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/> <Reference URI="#ApplData"> <Transforms> <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <DigestValue>pfQDAIAVdZvrFvZoCTsCp5juOrxd7G1jS168/dFBm7Y=</DigestValue> </Reference> </SignedInfo> <SignatureValue> Zrm+z6efKMPj2ugb/jFPPAOFasMSRgJDAFv63Qz2XeKnAc5pLPq1F3HXqrcZRY0zueyVkC2ceB9N 3EO+b/cXvpS2egSp4NC2LiFMx02r0qnpgHaxwCEebb5szCq6MgVYE6tL8xzIdP2ToSigMr+16vu9 AkWORFQX0JVyP4J3Fpl3/FvHdPzlId2fwIpZ1GfCdDhgp35oS85RUcP/JLVnqJ9b9V688LQZkP5g 29LqHbsj8626VX6TD6zYQNlr1rTtdoBIGvNs1Ve51ichNfHF4HeA8x7CG/vTN2AsF73dBLBzY0u4 sMD4zEM7nUBbKeuV831DE+2fbxZq+h6r+ThSdg== </SignatureValue> <KeyInfo> <X509Data> <X509Certificate> MIIEizCCA3OgAwIBAgIEWCsMRzANBgkqhkiG9w0BAQsFADAuMQswCQYDVQQGEwJKUDERMA8GA1UE CgwIRGVtb01pbjExDDAKBgNVBAsMA0NBMTAeFw0xOTEwMjUwMTE2NTZaFw0yNDEwMjQxNDU5NTla MEkxCzAJBgNVBAYTAkpQMREwDwYDVQQKDAhEZW1vTWluMTEMMAoGA1UECwwDQ0ExMRkwFwYDVQQD DBBJY2hpcm8gTWFkb2d1Y2hpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsD81VFFm GM26HYdeGrGzbPbadeBzU4WxIE1r6qeZLxjz7DiV42tjo8QFulWZk6hmCeb5j9ChKp+BA/9Yj6us ccKHZrPQmbcXUZkXRXz9z/7CLxp8b1zAzJakZ0g7o9iU3a3TpyeP6V+GFtkO8YqfthEsJsYzx82d Ui85Bm4l5pnztozPdXRZd2VJpdwzGqtqw6N9PKzMNWux5C0jklkaPeP5P6NHLfeN51+phkY2xkdJ lry629PeKjZAwN0Z8Xu0JdubCvA95UctoZgBoOjpgDtWImV3o9hDpwu+1tMlEBhNZIuKOr2G38qR Z9iJUyoK+XhbzyBUu0A2cwCj0MshbwIDAQABo4IBlDCCAZAwDgYDVR0PAQH/BAQDAgbAMBEGCWCG SAGG+EIBAQQEAwIGQDBOBgNVHRIERzBFpEMwQTELMAkGA1UEBhMCSlAxHjAcBgNVBAoMFeaooeaT rOawkemWk+iqjeiovOWxgDESMBAGA1UECwwJ77yj77yh77yRMB8GA1UdIAEB/wQVMBMwEQYPAoM4 ho4xCAEBAAKMmyVkMGgGA1UdEQRhMF+kXTBbMQswCQYDVQQGEwJKUDEeMBwGA1UECgwV5qih5pOs 5rCR6ZaT6KqN6Ki85bGAMRIwEAYDVQQLDAnvvKPvvKHvvJExGDAWBgNVBAMMD+eqk+WPo+OAgOS4 gOmDjjBQBgNVHR8ESTBHMEWgQ6BBpD8wPTELMAkGA1UEBhMCSlAxETAPBgNVBAoMCERlbW9NaW4x MQwwCgYDVQQLDANDQTExDTALBgNVBAMMBENSTDEwHwYDVR0jBBgwFoAUFNPnlvRSx8XFOnRlLumW 95h4I18wHQYDVR0OBBYEFD6UH9Exye9dEpz9MHJ3sbUDpoZSMA0GCSqGSIb3DQEBCwUAA4IBAQBb CKyFGsqMv6+HkrY0OK+4v40PJQAa/KbOC3JTKooLfNCNXTiTwtWAl1sGN+Ow8pIp8Yvj16VcYpi8 zO4TmNe8NT+u/e2OvBXwJ9OxOs9UNI2m/mXGcSSJ7eXMR3aVCniDU7IaQeicquQttLP9IOk9Ao1W +BM35y5bITA/BMO5tzgaimp4G484QtF/XLi40rGhaZAHGEfvl0abJXPumjajhnGv7SCkjw4+9qdz 5Dtp6kl+GVshQgo6ofpEWhVzdhfqKhNy8dNRL7C/gOTYm+M9SAFk9syL5xKXRyMUGDOheypiJrW/ QyOjrxs6cFa5VqaZWcRIq8yVPwABCpGG/hjU </X509Certificate> </X509Data> </KeyInfo> </Signature> </DataRoot><DigestValue>タグで挟まれている部分がハッシュ値であり、<ApplData>タグ全体をsha256でハッシュ計算した結果です。ハッシュ計算では、ハッシュ値から元の内容を求める事が困難であり、また元の内容を変更するとハッシュ値が変化するという性質があります。
<SignatureValue>タグで挟まれている部分が署名値であり、<SignedInfo>タグ全体を秘密鍵で署名したものです。
<X509Certificate>タグで挟まれている部分が証明書(公開鍵)です。この内容については、検証環境テスト用電子証明書(e-GovEE01_sha2.pfx)の公開鍵の部分と一致しています。
e-Govでは、署名付きxmlを解析し、署名値を公開鍵で復号する事で、送信者本人である事を確認します。さらにハッシュ値が一致する事で改ざんが無いことを確認します。
1.4 ハッシュ値を検証する
ハッシュ値の計算対象は、ユーザIDを含む<ApplData>タグ全体であり、これをSHA-256のアルゴリズムで計算した結果です。この結果が正しいかを確認するため、データ変換ツールを使って検証しました。
データ変換ツールを立ち上げ、<ApplData>タグ全体を変換元(入力)データに設定した上で、次の変換ルールにより変換しました。その結果を署名付きxmlのハッシュ値と比較したところ、結果が一致しました。
- 入力形式(デコード):プレーンTEXT
- 文字コード変換:変換不要
- ハッシュ:SHA-256
- 出力形式(エンコード):BASE64
2 標準形式による一括申請
curlコマンドを使って、認証、一括申請、送信案件一覧情報取得という順にe-Govにアクセスして、当該手続の状態が「到達」になるまでの各レスポンスを確認します。
2.1 署名付きxmlを生成する
ドライバーを実行して、標準形式による一括申請の署名付きxmlを生成します。
標準形式による一括申請の署名付きxml>ruby make_zip_file_standard_format.rbドライバーを実行すると、zip形式ファイルが作成されます。これをデータ変換ツールでBase64形式エンコードに変換して、apply_data.xmlに取り込んで、申請データを作成します。
kousei.xmlの署名情報タグは次のようになります。
kousei.xml<署名情報> <Signature xmlns="http://www.w3.org/2000/09/xmldsig#" Id="20200822191454"> <SignedInfo> <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/> <Reference URI="#%E6%A7%8B%E6%88%90%E6%83%85%E5%A0%B1"> <Transforms> <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <DigestValue>5OBpw3i4ksNiXkS0zjRaeTpdGpX9FqSK+DjyL3J6TPU=</DigestValue> </Reference> <Reference URI="900A01020000100001_01.xml"> <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <DigestValue>Vb10zdwjOtrhECwy/TyswwTqSzEhWQKDc2B1y7j9v3M=</DigestValue> </Reference> <Reference URI="%E6%B7%BB%E4%BB%98%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB.docx"> <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <DigestValue>OAjJfENMRUepWtYYSvAqGnSv6nxx4brjif93tCHopKk=</DigestValue> </Reference> </SignedInfo> <SignatureValue> fhdK4cIq4jAfrGM21nnerwL/U0vob2SMI6nkf1O7WgplbSAqFPooViJqcdk4UbulQlV3mR5WLd23 VphT/QWycbL6q4bav5qmesCE9AaGDexNTkn2Do+eWNAGJbFSSNhAwa+mY0QoMtgbR9tVlXxoYPQ1 9y8ak2WABQPSscAwPGqtb4BsDZMgOJKYqx5zPZlb+EnkC4sxnDMtEPqRjLUFAisQsVynbg3FOy0V dQ6/psJc6GxCe3qAqCWFpUsLlC7WmiadwcITqgWWiDDBevUDVqLLEdYCcOJzQt4PPpGMNIaRUai+ Mec9e6KvSLnn/TGsnBf7z7nu7o4l1UomnPFUEw== </SignatureValue> <KeyInfo> <X509Data> <X509Certificate> MIIEizCCA3OgAwIBAgIEWCsMRzANBgkqhkiG9w0BAQsFADAuMQswCQYDVQQGEwJKUDERMA8GA1UE CgwIRGVtb01pbjExDDAKBgNVBAsMA0NBMTAeFw0xOTEwMjUwMTE2NTZaFw0yNDEwMjQxNDU5NTla MEkxCzAJBgNVBAYTAkpQMREwDwYDVQQKDAhEZW1vTWluMTEMMAoGA1UECwwDQ0ExMRkwFwYDVQQD DBBJY2hpcm8gTWFkb2d1Y2hpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsD81VFFm GM26HYdeGrGzbPbadeBzU4WxIE1r6qeZLxjz7DiV42tjo8QFulWZk6hmCeb5j9ChKp+BA/9Yj6us ccKHZrPQmbcXUZkXRXz9z/7CLxp8b1zAzJakZ0g7o9iU3a3TpyeP6V+GFtkO8YqfthEsJsYzx82d Ui85Bm4l5pnztozPdXRZd2VJpdwzGqtqw6N9PKzMNWux5C0jklkaPeP5P6NHLfeN51+phkY2xkdJ lry629PeKjZAwN0Z8Xu0JdubCvA95UctoZgBoOjpgDtWImV3o9hDpwu+1tMlEBhNZIuKOr2G38qR Z9iJUyoK+XhbzyBUu0A2cwCj0MshbwIDAQABo4IBlDCCAZAwDgYDVR0PAQH/BAQDAgbAMBEGCWCG SAGG+EIBAQQEAwIGQDBOBgNVHRIERzBFpEMwQTELMAkGA1UEBhMCSlAxHjAcBgNVBAoMFeaooeaT rOawkemWk+iqjeiovOWxgDESMBAGA1UECwwJ77yj77yh77yRMB8GA1UdIAEB/wQVMBMwEQYPAoM4 ho4xCAEBAAKMmyVkMGgGA1UdEQRhMF+kXTBbMQswCQYDVQQGEwJKUDEeMBwGA1UECgwV5qih5pOs 5rCR6ZaT6KqN6Ki85bGAMRIwEAYDVQQLDAnvvKPvvKHvvJExGDAWBgNVBAMMD+eqk+WPo+OAgOS4 gOmDjjBQBgNVHR8ESTBHMEWgQ6BBpD8wPTELMAkGA1UEBhMCSlAxETAPBgNVBAoMCERlbW9NaW4x MQwwCgYDVQQLDANDQTExDTALBgNVBAMMBENSTDEwHwYDVR0jBBgwFoAUFNPnlvRSx8XFOnRlLumW 95h4I18wHQYDVR0OBBYEFD6UH9Exye9dEpz9MHJ3sbUDpoZSMA0GCSqGSIb3DQEBCwUAA4IBAQBb CKyFGsqMv6+HkrY0OK+4v40PJQAa/KbOC3JTKooLfNCNXTiTwtWAl1sGN+Ow8pIp8Yvj16VcYpi8 zO4TmNe8NT+u/e2OvBXwJ9OxOs9UNI2m/mXGcSSJ7eXMR3aVCniDU7IaQeicquQttLP9IOk9Ao1W +BM35y5bITA/BMO5tzgaimp4G484QtF/XLi40rGhaZAHGEfvl0abJXPumjajhnGv7SCkjw4+9qdz 5Dtp6kl+GVshQgo6ofpEWhVzdhfqKhNy8dNRL7C/gOTYm+M9SAFk9syL5xKXRyMUGDOheypiJrW/ QyOjrxs6cFa5VqaZWcRIq8yVPwABCpGG/hjU </X509Certificate> </X509Data> </KeyInfo> </Signature> </署名情報>2.2 一括申請を行う
curlコマンドで一括申請を行います。「-X POST」「-d @apply_data.xml」等の必要なパラメタを指定します。
HTTPレスポンスでは、<SendNumber>タグに送信番号が設定されています。
curl>curl -X POST -d @apply_data.xml -H "x-eGovAPI-AccessKey: 78zTKYZQrWdLYgC5cP4oTmalTQE8T7uPF+UDzUEYKZnM4o+teUfNgBGtQsEj6OLsLJX8bW72/B1C me9RmoZLt/YjnLey59EVKzGKVmv/aHQ9ZCCh9zV6gP1h2B8A4/rQOdiXlS9bQ6YGoahaiJzoooW4 n7dF27OCt71+2yTgG6vMnTify8TYVX7ftazfNUfCPm/XPKTg+4PUnSlcXxPrN8dgRiuv6dSeTZFN 6juFvO+PsXIcxr//WtGvMP0V4cFP" -H "x-eGovAPI-SoftwareID: ************" -H "Authorization: Basic ****************************" https://*****************/****/*/**************/apply <?xml version="1.0" encoding="UTF-8"?> <DataRoot> <Result> <Code>0</Code> <Message></Message> </Result> <ApplData> <SendNumber>202007231845376104</SendNumber> <SendDate>20200723184537</SendDate> <SendFileName>apply_data.zip</SendFileName> <SendApplyCount>1</SendApplyCount> <ErrorCount>0</ErrorCount> </ApplData> </DataRoot>2.3 送信案件一覧情報を取得する
curlコマンドで送信案件一覧情報を取得します。一括申請のレスポンスで取得した送信番号をリクエストURIのパラメタとして設定する点に留意します。
curl>curl -X GET -H "x-eGovAPI-AccessKey: 78zTKYZQrWdLYgC5cP4oTmalTQE8T7uPF+UDzUEYKZnM4o+teUfNgBGtQsEj6OLsLJX8bW72/B1C me9RmoZLt/YjnLey59EVKzGKVmv/aHQ9ZCCh9zV6gP1h2B8A4/rQOdiXlS9bQ6YGoahaiJzoooW4 n7dF27OCt71+2yTgG6vMnTify8TYVX7ftazfNUfCPm/XPKTg+4PUnSlcXxPrN8dgRiuv6dSeTZFN 6juFvO+PsXIcxr//WtGvMP0V4cFP" -H "x-eGovAPI-SoftwareID: ************" -H "Authorization: Basic ****************************" https://*******************/*******/*/******/apply;id=202007231845376104 <?xml version="1.0" encoding="UTF-8"?> <DataRoot> <Result> <Code>0</Code> <Message></Message> </Result> <ApplData> <SendNumber>202007231845376104</SendNumber> <SendDateFrom></SendDateFrom> <SendDateTo></SendDateTo> <PackageApplyCount>1</PackageApplyCount> <PackageApply> <No>1</No> <SendNumber>202007231845376104</SendNumber> <SendDate>20200723184537</SendDate> <WorkStatus></WorkStatus> <NormalCount>1</NormalCount> <AllCount>1</AllCount> <SupplementaryMessage></SupplementaryMessage> <ErrorCount>0</ErrorCount> <ErrorFile></ErrorFile> <ApplyCount>0</ApplyCount> </PackageApply> </ApplData> </DataRoot>この段階では、一括申請のデータ形式チェックが行われている状態で、申請案件一覧情報項目の<apply>タグが出力されていません。
しばらくして、もう一度、同じコマンドを実行すると、レスポンスの<Status>タグより、形式チェックが終わり、到達状態に遷移している事を確認できます。この時の到達番号については、<ArriveID>タグより「9002020000002128」であることが分かります。
curl>curl -X GET -H "x-eGovAPI-AccessKey: 78zTKYZQrWdLYgC5cP4oTmalTQE8T7uPF+UDzUEYKZnM4o+teUfNgBGtQsEj6OLsLJX8bW72/B1C me9RmoZLt/YjnLey59EVKzGKVmv/aHQ9ZCCh9zV6gP1h2B8A4/rQOdiXlS9bQ6YGoahaiJzoooW4 n7dF27OCt71+2yTgG6vMnTify8TYVX7ftazfNUfCPm/XPKTg+4PUnSlcXxPrN8dgRiuv6dSeTZFN 6juFvO+PsXIcxr//WtGvMP0V4cFP" -H "x-eGovAPI-SoftwareID: ************" -H "Authorization: Basic ****************************" https://*******************/*******/*/******/apply;id=202007231845376104 <?xml version="1.0" encoding="UTF-8"?> <DataRoot> <Result> <Code>0</Code> <Message></Message> </Result> <ApplData> <SendNumber>202007231845376104</SendNumber> <SendDateFrom></SendDateFrom> <SendDateTo></SendDateTo> <PackageApplyCount>1</PackageApplyCount> <PackageApply> <No>1</No> <SendNumber>202007231845376104</SendNumber> <SendDate>20200723184537</SendDate> <WorkStatus></WorkStatus> <NormalCount>1</NormalCount> <AllCount>1</AllCount> <SupplementaryMessage></SupplementaryMessage> <ErrorCount>0</ErrorCount> <ErrorFile></ErrorFile> <ApplyCount>1</ApplyCount> <Apply> <No>1</No> <ArriveID>9002020000002128</ArriveID> <MinistryName>デグレ省</MinistryName> <ProcName> APIテスト用手続(労働保険関係手続)(通)0001/APIテスト用手続(労働保険関係手続)(通)0001 </ProcName> <ProcFolderName>900A010200001000(1)</ProcFolderName> <ApplicantName>ほげ ほげ</ApplicantName> <CorporationtName></CorporationtName> <DepartmentName></DepartmentName> <Status>到達</Status> <StatusDate>20200723184907</StatusDate> <PayStatus>-</PayStatus> <PayWaitCount>0</PayWaitCount> <CorrectStatus>なし</CorrectStatus> <CommentCountNotRead>0</CommentCountNotRead> <CommentCount>0</CommentCount> <DocCountNotDownload>0</DocCountNotDownload> <DocCount>0</DocCount> </Apply> </PackageApply> </ApplData> </DataRoot>3 個別ファイル署名形式による一括申請
curlコマンドを使って、認証、一括申請、送信案件一覧情報取得、公文書・コメント一覧取得、公文書取得という順にe-Govにアクセスして、各レスポンスを確認します。さらにe-Govが発出する公文書の内容を確認します。
3.1 署名付きxmlを生成する
個別ファイル署名形式による一括申請の署名付きxml>ruby make_zip_file_individual_signature_format.rbドライバーを実行すると、zip形式ファイルが作成されます。これをデータ変換ツールでBase64形式エンコードに変換して、apply_data.xmlに取り込んで、申請データを作成します。
kousei20200716142110000.xmlの署名情報タグは次のようになります。
kousei20200716142110000.xml<署名情報> <Signature xmlns="http://www.w3.org/2000/09/xmldsig#" Id="20200907161432"> <SignedInfo> <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/> <Reference URI="#%E6%A7%8B%E6%88%90%E6%83%85%E5%A0%B1"> <Transforms> <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <DigestValue>fy/hXgyLyi66Vu8MmkmLVshFMOckyFz5FL4cXx88LFc=</DigestValue> </Reference> <Reference URI="950A10181002400001_01.xml"> <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <DigestValue>R657a0a/AIViJFr/JMi7hl350io/wOr4hSuNRmVfcsQ=</DigestValue> </Reference> </SignedInfo> <SignatureValue> cccorxnQJsDjeLvf05LcpwX8E2VfowS0kDPXBi9KoSnC00C9VMEqXxJGDvABi5+rCm+TL49KGGVl mTYSCBtbXIWScKf/ep1wNro33lWN99+Xtoqpbeb7pqja9XkFzcR9XTtRfJBtjVjPSlAg36s+0cUm WAQmXNokYeiYcyLojuO18tiiywJxxV4605UjhSF67WVQ4HcEJlbqnb4VvlwJYsN8YDSJzCktBuHM /bvM8HEi4DOGhJ+H7zmtJ6Gjzlm/SYyD5r8/kqNOMtTSx63X1B29dpC6hcKDuo+UQxsPFZbh06NU vZYCDvIpzmNrrhwdj0dZaOliVfgaCtDUC3ed3g== </SignatureValue> <KeyInfo> <X509Data> <X509Certificate> MIIEizCCA3OgAwIBAgIEWCsMRzANBgkqhkiG9w0BAQsFADAuMQswCQYDVQQGEwJKUDERMA8GA1UE CgwIRGVtb01pbjExDDAKBgNVBAsMA0NBMTAeFw0xOTEwMjUwMTE2NTZaFw0yNDEwMjQxNDU5NTla MEkxCzAJBgNVBAYTAkpQMREwDwYDVQQKDAhEZW1vTWluMTEMMAoGA1UECwwDQ0ExMRkwFwYDVQQD DBBJY2hpcm8gTWFkb2d1Y2hpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsD81VFFm GM26HYdeGrGzbPbadeBzU4WxIE1r6qeZLxjz7DiV42tjo8QFulWZk6hmCeb5j9ChKp+BA/9Yj6us ccKHZrPQmbcXUZkXRXz9z/7CLxp8b1zAzJakZ0g7o9iU3a3TpyeP6V+GFtkO8YqfthEsJsYzx82d Ui85Bm4l5pnztozPdXRZd2VJpdwzGqtqw6N9PKzMNWux5C0jklkaPeP5P6NHLfeN51+phkY2xkdJ lry629PeKjZAwN0Z8Xu0JdubCvA95UctoZgBoOjpgDtWImV3o9hDpwu+1tMlEBhNZIuKOr2G38qR Z9iJUyoK+XhbzyBUu0A2cwCj0MshbwIDAQABo4IBlDCCAZAwDgYDVR0PAQH/BAQDAgbAMBEGCWCG SAGG+EIBAQQEAwIGQDBOBgNVHRIERzBFpEMwQTELMAkGA1UEBhMCSlAxHjAcBgNVBAoMFeaooeaT rOawkemWk+iqjeiovOWxgDESMBAGA1UECwwJ77yj77yh77yRMB8GA1UdIAEB/wQVMBMwEQYPAoM4 ho4xCAEBAAKMmyVkMGgGA1UdEQRhMF+kXTBbMQswCQYDVQQGEwJKUDEeMBwGA1UECgwV5qih5pOs 5rCR6ZaT6KqN6Ki85bGAMRIwEAYDVQQLDAnvvKPvvKHvvJExGDAWBgNVBAMMD+eqk+WPo+OAgOS4 gOmDjjBQBgNVHR8ESTBHMEWgQ6BBpD8wPTELMAkGA1UEBhMCSlAxETAPBgNVBAoMCERlbW9NaW4x MQwwCgYDVQQLDANDQTExDTALBgNVBAMMBENSTDEwHwYDVR0jBBgwFoAUFNPnlvRSx8XFOnRlLumW 95h4I18wHQYDVR0OBBYEFD6UH9Exye9dEpz9MHJ3sbUDpoZSMA0GCSqGSIb3DQEBCwUAA4IBAQBb CKyFGsqMv6+HkrY0OK+4v40PJQAa/KbOC3JTKooLfNCNXTiTwtWAl1sGN+Ow8pIp8Yvj16VcYpi8 zO4TmNe8NT+u/e2OvBXwJ9OxOs9UNI2m/mXGcSSJ7eXMR3aVCniDU7IaQeicquQttLP9IOk9Ao1W +BM35y5bITA/BMO5tzgaimp4G484QtF/XLi40rGhaZAHGEfvl0abJXPumjajhnGv7SCkjw4+9qdz 5Dtp6kl+GVshQgo6ofpEWhVzdhfqKhNy8dNRL7C/gOTYm+M9SAFk9syL5xKXRyMUGDOheypiJrW/ QyOjrxs6cFa5VqaZWcRIq8yVPwABCpGG/hjU </X509Certificate> </X509Data> </KeyInfo> </Signature> </署名情報>kousei20200716142110001.xmlの署名情報タグについては省略します。
3.2 一括申請を行う
curlコマンドで一括申請を行います。
curl>curl -X POST -d @apply_data.xml -H "x-eGovAPI-AccessKey: RJc23F21t9vhjtaleH9N/Y9fhDJ9O8u0q+A5MHa50opasIe+cNVgM+8MJ9VVwR2gjiqIHH9Fr0oy 46lRRXZTaW6mbs4dWFvK6VUoj/tUwgd7XorYA7KtpGhQGVjc3E37Z6Di167pFycmB6+SjfalAgF6 eyQPWzvs9Kl7IgP68NqMbp0neFNCprL0qgUM1NxxuxlsJbkF1cbp4FJ3rt2VpPUEosfe1+Nmj8w9 sCvEQKSKBDTytDwpFCU6IzMuH3m3" -H "x-eGovAPI-SoftwareID: ************" -H "Authorization: Basic ****************************" https://*******************/*******/*/******/apply <?xml version="1.0" encoding="UTF-8"?> <DataRoot> <Result> <Code>0</Code> <Message></Message> </Result> <ApplData> <SendNumber>202007252053026176</SendNumber> <SendDate>20200725205302</SendDate> <SendFileName>apply_data.zip</SendFileName> <SendApplyCount>1</SendApplyCount> <ErrorCount>0</ErrorCount> </ApplData> </DataRoot>3.3 送信案件一覧情報を取得する
curlコマンドで送信案件一覧情報を取得します。
curl>curl -X GET -H "x-eGovAPI-AccessKey: NN1s3WgK2pcwv/5aELtNyRlcTjENG+pM4yAX3gU6NeSyCxACKwBXTUsdx4oxAmv75gXGsdvS6lFX n2Ar5RNCUlwpjRhGbW8jZzoW5CCXQYOLLlNl21sffoKodYSMIr1GPMzJSeQTC+tT0gGFqAsyQxm6 Iethc/5AFJgdPX3bDAC9g2OvMDDNrECKY1/XaGCXDckIeI8H1SLz7mDi4Q1ZYofF5gDCFcSPsNSJ A2fuEZoPRoHVBw/mzo7/qBFA1wWY" -H "x-eGovAPI-SoftwareID: ************" -H "Authorization: Basic ****************************" https://*******************/*******/*/******/apply;id=202007252053026176 <?xml version="1.0" encoding="UTF-8"?> <DataRoot> <Result> <Code>0</Code> <Message></Message> </Result> <ApplData> <SendNumber>202007252053026176</SendNumber> <SendDateFrom></SendDateFrom> <SendDateTo></SendDateTo> <PackageApplyCount>1</PackageApplyCount> <PackageApply> <No>1</No> <SendNumber>202007252053026176</SendNumber> <SendDate>20200725205302</SendDate> <WorkStatus>処理待ち</WorkStatus> <NormalCount>0</NormalCount> <AllCount>1</AllCount> <SupplementaryMessage></SupplementaryMessage> <ErrorCount>0</ErrorCount> <ErrorFile></ErrorFile> <ApplyCount>0</ApplyCount> </PackageApply> </ApplData> </DataRoot><WorkStatus>タグより、処理待ち状態です。しばらくして、もう一度、同じコマンドを実行すると、形式チェックが始まっている様子です。
curl>curl -X GET -H "x-eGovAPI-AccessKey: NN1s3WgK2pcwv/5aELtNyRlcTjENG+pM4yAX3gU6NeSyCxACKwBXTUsdx4oxAmv75gXGsdvS6lFX n2Ar5RNCUlwpjRhGbW8jZzoW5CCXQYOLLlNl21sffoKodYSMIr1GPMzJSeQTC+tT0gGFqAsyQxm6 Iethc/5AFJgdPX3bDAC9g2OvMDDNrECKY1/XaGCXDckIeI8H1SLz7mDi4Q1ZYofF5gDCFcSPsNSJ A2fuEZoPRoHVBw/mzo7/qBFA1wWY" -H "x-eGovAPI-SoftwareID: ************" -H "Authorization: Basic ****************************" https://*******************/*******/*/******/apply;id=202007252053026176 <?xml version="1.0" encoding="UTF-8"?> <DataRoot> <Result> <Code>0</Code> <Message></Message> </Result> <ApplData> <SendNumber>202007252053026176</SendNumber> <SendDateFrom></SendDateFrom> <SendDateTo></SendDateTo> <PackageApplyCount>1</PackageApplyCount> <PackageApply> <No>1</No> <SendNumber>202007252053026176</SendNumber> <SendDate>20200725205302</SendDate> <WorkStatus>処理待ち</WorkStatus> <NormalCount>0</NormalCount> <AllCount>1</AllCount> <SupplementaryMessage></SupplementaryMessage> <ErrorCount>0</ErrorCount> <ErrorFile></ErrorFile> <ApplyCount>0</ApplyCount> </PackageApply> </ApplData> </DataRoot>さらにしばらく待ち、もう一度、同じコマンドを実行すると、到達状態から審査終了に遷移しました。<ArriveID>タグには到達番号が設定されています。
curl>curl -X GET -H "x-eGovAPI-AccessKey: NN1s3WgK2pcwv/5aELtNyRlcTjENG+pM4yAX3gU6NeSyCxACKwBXTUsdx4oxAmv75gXGsdvS6lFX n2Ar5RNCUlwpjRhGbW8jZzoW5CCXQYOLLlNl21sffoKodYSMIr1GPMzJSeQTC+tT0gGFqAsyQxm6 Iethc/5AFJgdPX3bDAC9g2OvMDDNrECKY1/XaGCXDckIeI8H1SLz7mDi4Q1ZYofF5gDCFcSPsNSJ A2fuEZoPRoHVBw/mzo7/qBFA1wWY" -H "x-eGovAPI-SoftwareID: ************" -H "Authorization: Basic ****************************" https://*******************/*******/*/******/apply;id=202007252053026176 <?xml version="1.0" encoding="UTF-8"?> <DataRoot> <Result> <Code>0</Code> <Message></Message> </Result> <ApplData> <SendNumber>202007252053026176</SendNumber> <SendDateFrom></SendDateFrom> <SendDateTo></SendDateTo> <PackageApplyCount>1</PackageApplyCount> <PackageApply> <No>1</No> <SendNumber>202007252053026176</SendNumber> <SendDate>20200725205302</SendDate> <WorkStatus></WorkStatus> <NormalCount>1</NormalCount> <AllCount>1</AllCount> <SupplementaryMessage></SupplementaryMessage> <ErrorCount>0</ErrorCount> <ErrorFile></ErrorFile> <ApplyCount>1</ApplyCount> <Apply> <No>1</No> <ArriveID>9502020000056054</ArriveID> <MinistryName>APIステータス省</MinistryName> <ProcName> APIテスト用手続(社会保険関係手続)(個)1006/APIテスト用手続(社会保険関係手続)(個)1006 </ProcName> <ProcFolderName>950A102200039000(1)</ProcFolderName> <ApplicantName>松井 英夫</ApplicantName> <CorporationtName></CorporationtName> <DepartmentName></DepartmentName> <Status>審査終了</Status> <StatusDate>20200725210114</StatusDate> <PayStatus>-</PayStatus> <PayWaitCount>0</PayWaitCount> <CorrectStatus>なし</CorrectStatus> <CommentCountNotRead>0</CommentCountNotRead> <CommentCount>0</CommentCount> <DocCountNotDownload>1</DocCountNotDownload> <DocCount>1</DocCount> </Apply> </PackageApply> </ApplData> </DataRoot>3.4 公文書・コメント一覧を取得する
curlコマンドで公文書・コメント一覧を取得します。送信案件一覧情報取得のレスポンスで取得した到達番号をリクエストURIの一部に設定する点に留意します。
curl>curl -X GET -H "x-eGovAPI-AccessKey: NN1s3WgK2pcwv/5aELtNyRlcTjENG+pM4yAX3gU6NeSyCxACKwBXTUsdx4oxAmv75gXGsdvS6lFX n2Ar5RNCUlwpjRhGbW8jZzoW5CCXQYOLLlNl21sffoKodYSMIr1GPMzJSeQTC+tT0gGFqAsyQxm6 Iethc/5AFJgdPX3bDAC9g2OvMDDNrECKY1/XaGCXDckIeI8H1SLz7mDi4Q1ZYofF5gDCFcSPsNSJ A2fuEZoPRoHVBw/mzo7/qBFA1wWY" -H "x-eGovAPI-SoftwareID: ************" -H "Authorization: Basic ****************************" https://*******************/*******/*/******/notice/9502020000056054 <?xml version="1.0" encoding="UTF-8"?> <DataRoot> <Result> <Code>0</Code> <Message></Message> </Result> <ApplData> <ArriveID>9502020000056054</ArriveID> <ProcName>APIテスト用手続(社会保険関係手続)(個)1006/APIテスト用手続(社会保険関係手続)(個)1006</ProcName> <Official> <NoticeSubID>1</NoticeSubID> <AllowedDate>20200725210114</AllowedDate> <DocTitle>署名つき公文書</DocTitle> <File> <FileName>official_doc1.xml</FileName> </File> <DownloadExpiredDate>20201122000000</DownloadExpiredDate> <DownloadDate></DownloadDate> <Sign>あり</Sign> <ExpiredDateFlag>0</ExpiredDateFlag> </Official> </ApplData> </DataRoot><DocTitle>タグより、サブID「1」の署名付き公文書が発出されています。また、<DownloadExpiredDate>タグより、取得期限が2020年11月20日までです。
3.5 公文書を取得する
curlコマンドで公文書を取得します。ここでも送信案件一覧情報取得のレスポンスで取得した到達番号「9502020000056054」及びサブID「1」をリクエストURIの一部に設定する点に留意します。
curl>curl -X GET -H "x-eGovAPI-AccessKey: NN1s3WgK2pcwv/5aELtNyRlcTjENG+pM4yAX3gU6NeSyCxACKwBXTUsdx4oxAmv75gXGsdvS6lFX n2Ar5RNCUlwpjRhGbW8jZzoW5CCXQYOLLlNl21sffoKodYSMIr1GPMzJSeQTC+tT0gGFqAsyQxm6 Iethc/5AFJgdPX3bDAC9g2OvMDDNrECKY1/XaGCXDckIeI8H1SLz7mDi4Q1ZYofF5gDCFcSPsNSJ A2fuEZoPRoHVBw/mzo7/qBFA1wWY" -H "x-eGovAPI-SoftwareID: ************" -H "Authorization: Basic ****************************" https://*******************/*******/*/******/officialdocument/9502020000056054/1 <?xml version="1.0" encoding="UTF-8"?> <DataRoot> <Result> <Code>0</Code> <Message></Message> </Result> <ApplData> <ArriveID>9502020000056054</ArriveID> <NoticeSubID>1</NoticeSubID> <Download> <FileData> UEsDBBQACAAIAGCp+VAAAAAAAAAAAAAAAAARAAAAb2ZmaWNpYWxfZG9jMS54bWytV1uv4tYVfkfi PxyRRzRjLoZjVxyi7SsGfMVcXyoDxgYDNrbBhqeBk2tn1KRJpukkVSfTROk0aSeJ0ksSpZ0f45xz Jk/5C93AnAk5M4naKkiW9lp77W9967L3EoXnw8n4aKG73tCeniTS11OJI33as/vDqXGSqKvMNSxx 5PnatK+N7al+kpjaieeLhe2pa56/HOueqev+kb904J6vhz4SeuPEkenqg5OEPRgMe0Nt/Mu+3Utf 3248X4zHCpRIHjVopcaJwt4jVB7BX4FRREF9LOwUNZpUaLJdfPThR2ef3S4gl/KhiQp4CRqs4e5u ebDHCywnMOKR2pbok0QK/q6l06l0orhdFpDH25fekUP3BUKk2kccdZKAbLfrxA+dtqt0rUTT6lFd 4Z4VKHJoDiEEsQgk7loqlS4ge/FwH6j0Y5KQXDqHpDNIZmsI9Yd26uGpnQYwTPHiozfPXnu/gGzX V7YFwNPFb7648e3prwvITrhiUBIFUeEYsnj+4GEBeSId+ER+4LQA6mpJVJ5J45+vnd28c/H79Y8z (省略) DsArHMBXOICvcABf4QC+wgF8hQP4CgfwFQ7gKxzAVziAr3CAXuEAvcIBeoUD9AoH6BUO0CscoFc4 QK9wgF7hAL3CAX6FA/wKB/gVDvArHOBXOMCvcIBf4QC/wgF+hQP8Cgf7Kxzsr3Cwv8LB/goH+ysc 7K9wsL/Cwf4KB/M/8mMDv+zzv0r+/bwHAIH//Y4CcHBwsP+XB3zZHv/9zgO+LKv/iHN2NLCwNnH8 87jz1yGH7f/1yeavs4uBo/Nf73Igy8vmREKgoBCRE/2f9zvz/5fUmOQdTVyJ/8zoz3OfkrMN8Z8D 9t85s71siL9z/j9QSwcIOMaRTY3BAAAA0AAAUEsBAhQAFAAIAAgAYKn5UEytAV1HCgAAGBEAABEA AAAAAAAAAAAAAAAAAAAAAG9mZmljaWFsX2RvYzEueG1sUEsBAhQAFAAIAAgAYKn5UIAs/hjbAgAA hA4AABEAAAAAAAAAAAAAAAAAhgoAAG9mZmljaWFsX2RvYzEueHNsUEsBAhQAFAAIAAgAYKn5UDjG kU2NwQAAANAAABEAAAAAAAAAAAAAAAAAoA0AAG9mZmljaWFsX2RvYzEucGRmUEsFBgAAAAADAAMA vQAAAGzPAAAAAA== </FileData> </Download> </ApplData> </DataRoot><FileData>タグで囲まれた部分が、Base64形式でエンコードされた公文書データです。これをデータ変換ツールを使ってデコードした上でバイナリファイルに出力します。出力されたファイルの拡張子をzipに変更して解凍すると、次の公文書が得られます。
- official_doc1.pdf
- official_doc1.xml
- official_doc1.xsl
official_doc1.pdfは次の内容です。
4 まとめ
今回の試行を通して見えてきたe-Govの問題点を定義した上で、マイナポータルAPIで改善できるか考察します。
マイナポータルとは、政府が運営するオンラインサービスです。子育てや法人設立等に関する行政手続をワンストップで行えたり、行政機関からのお知らせの確認等が行えます。マイナポータルでは、申請API等が用意される予定であり、今後も、順次、機能拡充が行われます。
マイナポータルAPIの仕様書については、ソフトウェア開発業者向け専用ページに入手方法が記載されています。
4.1 e-Govの問題点を定義する
(1)HTTPレスポンスがXML形式であるため扱いにくい
HTTPレスポンスについては、一般的にはJson形式が多いです。一方、e-GovはXML形式です。XML形式だとテキストしか扱えず、integerやboolean、nullといった基本的な型すら存在しません。バイナリデータを含めるためには、Base64形式でエンコードする必要があるため、ソフトウェアサービス側で変換コードを記述する必要があり、扱いにくいです。
(2)公文書及びコメント通知の機械読みを想定していない
各府省が発出する公文書及びコメント通知については、機械読みを想定しておらず、形式も公開されていません。ファイルもxml+xsl、PDFとバラバラです。公文書及びメッセージを固めたZipファイルをBase64形式でエンコードした結果を返す方式であり、各府省が付与する情報をHTTPレスポンスから直接取り出すことができません。
各府省が発出する情報としては、次の情報があります。これらは一例であり、実際は沢山の情報があります。
- 雇用保険に新規適用される事で付与される雇用保険適用事業所番号
- 雇用保険の新規加入者に付与される雇用保険被保険者番号
- 社会保険料に関する改定後の標準報酬月額
- 申請に対する返戻理由等
現状は、これらの情報をソフトウェアサービスに反映させるため、ファイル展開をした後、人手で公文書及びコメント通知を確認して入力する必要があり、無駄な手間や入力ミス等が生じます。
公文書及びコメント通知については、公的機関が発出した書類であり、改ざんが出来ないようにする事及び改善されても後から分かる事を目的としているため、このような仕様になっていると考えます。
4.2 マイナポータルAPIで問題点が改善するか
マイナポータルAPIでは、HTTPレスポンスがJson形式になっており、e-Govの1つ目の問題点については、改善されます。一方、2つ目の問題点については、e-Govと同様にHTTPレスポンス内に公文書等のファイルをBase64エンコード形式で返す方式になっています。HTTPレスポンスから各府省が発出する情報を直接取得する事ができません。
マイナポータルAPIには、e-Govに存在しない機能として「自己情報所得API」が用意されています。「自己情報取得API」は、ユーザが行政所有の自己情報を確認するだけにとどまらず、Webサービス事業者に自己情報を提供することまで可能にします。
例えば、金融機関でローンを組む際、金融機関から所得証明書(課税証明書)の提出が求められるとします。現状は、ユーザが行政窓口に赴き、所得証明書(課税証明書)を発行してもらい、金融機関に提出します。一方、自己情報取得APIによるシステム間連携が実現すれば、金融機関がマイナポータルから所得情報を提供してもらう事が可能です。ユーザが行政窓口に赴く必要はなく、利便性が向上します。
この自己情報所得APIを使えば、行政が持つ自己の雇用保険被保険者番号や標準報酬月額等の情報を取得できます。今後、社労士等の代理人の操作により行政側の情報を取得する事が可能になれば、2つ目の問題点も改善できる可能性があります。
- 投稿日:2020-09-18T14:02:11+09:00
第3回 SmartHRのライブラリkijiを使ってe-Govを動かす(実行環境構築編)
kijiは、SmartHRに組み込まれているライブラリであり、GitHubで公開されています。このライブラリはRubyで作られたOSSであり、誰でも入手・改造・再頒布が可能です。
SmartHR kijiここでは、kijiを呼び出して署名付きxmlを生成するドライバーを開発します。
1.kijiのフォルダ構成
GitHubからkijiをダウンロードします。フォルダ構成は次のとおりです。
kiji-masterフォルダ構成kiji-master/ ├ bin/ ├ lib/ # 開発ソース一式 │ └ kiji/ └ spec/ # テスト環境一式kiji-master直下にあるlibに開発ソース一式、specにテスト環境一式があります。kiji-master直下にあるkiji.gemspecを確認すると、kijiが依存する他のgemパッケージのバージョンを確認できます。
後述しますが、開発ソースを一部修正する必要が生じたため、ローカル環境でkijiをビルドしました。kiji.gemspecのspec.filesよりGitHubからファイル一覧を取得するようになっていたので、ローカルフォルダーから取得するように変更しました。
lib内の開発ソースを以下に示します。
libフォルダ構成lib/ ├ kiji.rb └ kiji/ ├ access.rb ├ api.rb ├ authentication.rb ├ client.rb ├ digester.rb ├ signer.rb ├ version.rb └ zipper.rbkiji.rbを確認すると、アプリケーションが、version.rb、client.rb、zipper.rbを直接取り込んでいる事が分かります。
kiji.rbrequire 'kiji/version.rb' require 'kiji/client.rb' require 'kiji/zipper.rb'2.kijiの構造について
kijiは4つのクラスと2つのモジュールから構成されます。
kijiの構造module kiji ├ class Zipper # zipファイルに固める ├ class Client # e-Gov外部連携APIに送信 ├ class Signer # 署名付きxml作成 ├ class Digester # デジタル署名作成 ├ module Authentication # 認証関係のユーティリティ └ module Access # e-Gov関係のユーティリティ4つのクラスの階層としては、class Zipperとclass Clientが最上位にあり、その下にclass Signer、class Digesterと続きます。クラス図で書くと次のようになります。
e-Gov電子申請は、次の手順で行われます。
①署名付きxmlを作成する
②署名付きxml、添付ファイル等をzipファイルに固める
③zipファイルをBase64形式にエンコードして、送信データに格納する
④e-Gov外部連携APIのhttpリクエストボディに③を設定した上で送信するclass Zipperは①②、class Clientは③④の機能を持ちます。
今回は、class Zipperを利用して①②を行い、e-Govへの送信はcurlコマンドを利用する事にしました。これは、署名付きxmlの内容及びe-Govからのレスポンスを確認するためです。
3.windows環境の改行コードに起因するkijiの修正
zipper.rbにて、申請書のハッシュ値を計算する際、署名対象となるxmlを読み込む箇所に問題があり、コードを修正しました。
オリジナルのコード「app_doc = File.read(app_file_path)」では、xmlをテキストモードで読み込むため、windows形式の改行コード"\r\n"を内部で自動的に"\n"に置き換えてしまい、その結果に対してハッシュ値を計算していました。そのため、改行コードの置き換えをしないバイナリモードで読み込むように修正しました。
zipper.rb# 申請書のハッシュ値を求める app_file_paths.each do |app_file_path| - app_doc = File.read(app_file_path) + f = File.open(app_file_path, "rb") + app_doc = f.read app_file_name = File.basename(app_file_path) signer.digest_file!(app_doc, id: app_file_name) endこれによって、ローカルgemを作成する事になりました。kiji.gemspecのspec.filesをローカル先に変更した後、kijiをビルドして、ローカル環境にインストールしました。
kijiビルド\kiji-master>rake build kiji 0.2.2 built to pkg/kiji-0.2.2.gem. \kiji-master>dir pkg 2020/08/21 11:42 <DIR> . 2020/08/21 11:42 <DIR> .. 2020/08/16 20:54 13,312 kiji-0.2.2.gem \kiji-master>gem install pkg\kiji-0.2.2.gem Successfully installed kiji-0.2.2 Parsing documentation for kiji-0.2.2 Installing ri documentation for kiji-0.2.2 Done installing documentation for kiji after 0 seconds 1 gem installed4.テストデータを用意する
一括申請を行う際に必要になるテストデータを用意します。テストデータとしては、標準形式及び個別ファイル署名形式があります。この際、参照すべき公開資料としては、検証環境テスト用手続に関する資料です。公開資料「APIテスト用手続一覧」には、以下の情報が掲載されています。
APIテスト用手続一覧手続情報 ├ 手続識別子 ├ 手続名称(構成管理XMLの「手続名称タグ」に記載する名称) ├ 様式ID ├ 申請書様式名称 ├ 申請書様式仕様パターン ├ 受付行政機関ID ├ 分類 ├ ひな形とした手続に関する情報 │ ├ 手続識別子 │ └ 手続名 ├ 手続に関する条件 │ ├ 申請種別 │ ├ 申請書(手続識別子、申請書様式バージョン) │ ├ 署名(有無、署名最大人数) │ ├ 添付書類(有無、必須/任意/添付不可、書類名、固定/任意、書類名、送付方法) │ ├ 提出先(提出先識別子、提出先名称、大分類、中分類、小分類) │ ├ 単様式/複数様式 │ ├ 取下げ方法 │ ├ 府省照会有無 │ ├ 手数料種別 │ ├ 受付期間 │ ├ 様式 │ ├ 手続削除フラグ │ └ ステータス自動遷移対象 └ 到達以降の可能処理 ├ 取下げ(なし、依頼、申請) ├ 補正(なし、部分補正、再提出、補正申請) ├ 公文書発出(なし、あり) ├ 手数料登録(後納)(なし、あり) ├ 手続終了(通知、取下げ済み、再提出済み、公文書取得最終) └ コメント通知(なし、メッセージあり、ファイルあり)また、公開資料「APIテスト用手続ステータス遷移一覧」には、新規申請、取下げ依頼、取下げ申請、補正申請毎に、以下の情報が掲載されています。
APIテスト用手続ステータス遷移一覧ステータス遷移一覧 ├ 手続識別子 ├ 手続名 ├ 申請に対する操作/処理 │ ├ 取下げ │ ├ 補正申請 │ ├ コメント通知 │ ├ 公文書発出 │ └ 納付ステータス変更 ├ 申請ステータスとアクション │ ├ 申請の事前状態 │ │ ├ 対象の申請 │ │ └ 申請ステータス(申請サブステータス) │ ├ アクション │ │ ├ 利用者側の操作 │ │ └ e-Gov側処理の有無 │ └ 申請の事後状態 │ ├ 対象の申請 │ └ 申請ステータス(申請サブステータス) └ 備考これらの資料を参考にして、目的とするテストを実施できるようにテストデータを作成します。
4.1 標準形式
テストデータとして、手続識別子「900A010200001000」を取り上げます。この手続は「雇用保険被保険者資格取得届/電子申請」です。添付書類が必須であり、公文書の発出はありません。
必要なファイルは以下の通りです。ファイル名称については、公開資料「外部連携 API申請データ仕様 共通データ仕様書」で仕様が決められているので、その規則に従います。
No ファイル種別 ファイル名称 備考 1 構成管理XML kousei.xml 2 申請書XML 900A01020000100001_01.xml 様式ID(半角英数字18桁)+「_01」固定 3 添付書類 添付ファイル.docx これらのファイルは次の階層になっています。
ファイル階層kousei.xml ├ 900A01020000100001_01.xml └ 添付ファイル.docx構成管理XML(kousei.xml)に必要な情報は、以下のとおりです。
No 項目 内容 備考 1 手続識別子 900A010200001000 2 手続名称 APIテスト用手続(労働保険関係手続)(通)0001/APIテスト用手続(労働保険関係手続)(通)0001 構成管理XMLの「手続名称タグ」に記載する名称 3 様式ID 900A01020000100001 4 申請書様式名称 APIテスト用手続(労働保険関係手続)(通)0001_01 5 申請書様式仕様パターン 0001 申請書XML構造定義【APIテスト用手続き】におけるパターン 6 受付行政機関ID 100900 構成管理XML(kousei.xml)に記載する手続きに関する条件については、以下のとおりです。
No 項目 内容 備考 1 申請種別 通常申請 2 申請書-手続識別子(申請書) 900A010200001000 3 申請書-申請書様式バージョン 0003 4 署名-有無 署名あり(単署) 5 署名-署名最大人数 1 6 添付書類-有無 有 7 添付書類-必須/任意 必須 8 添付書類-書類名固定/任意 書類名固定 書類名を「テスト添付書類1」とする 9 添付書類-書類名 テスト添付書類1 10 添付書類-送付方法 添付 11 提出先-提出先識別子 900API00000000001001001 12 提出先-提出先名称 総務省,行政管理局,API これらの情報を構成管理XML(kousei.xml)のタグに埋め込みます。
kousei.xml<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet href="999000000000000001.xsl" type="text/xsl"?> <DataRoot> <様式ID>999000000000000001</様式ID> <様式バージョン>0001</様式バージョン> <STYLESHEET>999000000000000001.xsl</STYLESHEET> <構成情報 ID="構成情報"> <管理情報> <手続番号> <受付行政機関ID>100900</受付行政機関ID> <手続ID>900A010200001000</手続ID> </手続番号> <手続名称>APIテスト用手続(労働保険関係手続)(通)0001/APIテスト用手続(労働保険関係手続)(通)0001</手続名称> <初回受付番号/> <申請種別>新規申請</申請種別> <申請者連絡先情報> <申請者情報> <氏名>ほげ ほげ</氏名> <氏名フリガナ>フリ ガナ</氏名フリガナ> <役職/> <法人団体名/> <法人団体名フリガナ/> <部門名/> <部門名フリガナ/> <郵便番号>1200001</郵便番号> <住所>東京都足立区大谷田</住所> <住所フリガナ>トウキョウトアダチクオオヤタ</住所フリガナ> <電話番号>12-232-1232</電話番号> <FAX番号/> <電子メールアドレス>zzz@zzz.zz</電子メールアドレス> </申請者情報> <連絡先情報> <氏名>漢 字</氏名> <氏名フリガナ>フリ ガナ</氏名フリガナ> <役職/> <法人団体名/> <法人団体名フリガナ/> <部門名/> <部門名フリガナ/> <郵便番号>1200001</郵便番号> <住所>東京都足立区大谷田</住所> <住所フリガナ>トウキョウトアダチクオオヤタ</住所フリガナ> <電話番号>12-232-1232</電話番号> <FAX番号/> <電子メールアドレス>zzz@zzz.zz</電子メールアドレス> </連絡先情報> <委任登録票添付情報> <発行番号/> <委任登録票名称/> <委任登録票ファイル名称/> </委任登録票添付情報> </申請者連絡先情報> </管理情報> <添付書類属性情報> <添付種別>添付</添付種別> <添付書類名称>テスト添付書類1</添付書類名称> <添付書類ファイル名称>添付ファイル.docx</添付書類ファイル名称> <提出情報>1</提出情報> </添付書類属性情報> <手数料情報> <手数料1> <手数料識別子/> <略科目コード/> <略科目名/> <振込金額/> </手数料1> <手数料2> <手数料識別子/> <略科目コード/> <略科目名/> <振込金額/> </手数料2> <手数料3> <手数料識別子/> <略科目コード/> <略科目名/> <振込金額/> </手数料3> <手数料4> <手数料識別子/> <略科目コード/> <略科目名/> <振込金額/> </手数料4> <手数料5> <手数料識別子/> <略科目コード/> <略科目名/> <振込金額/> </手数料5> <手数料6> <手数料識別子/> <略科目コード/> <略科目名/> <振込金額/> </手数料6> </手数料情報> <通信欄/> <府省照会情報> <府省照会1> <府省照会情報ラベル/> <府省照会情報/> </府省照会1> <府省照会2> <府省照会情報ラベル/> <府省照会情報/> </府省照会2> <府省照会3> <府省照会情報ラベル/> <府省照会情報/> </府省照会3> <府省照会4> <府省照会情報ラベル/> <府省照会情報/> </府省照会4> <府省照会5> <府省照会情報ラベル/> <府省照会情報/> </府省照会5> <府省照会6> <府省照会情報ラベル/> <府省照会情報/> </府省照会6> <府省照会7> <府省照会情報ラベル/> <府省照会情報/> </府省照会7> <府省照会8> <府省照会情報ラベル/> <府省照会情報/> </府省照会8> <府省照会9> <府省照会情報ラベル/> <府省照会情報/> </府省照会9> <府省照会10> <府省照会情報ラベル/> <府省照会情報/> </府省照会10> </府省照会情報> <提出先情報> <提出先識別子>900API00000000001001001</提出先識別子> <提出先名称>総務省,行政管理局,API</提出先名称> </提出先情報> <申請書属性情報><申請書様式ID>900A01020000100001</申請書様式ID><申請書様式バージョン>0003</申請書様式バージョン> <申請書様式名称>APIテスト用手続(労働保険関係手続)(通)0001_01</申請書様式名称><申請書ファイル名称>900A01020000100001_01.xml</申請書ファイル名称></申請書属性情報></構成情報> </DataRoot>申請書XML(900A01020000100001_01.xml)について、次のように作成します。
900A01020000100001_01.xml<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="900A01020000100001.xsl" ?> <DataRoot> <様式ID>900A01020000100001</様式ID> <様式バージョン>0003</様式バージョン> <STYLESHEET>900A01020000100001.xsl</STYLESHEET> <様式コピー情報>0</様式コピー情報> <Doctype>1</Doctype> <G00005-A-250045-001_1> <帳票種別>13101</帳票種別> <被保険者番号> <被保険者番号4桁></被保険者番号4桁> <被保険者番号6桁></被保険者番号6桁> <被保険者番号CD></被保険者番号CD> </被保険者番号> <取得>1</取得> <被保険者氏名>えーぴーあい 太郎</被保険者氏名> <被保険者氏名フリガナ>エーピーアイ タロウ</被保険者氏名フリガナ> <変更後の氏名></変更後の氏名> <変更後の氏名フリガナ></変更後の氏名フリガナ> <性別>男</性別> <生年月日> <年号>昭和</年号> <年>59</年> <月>5</月> <日>23</日> </生年月日> <事業所番号> <事業所番号4桁>1111</事業所番号4桁> <事業所番号6桁>222222</事業所番号6桁> <事業所番号CD>3</事業所番号CD> </事業所番号> <資格取得年月日> <年号>平成</年号> <年>27</年> <月>4</月> <日>9</日> </資格取得年月日> <被保険者となったことの原因>1</被保険者となったことの原因> <賃金> <支払の態様>1</支払の態様> <賃金月額>500</賃金月額> </賃金> <雇用形態>7</雇用形態> <職種>1</職種> <取得時被保険者種類区分/> <番号複数取得チェック不要/> <契約期間の定め>有</契約期間の定め> <契約期間の定めの有無> <契約期間の定め有> <契約期間開始> <年号>平成</年号> <年>27</年> <月>4</月> <日>9</日> </契約期間開始> <契約期間終了> <年号>平成</年号> <年>28</年> <月>3</月> <日>31</日> </契約期間終了> <契約更新条項有無>有</契約更新条項有無> </契約期間の定め有> </契約期間の定めの有無> <一週間の所定労働時間> <時間>40</時間> <分>0</分> </一週間の所定労働時間> <事業所名称>株式会社試験</事業所名称> <備考欄_申請者> <国籍/> <在留資格/> <在留期間> <年/> <月/> <日/> </在留期間> <資格外活動許可の有無/> <派遣請負労働者/> <備考/> </備考欄_申請者> <事業主> <住所>東京都</住所> <氏名>テストオーナー</氏名> <電話番号> <市外局番>03</市外局番> <市内局番>3030</市内局番> <加入者番号>3333</加入者番号> </電話番号> </事業主> <届出年月日> <年号>平成</年号> <年>27</年> <月>4</月> <日>1</日> </届出年月日> <あて先>品川</あて先> <社会保険労務士記載欄> <作成年月日> <年号>平成</年号> <年></年> <月></月> <日></日> </作成年月日> <提出代行者事務代理者の表示></提出代行者事務代理者の表示> <氏名></氏名> <電話番号> <市外局番></市外局番> <市内局番></市内局番> <加入者番号></加入者番号> </電話番号> <付記欄></付記欄> </社会保険労務士記載欄> <備考欄_職員> <備考></備考> <確認通知年月日> <年号>平成</年号> <年></年> <月></月> <日></日> </確認通知年月日> </備考欄_職員> <Xmit>0</Xmit> </G00005-A-250045-001_1> </DataRoot>各タグにおけるデータの省略可否、文字数、文字種別等の制約を遵守すれば、形式チェック等は問題なく通ります。テストデータであるため、架空の内容で大丈夫です。
4.2 個別ファイル署名形式
テストデータとして、手続識別子「950A102200039000」を取り上げます。この手続は、「国民年金被保険者資格取得・種別変更・種別確認(第3号被保険者該当)届書/電子申請」です。添付書類が不要であり、公文書が発出されます。
必要なファイルは以下の通りです。
No ファイル種別 ファイル名称 備考 1 構成管理XML kousei.xml 2 申請書XML① 950A10220003900001_01.xml 様式ID(半角英数字18桁)+「_01」固定 3 申請書に対する構成情報XML① kousei20200716142110000.xml ‘kousei’+ yyyyMMddHHmmssSSS 4 申請書XML② 950A10220003900002_01.xml 5 申請書に対する構成情報XML② kousei20200716142115000.xml 申請書に対する構成情報XMLにおける「yyyyMMddHHmmssSSS」 は、申請案件フォルダ内でファイル名を一意とするために付加する当該ファイルの作成日付と時刻(100 分の 1 秒単位)です。これについても、公開資料「外部連携 API申請データ仕様 共通データ仕様書」に記載されています。
これらのファイルは次のような階層になっています。
ファイル階層kousei.xml ├ 950A10220003900001_01.xml ├ kousei20200716142110000.xml ├ 950A10220003900002_01.xml └ kousei20200716142115000.xml具体的なxmlの内容については、省略します。
5.データ変換ツールについて
今後、テストデータを作成して動作確認を行う際、データ変換ツールを利用します。このツールでは、ハッシュ値の計算、Base64形式エンコード又はデコード、エンコード又はデコード結果のファイルへの出力等を行う事ができます。次のケースでツールを利用します。
- 署名付きxml生成ドライバーで計算したハッシュ値を検証する
- 申請データを作成するため、Zip形式の申請データセットをBase64形式にエンコードする
- e-Govよりダウンロードした公文書をBase64形式デコードしてファイルに出力する
6.署名付きxml生成ドライバーを作成する
今回は、ユーザ登録・認証及び一括申請のために、署名付きxml生成ドライバーをそれぞれ作成します。なお、一括申請では標準形式及び個別ファイル署名形式で署名対象ファイルが異なるため、それぞれに対応するドライバーを作成します。
6.1 ユーザ登録・認証
ユーザ登録・認証の場合、ひな形となるxmlを使わず、kousei.xmlの<ApplData>タグで囲まれた範囲に対して、ハッシュ値を計算し、そのハッシュ値に対して署名を行う点がポイントになります。
ツールを実行する前に、環境変数「EGOV_USER_ID」にe-Gov検証環境利用申請で指定した利用者IDを設定しておく必要があります。
署名付きxml生成ドライバーは次の通りです。
make_register_xml.rbrequire 'kiji' egov_env = { "KEY" => File.expand_path("./証明書/e-GovEE01_sha2.pfx"), "KEY_PASSWORD" => "gpkitest", } user_id = ENV["EGOV_USER_ID"] pkcs12 = OpenSSL::PKCS12.new(File.open(egov_env["KEY"], "rb"),egov_env["KEY_PASSWORD"]) # ユーザIDを設定する appl_data = Nokogiri::XML::Builder.new do |xml| xml.DataRoot { xml.ApplData(Id: 'ApplData') { xml.UserID user_id } } end doc = appl_data.to_xml(save_with: 0) signer = Kiji::Signer.new(doc) do |s| s.cert = pkcs12.certificate s.private_key = pkcs12.key s.digest_algorithm = :sha256 s.signature_digest_algorithm = :sha256 end signer.security_node = signer.document.root # ダイジェスト値を計算する signer.document.xpath('/DataRoot/ApplData').each do |node| signer.digest!(node, id: '#ApplData') end # 署名する signer.sign!(issuer_serial: true) # xmlに書き込む File.write("register.xml", signer.to_xml)6.2 一括申請
(1)標準形式
標準形式では、構成管理XMLが署名付きxmlになるのがポイントです。次の手順に従って、署名を行います。
- zipperインスタンスを生成する
- zipperの第1引数(署名出力先)に構成管理XML、第2引数(署名対象)に申請書XML(+添付ファイル)のファイルパスをそれぞれ渡して、署名付きの構成管理XMLを作成する
- 署名付きの構成管理XML及び申請書XML(+添付ファイル)をzip形式ファイルに固める
- 手続が複数あれば、各手続毎に2を繰り返してから、最後に3を行う
作成するドライバーについては、make_zip_file_standard_format.rbとします。
ドライバの入出力については、ドライバがあるフォルダに./zip_data/standard/があり、その下にin及びoutフォルダがあります。inフォルダの中には、900A010200001000(1)フォルダがあり、以下3ファイルがあります。
・kousei.xml
・900A01020000100001_01.xml
・添付ファイル.docxまた、証明書ファイルについては、ドライバーがあるフォルダ直下に証明書フォルダを作成し、その中にe-GovEE01_sha2.pfxを配置します。
ドライバー実行前、outフォルダの中は空です。
ドライバーを実行すると900A010200001000(1)フォルダ、apply_data.zipが作成されます。900A010200001000(1)フォルダには、inフォルダと同じファイルがあり、ここにあるkousei.xmlは、署名付きxmlです。apply_data.zipについては、900A010200001000(1)フォルダをzip形式で固めたものです。apply_data.zipについて、データ変換ツールを使ってBase64形式にエンコードします。エンコード結果をapply_data.xmlの<FileData>タグに挿入すると、e-Govに送信できる申請データが完成します。
ドライバ実行後、実行環境は次の状態になります。
標準形式の配置構成実行環境/ ├ make_zip_file_standard_format.rb ├ 証明書/ │ └ e-GovEE01_sha2.pfx └ zip_data/ └ standard/ ├ in/ │ └ 900A010200001000(1)/ │ ├ kousei.xml │ ├ 900A01020000100001_01.xml │ └ 添付ファイル.docx └ out/ ├ 900A010200001000(1)/ │ ├ kousei.xml │ ├ 900A01020000100001_01.xml │ └ 添付ファイル.docx └ apply_data.zip署名付きxml生成ドライバーは次の通りです。
make_zip_file_standard_format.rbrequire 'fileutils' require 'zip' require "cgi" require 'date' require 'kiji' # 署名ファイル、パスワード Key = "./証明書/e-GovEE01_sha2.pfx" password = "gpkitest" # 入出力先パスを定義する input_base_path = "./zip_data/standard/in/" output_base_path = "./zip_data/standard/out/" # 入出力データのファイルパスを定義する Procedure = Struct.new(:folder, :kousei_xml, :application_xml, :attachment_file) proc = Procedure.new("900A010200001000(1)","kousei.xml","900A01020000100001_01.xml","添付ファイル.docx") input_path = "#{input_base_path}/#{proc.folder}" output_path = "#{output_base_path}/#{proc.folder}" signed_xml_path = "#{input_path}/#{proc.kousei_xml}" style_file_path = "#{input_path}/#{proc.application_xml}" attachment_file_path = "#{input_path}/#{proc.attachment_file}" app_files_path = ["#{style_file_path}", "#{attachment_file_path}"] # 出力先のフォルダ、ファイル等を消す Dir.glob("#{output_base_path}/*") do |f| FileUtils.rm_r(f) end # Zipper生成 pkcs12 = OpenSSL::PKCS12.new(File.open(Key, "rb"),password) zipper = Kiji::Zipper.new() do |s| s.cert = pkcs12.certificate s.private_key = pkcs12.key end # 署名を行う signer = zipper.sign(signed_xml_path, app_files_path) # 申請フォルダを作成する FileUtils.mkdir_p(output_path) # 署名付きxmlを書き出す File.write("#{output_path}/#{proc.kousei_xml}", signer.to_xml) # 申請書XML、添付ファイルをコピーする app_files_path.each do |f| FileUtils.cp(f, output_path) end # 出力先にあるフォルダをzipに固める zipper.write_zip(output_base_path, "#{output_base_path}/apply_data.zip")(2)個別ファイル署名形式
個別ファイル署名形式では、「申請書①に対する構成情報XML」及び「申請書②に対する構成情報XML」が、それぞれ署名付きxmlになるのがポイントです(kousei.xmlは署名付きxmlになりません)。次の手順に従って、署名を行います。
- zipperインスタンスを生成する
- zipperの第1引数(署名出力先)に「申請書①に対する構成情報XML」、第2引数(署名対象)に「申請書②XML」のファイルパスを渡して、「申請書①に対する構成情報XML」に署名を付与する
- zipperの第1引数(署名出力先)に「申請書②に対する構成情報XML」、第2引数(署名対象)に「申請書②XML」のファイルパスを渡して、「申請書②に対する構成情報XML」に署名を付与する
- 構成管理XML、申請書①に対する構成情報XML、申請書①XML、申請書②に対する構成情報XML、申請書②XMLをzip形式ファイルに固める
- 手続が複数あれば、各手続毎に2,3を繰り返し、最後に4を行う
作成するドライバーについては、make_zip_file_individual_signature_format.rbとします。
ドライバの入出力については、ドライバがあるフォルダに./zip_data/indivisual/があり、その下にin及びoutフォルダがあります。
inフォルダの中には、950A102200039000(1)フォルダがあり、以下5ファイルがあります。
・kousei.xml // 構成管理XML
・950A10220003900001_01.xml // 申請書XML①
・kousei20200716142110000.xml // 申請書XML①に対する構成情報XML
・950A10220003900002_01.xml // 申請書XML②
・kousei20200716142115000.xml // 申請書XML②に対する構成情報XMLまた、証明書ファイルについては、ドライバーがあるフォルダ直下に証明書フォルダを作成し、その中にe-GovEE01_sha2.pfxを配置します。
ドライバー実行前、outフォルダは空です。
ドライバを実行すると950A102200039000(1)フォルダ、apply_data.zipが作成されます。
950A102200039000(1)フォルダには、inフォルダと同じファイルがあります。ただし、ここにあるkousei20200716142110000.xml及びkousei20200716142115000.xmlは、署名付きxmlです。
apply_data.zipについては、950A102200039000(1)フォルダをzip形式で固めたものです。apply_data.zipについて、データ変換ツールを使ってBase64形式にエンコードします。エンコード結果をapply_data.xmlの<FileData>タグに挿入すると、e-Govに送信できる申請データが完成します。なお、apply_data.xmlの書式については、標準形式と同じです。
ドライバ実行後、実行環境は次の状態になります。
個別ファイル署名形式の配置構成実行環境/ ├ make_zip_file_individual_signature_format.rb ├ 証明書/ │ └ e-GovEE01_sha2.pfx └ zip_data/ └ indivisual/ ├ in/ │ └ 950A102200039000(1)/ │ ├ kousei.xml │ ├ 950A10220003900001_01.xml │ ├ kousei20200716142110000.xml │ ├ 950A10220003900002_01.xml │ └ kousei20200716142115000.xml └ out/ ├ 950A102200039000(1)/ │ ├ kousei.xml │ ├ 950A10220003900001_01.xml │ ├ kousei20200716142110000.xml │ ├ 950A10220003900002_01.xml │ └ kousei20200716142115000.xml └ apply_data.zip署名付きxml生成ドライバーは次の通りです。
make_zip_file_individual_signature_format.rbrequire 'fileutils' require 'zip' require "cgi" require 'date' require 'kiji' # 署名ファイル、パスワードを定義する Key = "./証明書/e-GovEE01_sha2.pfx" password = "gpkitest" # 入出力先パスを定義する input_base_path = "./zip_data/indivisual/in/" output_base_path = "./zip_data/indivisual/out/" # 出力先のフォルダ、ファイル等を消す Dir.glob("#{output_base_path}/*") do |f| FileUtils.rm_r(f) end # 入出力データのファイルパスを定義する Procedure = Struct.new(:folder, :kousei_xml, :config_info_appl_xml, :application_xml, :config_info_appl_xml_2, :application_xml_2) proc = Procedure.new("950A102200039000(1)","kousei.xml", "kousei20200716142110000.xml","950A10220003900001_01.xml", "kousei20200716142115000.xml","950A10220003900002_01.xml") input_path = "#{input_base_path}/#{proc.folder}" output_path = "#{output_base_path}/#{proc.folder}" # 申請フォルダを作成する FileUtils.mkdir_p(output_path) # Zipper生成 pkcs12 = OpenSSL::PKCS12.new(File.open(Key, "rb"),password) zipper = Kiji::Zipper.new() do |s| s.cert = pkcs12.certificate s.private_key = pkcs12.key end # 申請書1に対する構成情報XMLに対して署名を行う signed_xml_path = "#{input_path}/#{proc.config_info_appl_xml}" style_file_path = ["#{input_path}/#{proc.application_xml}"] signer = zipper.sign(signed_xml_path, style_file_path) # 署名付きxmlを書き出す File.write("#{output_path}/#{proc.config_info_appl_xml}", signer.to_xml) # 申請書2に対する構成情報XMLに対して署名を行う signed_xml_path_2 = "#{input_path}/#{proc.config_info_appl_xml_2}" style_file_path_2 = ["#{input_path}/#{proc.application_xml_2}"] signer = zipper.sign(signed_xml_path_2, style_file_path_2) # 署名付きxmlを書き出す File.write("#{output_path}/#{proc.config_info_appl_xml_2}", signer.to_xml) # コピーする申請書XML、添付ファイルをリスト化する copy_files_path = ["#{input_path}/#{proc.kousei_xml}", style_file_path, style_file_path_2] # 申請書XML、添付ファイルをコピーする copy_files_path.each do |f| FileUtils.cp(f, output_path) end # 出力先リストをzipに固める zipper.write_zip(output_base_path, output_base_path + "apply_data.zip")
- 投稿日:2020-09-18T13:15:28+09:00
[Ruby]求める年が閏年かどうか
問題
ある年のある月の日数を求めるメソッドを作ります。
問題となるのは2月が閏年かどうか。閏年の判断基準
① その年が4で割り切れる場合、閏年の可能性がある。(これだけでは確定ではない)
② 年が100で割り切れて400で割り切れない場合は、閏年ではない。
作るプログラムの雛形
def get_days(year, month) # ここに問題の処理を書き加える end puts "年を入力してください" year = gets.to_i puts "月を入力してください" month = gets.to_i days = get_days(year, month) puts "#{year}年#{month}月は#{days}日間あります"模範解答
def get_days(year, month) month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] if month == 2 if year % 4 == 0 if year % 100 == 0 && year % 400 != 0 days = 28 else days = 29 end else days = 28 end else days = month_days[month - 1] end return days end puts "年を入力してください:" year = gets.to_i puts "月を入力してください:" month = gets.to_i days = get_days(year, month) puts "#{year}年#{month}月は#{days}日間あります"
- 投稿日:2020-09-18T12:01:59+09:00
【Ruby on Rails】ランキング表示(合計、平均値)
目標
ランキングの表示
開発環境
ruby 2.5.7
Rails 5.2.4.3
OS: macOS Catalina前提
※ ▶◯◯ を選択すると、説明等が出てきますので、
よくわからない場合の参考にしていただければと思います。
- controllerとviewのみ変更
- userモデル
- postモデル(:body, :score)
- いいね機能実装
- Google Natural Language APIによるコメント感情分析導入済み(投稿の点数付け)
※今回の方法では、
数値がない(いいねが押されていない、平均値がない)ユーザーは
表示されません。controller
コントローラーさえできればできたも同然です!
平均値のランキング:app/controllers/users_controller.rbdef rank @users = User. find(Post. group(:score). order('avg(score) desc'). pluck(:user_id) ) end
合計のランキング:app/controllers/posts_controller.rbdef rank @posts = Post. find(Favorite. group(:post_id). order('count(post_id) desc'). pluck(:post_id) ) end
view
下記のようにeach降順順で表示可能
<% @users.each do |user| %> <% end %> <% @posts.each do |user| %> <% end %>Kaminariを使用する場合
前にKaminari.paginate_array( )、後ろに.page(params[:page])をつければOK
@posts = Kaminari.paginate_array(Post.find(Favorite.group(:post_id).order('count(post_id) desc').pluck(:post_id))).page(params[:page])参考サイト
- 投稿日:2020-09-18T11:16:51+09:00
Rubyで数値の絶対値を取得する方法
数値から絶対値を求める方法
そもそも絶対値とは? 原点0から見た距離 = 符号なしの数値
絶対値を取得するメソッドとして "abs" があります!(absolute valueの略)absメソッド
対象とある数値の絶対値を取得することができる。
・正の数の場合は、そのまま。
・負の数の場合は、符号を取って正の数にした数値が取得できる。正の数の場合。 num = 5.abs #=> 5 負の数の場合。 num = (-5).abs #=> 5問題
3つの整数a b cが与えられた場合、
bまたはcがaとの差が1で、かつ bとcとの数値の差が2以上の場合は"True"。
それ以外は"False"と出力するメソッドを作る。出力例
close_far(1, 2, 10) → True
close_far(1, 2, 3) → False
close_far(4, 1, 3) → True模範解答
def close_far(a,b,c) x = (a-b).abs y = (a-c).abs z = (b-c).abs if x == 1 && z >= 2 puts "True" elsif y == 1 && z >= 2 puts "True" else puts "False" end end
- 投稿日:2020-09-18T09:34:47+09:00
Railsを始める時に読んでおきたい入門書
はじめに
Railsで開発を行うにあたり読んで良かった入門書をまとめましたが、一番は公式ドキュメントを読む事です。
ただ公式ドキュメントだけだとなかなか理解が進まない事も多いかと思います。
そういった方に参考になれば幸いです。Ruby
Ruby on Rails
Rspec
・Everyday Rails - RSpecによるRailsテスト入門
https://leanpub.com/everydayrailsrspec-jp・Effective Testing with RSpec 3
https://www.amazon.co.jp/Effective-Testing-RSpec-Confidence-English-ebook/dp/B076VTMTV8/ref=sr_1_6?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&dchild=1&keywords=rspec&qid=1600388276&sr=8-6React
・速習 React
https://www.amazon.co.jp/%E9%80%9F%E7%BF%92-React-%E9%80%9F%E7%BF%92%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E5%B1%B1%E7%94%B0%E7%A5%A5%E5%AF%9B-ebook/dp/B07GWFRCR1/ref=sr_1_6?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&dchild=1&keywords=react&qid=1600389000&sr=8-6
・りあクト! TypeScriptで始めるつらくないReact開発 第3版【Ⅰ. 言語・環境編】
https://booth.pm/ja/items/2368045
・りあクト! TypeScriptで始めるつらくないReact開発 第3版【Ⅱ. React基礎編】
https://booth.pm/ja/items/2368019
・りあクト! TypeScriptで始めるつらくないReact開発 第3版【Ⅲ. React応用編】
https://booth.pm/ja/items/2367992jQuery
BootStrap
webpack
- 投稿日:2020-09-18T09:03:07+09:00
WindowsデスクトップアプリケーションをRubyで作って実行可能ファイル(.exe)を配布する!
結論から
2018年3月現在、Windows向けデスクトップアプリケーションをRubyで書いて実行可能ファイル(.exe)を作って配布したいなら、採用するのはこれ択一!
- Ruby-GNOME2 (GUIライブラリ)
- OCRA (実行可能ファイル作成ツール)
手順
Ruby-GNOME2をインストール。Windows用GNOMEライブラリも自動でインストールされる。
gem install gtk2 -rRubyスクリプトからWindows実行可能ファイルを作成するツールOCRAをインストール。
gem install ocra -rOCRAを使ってRuby/GNOME2で書かれたデスクトップアプリケーションから実行ファイルを作成。
ocra desctopapplication.rb --no-autoload --add-all-core生成された実行ファイルdesctopapplication.exeは75MBだった。
RubyでWindowsデスクトップアプリケーション(GUIアプリケーション)を作るためライブラリ比較
名前 活動状況 サポート環境 メリット デメリット Ruby-GNOME2 活発 Windows, Linux, Mac 安定、情報豊富 メンテナンスのみ。機能開発はGNOME3 Ruby-GNOME3 活発 Windows, Linux, Mac 活発に開発中 情報が少ない、機能不足のところも FXRuby 活発 Windows, Linux, Mac Rubyらしいコードが書ける 日本語入力に対応していない(表示は可能) Shoes 活発 JRuby 簡素でRubyらしいコードで書ける JRubyが必要。将来pure Rubyになる? Qt wxRuby 7年間更新なし Windows, Linux, Mac - - gem install gtk2 -r -p http://lv2west-proxy.nwdept:8080結果
Fetching: pkg-config-1.2.9.gem (100%) Successfully installed pkg-config-1.2.9 Fetching: native-package-installer-1.0.6.gem (100%) Successfully installed native-package-installer-1.0.6 Fetching: cairo-1.15.11-x86-mingw32.gem (100%) Successfully installed cairo-1.15.11-x86-mingw32 Fetching: glib2-3.2.1-x86-mingw32.gem (100%) Successfully installed glib2-3.2.1-x86-mingw32 Fetching: atk-3.2.1-x86-mingw32.gem (100%) Successfully installed atk-3.2.1-x86-mingw32 Fetching: cairo-gobject-3.2.1-x86-mingw32.gem (100%) Successfully installed cairo-gobject-3.2.1-x86-mingw32 Fetching: gobject-introspection-3.2.1-x86-mingw32.gem (100%) Successfully installed gobject-introspection-3.2.1-x86-mingw32 Fetching: pango-3.2.1-x86-mingw32.gem (100%) Successfully installed pango-3.2.1-x86-mingw32 Fetching: gio2-3.2.1-x86-mingw32.gem (100%) Successfully installed gio2-3.2.1-x86-mingw32 Fetching: gdk_pixbuf2-3.2.1-x86-mingw32.gem (100%) Successfully installed gdk_pixbuf2-3.2.1-x86-mingw32 Fetching: gtk2-3.2.1-x86-mingw32.gem (100%) Successfully installed gtk2-3.2.1-x86-mingw32 Parsing documentation for pkg-config-1.2.9 Installing ri documentation for pkg-config-1.2.9 Parsing documentation for native-package-installer-1.0.6 Installing ri documentation for native-package-installer-1.0.6 Parsing documentation for cairo-1.15.11-x86-mingw32 Installing ri documentation for cairo-1.15.11-x86-mingw32 Parsing documentation for glib2-3.2.1-x86-mingw32 Installing ri documentation for glib2-3.2.1-x86-mingw32 Parsing documentation for atk-3.2.1-x86-mingw32 Installing ri documentation for atk-3.2.1-x86-mingw32 Parsing documentation for cairo-gobject-3.2.1-x86-mingw32 Installing ri documentation for cairo-gobject-3.2.1-x86-mingw32 Parsing documentation for gobject-introspection-3.2.1-x86-mingw32 Installing ri documentation for gobject-introspection-3.2.1-x86-mingw32 Parsing documentation for pango-3.2.1-x86-mingw32 Installing ri documentation for pango-3.2.1-x86-mingw32 Parsing documentation for gio2-3.2.1-x86-mingw32 Installing ri documentation for gio2-3.2.1-x86-mingw32 Parsing documentation for gdk_pixbuf2-3.2.1-x86-mingw32 Installing ri documentation for gdk_pixbuf2-3.2.1-x86-mingw32 Parsing documentation for gtk2-3.2.1-x86-mingw32 Installing ri documentation for gtk2-3.2.1-x86-mingw32 Done installing documentation for pkg-config, native-package-installer, cairo, glib2, atk, cairo-gobject, gobject-introspection, pango, gio2, gdk_pixbuf2, gtk2 after 21 seconds 11 gems installedocra sample.rb C:\Ruby193\lib\tcltk\ --no-autoload --add-all-core
公式サイトであったはずのfxruby.orgが死んでいるけどgithub上の活動はそこそこあるので気にしない。READMEで代用。
最新バージョンは1.6.39。2017年12月25日にリリースされている。
日本語入力ができない。非公式パッチの適評が必要。らしい。
http://magazine.rubyist.net/?0001-RubyGUI#l8Shoes ... JRuby用
FXRuby ... 生きてる
公式サイト http://www.fxruby.org/ は死んでるし、
trunkは6年放置されているけど、最新版の1.6はまあまあ活発に活動されているので大丈夫。
最新のりりース
=== 1.6.39 / 2017-12-26
Fix FXGLVisual.supported and .supported?
Add support for RubyInstaller-2.5Ruby/Tk
Ruby-GNOME2
QTRuby ... 最近怪しい
Ruby/FLTK ... 当時から開発が停止
wxRuby ... 7年前から更新なしlimelight ... 6年前から更新なし
FXRubyを使う
インストール
gem install fxruby -r -p http://lv2west-proxy.nwdept:8080結果
Fetching: fxruby-1.6.39-x86-mingw32.gem (100%) Successfully installed fxruby-1.6.39-x86-mingw32 Parsing documentation for fxruby-1.6.39-x86-mingw32 Installing ri documentation for fxruby-1.6.39-x86-mingw32 Done installing documentation for fxruby after 38 seconds 1 gem installed参考
https://code.i-harness.com/ja/q/3fb29
https://code.i-harness.com/ja/q/1c32d
https://qiita.com/drumath2237/items/16ab7247f30000cb7583
- 投稿日:2020-09-18T00:21:33+09:00
form_withについて
そもそもform_withってなんですか?
情報を送信するためのヘルパーメソッドです!
Q.ヘルパーメソッドって何ですか?
A.railsにおける、主にビューでHTMLタグを出現させたりテキストを加工するために使用するメソッドの総称のことです。要は、railsでビューの加工をするメソッドといったもので覚えておくといいと思いました。(同じことを言ってますねこれ?)form_withが何かわかりました!じゃあ、どのように使うのですか?
ビューファイルに記載していきます!
次のように記述します。index.html.erb<%= form_with url: "パス",local:true do |form| %>上記のような記述ではデータベースには保存されません。
index.html.erb<%= form_with(model: モデルクラスのインスタンス,local:true,) do |form| %>で、保存するためのボタンを作成するには
index.html.erb<%= form.submit %>と記述をすることで、ボタンを表示させ保存等を行うことができます。(ルーティングからコントローラーの設定ができていればの話ですが!)
まとめ
form_withは、要するに、ビューで使用するデータを受け渡すメソッドといった考えでいたらいいのかなと思っております。間違っていたら、ご指摘お願いします!
- 投稿日:2020-09-18T00:01:02+09:00
Rails 6で認証認可入り掲示板APIを構築する #13 認証ヘッダの付与
←Rails 6で認証認可入り掲示板APIを構築する #12 userとpostの関連付け
factoryの修正
factoryから直していきます。
一番多く飛んでいるのが
ActiveRecord::RecordInvalid: Validation failed: User must existというエラーですね。
これはcreate(:post)
した時に、user_idがnilになることで発生しています。factoryの修正で潰しましょう。
spec/factories/posts.rbfactory :post do subject { "MyString" } body { "MyText" } + + after(:build) do |obj| + obj.user = build(:user) if obj.user.nil? + end end
after(:build)
は、buildやcreateをした後に実行されます。
post.userにbuildしたuserを入れることにより、User must existエラーを潰せます。なお、
if obj.user.nil?
をすることでcreate(:post, user: user)
のように特定ユーザーを渡して生成した際、内部処理で上書きされてしまうのを防いでいます。実はもっとシンプルな方法で、とりあえず大半潰すことができたりします。
spec/factories/posts.rbfactory :post do subject { "MyString" } body { "MyText" } + user end
ただしこの方法だと
create(:post)
した時はいいのですが、build(:post)
した時にuserがnilで返ってくるため、前者の対応をしています。request specとcontrollerの修正
spec/requests/v1/posts_request_spec.rbdescribe "POST /v1/posts#create" do + let(:authorized_headers) do + user = create(:user) + post v1_user_session_url, params: { email: user.email, password: "password" } + headers = {} + headers["access-token"] = response.header["access-token"] + headers["client"] = response.header["client"] + headers["uid"] = response.header["uid"] + headers + end let(:new_post) do attributes_for(:post, subject: "create_subjectテスト", body: "create_bodyテスト") end it "正常レスポンスコードが返ってくる" do - post v1_posts_url, params: new_post + post v1_posts_url, params: new_post, headers: authorized_headers expect(response.status).to eq 200 end it "1件増えて返ってくる" do expect do - post v1_posts_url, params: new_post + post v1_posts_url, params: new_post, headers: authorized_headers end.to change { Post.count }.by(1) end it "subject, bodyが正しく返ってくる" do - post v1_posts_url, params: new_post + post v1_posts_url, params: new_post, headers: authorized_headers json = JSON.parse(response.body) expect(json["post"]["subject"]).to eq("create_subjectテスト") expect(json["post"]["body"]).to eq("create_bodyテスト") end it "不正パラメータの時にerrorsが返ってくる" do - post v1_posts_url, params: {} + post v1_posts_url, params: {}, headers: authorized_headers json = JSON.parse(response.body) expect(json.key?("errors")).to be true end endユーザーを生成し、そのユーザー情報を元にログイン。
レスポンスヘッダにある認証用3キーをheadersに加えてpostをすることで、create(:user)
したユーザーとして認証された状態でアクセスをします。
ただしcontroller側をまだ直していないのでエラーのままです。controllerの修正
app/controllers/v1/posts_controller.rbdef create - post = Post.new(post_params) + post = current_v1_user.posts.new(post_params) if post.save上記修正でテスト通過するようになるはずです。
挙動を説明すると、まずheadersで認証情報が渡ってきているため、controllerでは
current_v1_user
というメソッドが使えます。これはログイン中のユーザーインスタンスが返ってくるものです。
つまりcurrent_v1_user.posts.new
は、ログイン中のユーザーに紐づくpostをインスタンス化しています。
それにより、ログインしているユーザーのpostが作られます。rspecの認証済みヘッダ取得処理をhelperに移動
テストは通るようになったのですが、今後Punditを入れて認可を実装していくにあたり、認証済みヘッダを取得する処理を都度書いていては保守性が下がるので、spec用のhelperに移動します。
spec用helperは一般的にspec/supportに置いていくのでディレクトリを作ります。
$ mkdir spec/support $ touch spec/support/authorization_spec_helper.rbrspecにあった処理をごっそりこっちに持ってきます。
spec/support/authorization_spec_helper.rb# frozen_string_literal: true # # 認証用ヘルパ # module AuthorizationSpecHelper def authorized_user_headers user = create(:user) post v1_user_session_url, params: { email: user.email, password: "password" } headers = {} headers["access-token"] = response.header["access-token"] headers["client"] = response.header["client"] headers["uid"] = response.header["uid"] headers end endspec/support下に配置しただけでは勝手に読み込んでくれないので、spec/rails_helper.rbを修正します。
spec/rails_helper.rb-# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f } +Dir[Rails.root.join("spec", "support", "**", "*.rb")].sort.each { |f| require f } ... RSpec.configure do |config| ... + config.include(AuthorizationSpecHelper, type: :request) endコメントアウトされていたspec/support下を読みにいく処理を有効化するのと、AuthorizationSpecHelperをincludeします。上記のように書くことで、request specのみ有効になります。
spec/requests/v1/posts_request_spec.rb... require "rails_helper" RSpec.describe "V1::Posts", type: :request do + let(:authorized_headers) do + authorized_user_headers + end ... describe "POST /v1/posts#create" do - let(:authorized_headers) do - user = create(:user) - post v1_user_session_url, params: { email: user.email, password: "password" } - headers = {} - headers["access-token"] = response.header["access-token"] - headers["client"] = response.header["client"] - headers["uid"] = response.header["uid"] - headers - end ...あとは上記対応で完了。
テスト結果が変わらずgreenであればとりあえずOKです。テストは全部通過するものの、そもそもテストコードが不十分。
認証されていない時に#createを叩くと500エラーになったり、そもそも自分以外の投稿を更新したり削除できてしまう現状の仕様は困るので、次次回でいよいよ認可を入れていきます。次回はseedの整備を行います。
本日はここまで。続き
→
【連載目次へ】