tekitoumemo’s diary

思ったことを書くだけ。長文版Twitter

.Net CoreのRazorエンコードとかSEOの影響とか

.Net CoreのRazorは、デフォルトでエンコードされるように出来てます。これが普通に最低でした。みんなの洋楽ランキングは、タイトルをシステムで出力したりしてるんですが(以下)、それが全部エンコードされてわけわからん感じになります。

<title>@ViewBag.Title - みんなの洋楽ランキング</title>

こんな感じになる
f:id:tekitoumemo:20180806213305p:plain

.NET CoreのRazorはデフォルトでHtmlEncoder.Defaultってのが使われているのですが、これをエンコードしないようにDIして制御しなければいけません。なので以下のコードで制御します。

services.AddSingleton (HtmlEncoder.Create (UnicodeRanges.BasicLatin,
    UnicodeRanges.CjkSymbolsandPunctuation,
    UnicodeRanges.Hiragana,
    UnicodeRanges.Katakana,
    UnicodeRanges.CjkUnifiedIdeographs));

これはシングルトンにしてください。

f:id:tekitoumemo:20180806213842p:plain
最近SEO下がりまくってアクセスが激減したのですが、いくつか要因があるので一つずつ潰してます。
iTunesアフリエイトSVGがクソ重い(改善済)
・HTMLエンコード(改善済)
・導線とコンテンツ不足(日々改善)
一旦これで様子見て経過を書いていきます。まぁ今月は1万PV割れする可能性大なのでボロボロですね。

Net Frameworkではこんなことなかったので、気をつけなければ。。

.Net Coreの部分ビュー

同じビューを何回も書き続けるのが微妙なので、ASP.NET Coreでは部分ビューってのがあります(どのフレームワークにもあるけど)。ASP.NET5でもHTMLヘルパーであったのですが、ちょっと書き方が変わってるので備忘録として。

HTML5であった@Html.Partialとほとんど同じ機能ですが、.NETCoreになってHTMLヘルパーからタグヘルパーに変更されたのでクソダサい記述はなくなりました。さらにasync、awaitが使えるので非同期でビューを動かすことができるので、重いビューでも多少は大丈夫になった感じですかね?わざわざJS使う必要がないところとか良い感じでしょうか。

部分ビューとは

部分ビューとは、別のビュー内でレンダリングされるビューのことです。

1枚のページに複数のHTMLがかけるよ!しかもMVCでってこと。つまりこういうことです。
f:id:tekitoumemo:20180805220945p:plain
この赤い部分は再利用するので部分ビューにしています。

部分タグ ヘルパー

以下の記述ではViewsのSharedにある_TestPartial.cshtmlがタグを記述したところに挿入されます。

<partial name="_TestPartial" />

非同期もいけます。

@{
    await Html.RenderPartialAsync("_TestPartial");
}

パスを指定すれば、ディレクトリわけもできます。

<partial name="~/Views/Hoge/_Test.cshtml" />

モデルをバインドされる場合は以下です(試してない。

<partial name="_Test.cshtml" for="Model" />

非同期は、.NETCoreになってから追加された機能(おそらく)なので、わざわざJSフレームワークでやる必要はねぇなと思う場所には使えるかもしれません。モデルバインドも部分ビューもめっちゃ使えるのですが、結局依存しまくって汎用的に使えなかったり、技術負債になりがちなのでちゃんと考えて使った方が良いかもしれませんね。導入するのは比較的簡単なので、ビューのメンテナンスがめんどくさいなと思ったら導入するぐらいの気持ちでやった方が良いと思います。

React使ってちょっとしたもの作った

今週、seo下がっちゃってアクセス数激減してます、つらみ。今月は21KPV着地しそうです。今回はReact使って「いいね」機能を作ったので、簡単な仕組みと感想文を書きたいと思います。

f:id:tekitoumemo:20180726203349p:plain

上の画像の通り、いいねを作りました。会員登録とか無いサイトなので「いいね」作っても意味はないと思いますが、React使ってみたかったんです。以前に書いたSPAじゃないReactの延長みたいなもんです。

簡単な仕組み

みんなの洋楽ランキングには、会員がいないので誰でも「いいね」が出来ます。「いいね」して、また来たときに「いいね」の状態が残ってないと萎えるので、状態を保存するためにローカルストレージを使いました。

クッキーだと、最大20個?までと制限があるので、10M保存出来るローカルストレージが最適でした。ストレージにはJsonで状態を持ってる感じです。サーバ側はCRUDAPI作って押した状態(付けたか外したか)によって呼び出すAPIを変えています。

デザイン

Instagramモロパクリしてます。本当はTwitterを丸パクリしたかったのですが、cssの知識がないので妥協しました(下のリンクみたいにしたかった、かわいい)
https://isaito-kurumizawa.github.io/GoodButton/

出来なかったこと

今月と来月は時間を作ることが難しくなるので、仕事終わってから2日で仕上げました。やはり色々課題が残っちゃいました。

・Azure でnpm i出来てない。
しょうがないからgithubにnode_moduleをブッこむという荒技を使ってます。ちなみにAzure Web Serviceはデプロイ時にnpm iしてくれるのでAzureは悪くないです。単純にpackage.jsonの書き方が悪いだけだと思います。デプロイはnpm i productionなので下見て変えれば大丈夫だと思う。

・スタイルが効かない
Reactのスタイルはhtmlに書き込まれるのですが、なんかデプロイしたら効かなかった。まぁcssはminifyしてるんで、ここら辺はどうでもいいかなと思ってます。おそらくPhysicalFileProvider使わなければいけないのかな?

・Redux使えば良かった
単一コンポーネントなので無駄に分散させる必要ないなーと思ってたから使わなかったんですが、勉強がてら使えば良かった。

普段Angular使ってる私がReact使ってみた感想

今仕事でAngularを使っていてAngular歴3カ月程度になります。Angular嫌いでロクに勉強もしてないので仕組みとかよくわかってないですが、React使ってみてAngularの良さがわかったのでちょっとぼやき程度に話したいと思います。

Angularはちょー難しいけど、スキルが低いチームほど使った方が良い

一般的にはReact、Vue.jsの方が初心者向けと言われてるのですが、API群が少なく開発者が選定しなければいけません。これの何が難しいのかというと他人のソースをパクりにくいので、統一してコードを書くのが難しいだろうなぁと思います。Angularは、基本的にRxJs、InputOutput、typescript、ライフサイクルさえ覚えればだいたいみんな同じコードが書けるようになります。つまり、ほぼAngularで完結するようなAPI群が揃ってるのでスキルレベルに左右されないチームが出来るってことになります。現に今のチームでダントツで仕事ができない僕でも多少なんとかなってるのでまぁ良いフレームワークなんでしょう。ただ、難しいのはやっぱり状態管理で、ReactみたいにRedux使おうぜ!みたいな風潮がないので、普通に作り上げるとバグを潰す期間が必要で普通に辛いです(@ngrx/storeってのがあるんですけどね)また、DIのせいでテストを書くのがめっちゃむずかったりするんでそこらへんはスキルが必要なんでしょう。その点、Reactは初見でも理解が出来て、ステートレス推奨してるので僕はReactの方が好きですね(まだ2%ぐらいしか理解してないけど)。Angular難しいし、1系から2系に変わって全然違う言語になった嫌われ者なので僕も嫌いでしたが、だんだん理解が出来るようになりました。Angular6からRxJsの変更がえぐいのでまた嫌われそうですね(笑)


こんなのがあるんですね。便利。
github.com

git 2.18から「Unknown option: --list-cmds ...」が起きたのでその対処法

macで開発してとgitコマンドをtabで入力補完してくれ他のでイライラしたので、入力補完をいれてみました(今更)。まぁ、Homebrew経由でインストールすればこんなことにならないのでちゃんとやりましょう。

僕の場合はgitが以下のインストーラーで、bash-completionがHomebrewで入れたのでちょっとめんどくさいことになったのでその備忘録として。だから参考にしないでください。
Git

Homebrewをインストール

brew.sh
Macで入れてない人いないと思うんで上記のサイトのコマンドで適当にやってください。

bash-completionのインストール

brew install bash-completion

「/usr/local/etc/bash_completion.d/」にコマンド補完用のシンボリックリンクが出来ていると思います。僕の場合はgit-completion.bashとかが入ってますが気にしないでください。
f:id:tekitoumemo:20180714193042p:plain

bash_completion.dに以下を書く

if [ -f $(brew --prefix)/etc/bash_completion ]; then
  source $(brew --prefix)/etc/bash_completion
fi

gitをHomebrewでインストールしてないのでgit-completion.bashとgit-prompt.shがありません。
なのでwgetで入れます(そもそもwgetすらない)。

brew install wget
cd /usr/local/etc/bash_completion.d/
wget https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.bash -O git-completion.bash
wget https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh -O git-prompt.sh

cd
source .bash_profile

でgit statusって打とうとしてタブ入力したら以下のエラー

git staUnknown option: --list-cmds=list-mainporcelain,others,nohelpers,alias,list-complete,config
usage: git [--version] [--help] [-C <path>] [-c name=value]
           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
           [-p | --paginate | --no-pager] [--no-replace-objects] [--bare]
           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
           <command> [<args>]

これは2.18から「--list-cmds」が追加されたようで、git-completion.bashは2.18、gitは2.16.3だったのでgitでオプションがないよ!って言われてるみたい

git-completion.bashのバージョンを下げるかgitをあげるかどちらかなんですが、下げる意味がないので私はバージョンをあげて対処しました。Homebrewでインストールしてないのでここインストーラーから入れました。

まじで誰も得しない記事だなこれは!

Bing Webマスターツールを使う

SEOのお話。

なんだかんだ先月は18KPVまで行きました。今月は明日か明後日ぐらいには1万に達する感じなので2万ちょいの見込みです。ちょっと伸びが止まってきましたね。。
f:id:tekitoumemo:20180711215313p:plain

まぁ、いくつか要因はあるんですが検索順位がまったく変動しないことが一番かなと思います。やっぱりレ○チョクとかDoco○oとかには簡単には勝てんすね。
f:id:tekitoumemo:20180711215125p:plain

ここらへんは頑張りとかでは何ともならんのでどうしようもないですが、上の結果を見てBingが圏外になってるじゃないですかー!!と思ったのでここら辺を何とかしようと思います。まぁBingはシェア率がモバイル0.1%、PCが8%ぐらいなので効果が少ないとは思うのですが、少しはプラスになるのは間違いないのでやっとく意味はあるかなと

Bing Webマスターツールに登録する

www.bing.com
Facebookやらいろいろ使えるんで、適当に登録してください。

サイトを登録する

f:id:tekitoumemo:20180711222617p:plain
トラフィックが最も多いローカル時刻を聞かれるのでアクセスが多い時間を指定してください。みんなの洋楽ランキングは夜が圧倒的に多いのでそこを指定しました。アナリティクスでどの時間にアクセス多いか見れるので確認して見てください。こちらは[ホーム]から閲覧できます。
f:id:tekitoumemo:20180711222934p:plain

所有権を確認する

f:id:tekitoumemo:20180711223125p:plain
3つやりかたがありますが、1がいいでしょう、何も汚さないし。

サイトマップを登録する

f:id:tekitoumemo:20180711223444p:plain
トラフィックが最も多いローカル時刻を指定したからでしょうか、保留中になってるのでトラフィックがない時間に送信されるんですかね?よくわかりません。

URLを登録する

f:id:tekitoumemo:20180711223719p:plain
インデックスしてほしいURLを指定しました。

これで一通り完了したので、こっからちょいちょい確認して経過を見ようかと思います。

雑談
昨日、Githubが落ちたんですが、AzureのGithubデプロイも失敗しました。一旦切断して再度セットアップしたら直りました。Microsoftに買収されてからこれだと不安になりますね。ま、GitLabとかbitbucketに変える気は全くないですが(いろいろ楽よGithubの方が

追記
すごいね、たった1日で上がってきた!
f:id:tekitoumemo:20180712210756p:plain

ASP.NET Core MVCにおけるファイルダウンロードのあれこれ

f:id:tekitoumemo:20180709200515p:plain
結構サボってたー。ブログ書き続けるのは難しい!平日は寝たいし、休日は遊びたいし。

今回はASP.NET Core MVCでのファイルダウンロードの実装方法を説明します。ファイルダウンロードにもいろいろあって

  • ファイルを保存させる
  • ファイルをブラウザで表示させる

主にこの2つのパターンがあると思います。結構いろんなやり方があったのでその一部を書きます。

PhysicalFileResult

docs.microsoft.com
物理ファイルにアクセスしてファイルを操作する方法です。挙動はブラウザにファイルが表示されます。.net coreに移行する際など今までのディレクトリ構成を変えずに実装出来る方法です。

Startup.cs
物理的にアクセスする為にアクセスを許可します。今回は直下のImagesディレクトリを許可します。

app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(
    Path.Combine(Directory.GetCurrentDirectory(), "Images")),
    RequestPath = "/Images"
});


Controller
物理パスを取得し、PhysicalFileResultにパスとMIMEタイプを指定します。

public IActionResult PhysicalFileResult()
{
    var filePath = Path.Combine(Directory.GetCurrentDirectory(), "Images", "neet.png");
    return new PhysicalFileResult(filePath, MediaTypeNames.Image.Jpeg);
}

VirtualFileResult

docs.microsoft.com
これはURLで指定するような仮想パスを指定してファイルを取得する方法です。挙動はブラウザにファイルが表示されます。Controllerで完結するので比較的使いやすい部類かなと思います。
Controller
URLのパスとMIMEタイプを指定します。

public IActionResult VirtualFileResult()
{
    return new VirtualFileResult(@"/Images/wwwroot_neet.png", MediaTypeNames.Image.Jpeg);
}

FileContentResult

docs.microsoft.com
FileContentResultは二つ方法があります。こちらはDBに画像が格納されているパターンやバイナリを使う際に有効です。
まずはファイルがダウンロードされるパターン。

Controller
バイナリとMineタイプとファイル名を指定します。

public IActionResult FileContentResult()
{
    var filePath = Path.Combine(Directory.GetCurrentDirectory(), "Images", "neet.png");
    var file = System.IO.File.ReadAllBytes(filePath);
    return File(file, MediaTypeNames.Image.Jpeg, "neet.png");
}

ブラウザに表示するパターン
上記のコードでファイル名を省いたパターンです。

public IActionResult FileContentResult()
{
    var filePath = Path.Combine(Directory.GetCurrentDirectory(), "Images", "neet.png");
    var file = System.IO.File.ReadAllBytes(filePath);
    return File(file, MediaTypeNames.Image.Jpeg);
}

ResponceヘッダーのContent-Dispositionによってブラウザの挙動が変わるようで、ファイルを指定すると「attachment」になりファイルをダウンロードする動き、指定しないと「inline」になりファイルをブラウザで表示するような動きになります。

Responceヘッダを直接書きければ、同じように動くんですがわざわざやる意味もないのでここら辺を活用できればいいと思います。今回説明したパターンのコードを以下に置いとくので参考なれば。
github.com

【参考】
stackoverflow.com

Macで作ったASP.NET MVC CoreのDocker ImageをHerokuで動かす【完成編】

herokuでASP.NET MVC Core MVCが動いた!ちなみに以前に書いた記事では出来ませんでした。。
tekitoumemo.hatenablog.com
はい、出来なかったではないですね、確実にやり方が違いました。ただ、実際動いたけどよくわかってないのでやり方を書く感じで。

BuildPack

herokuで.NetをサポートしていないのでBuildPackが必要です。
github.com
Buildpackというのは、アプリケーションをデプロイするたびに任意のコマンドを実行できる仕組みだそうで、これがあるとherokuのDocker環境で.Net Coreが動くようになります。

Buildpackをなんかすれば動くそうですが、サンプルアプリがあったので、これを活用します(助かった)サンプルは以下のURLがあります。
github.com

サンプルアプリは、Reactのサンプルになっています。僕が作りたいアプリケーションはMVCなのでsrc/AspNetCoreDemoApp配下のファイルをごっそり変えちゃいます。以下の内容に変更しました。

AspNetCoreDemoApp.csproj

メールするアプリなので、MailKitをインストールしました。Microsoft.AspNetCore.Allは.Net Core 2.1からMicrosoft.AspNetCore.Appに変更されたので変更しておきましょう。

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="MailKit" Version="2.0.3" />
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.7" />
  </ItemGroup>

  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.3" />
  </ItemGroup>

</Project>
Program.cs

新規で作ったテンプレートと同じ状態にします。

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

namespace AspNetCoreDemoApp
{
    public class Program
    {
        public static void Main(string[] args)
        {
            BuildWebHost(args).Run();
        }

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .Build();
    }
}
Startup.cs

これも新規で作ったテンプレートと同じ

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

namespace AspNetCoreDemoApp
{
    public class Program
    {
        public static void Main(string[] args)
        {
            BuildWebHost(args).Run();
        }

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .Build();
    }
}

あとは、MVCアプリで必要なファイルを追加してHerokuにデプロイすれば完了です。herokuはgithubと連携出来るので以下のように関連付ければいけます。
f:id:tekitoumemo:20180629000214p:plain

そしたらhttps://{アプリ名}.herokuapp.com/にアクセスしてみましょう。

f:id:tekitoumemo:20180629000345p:plain
入社一年目のときに作ったフリーソフトの問い合わせ画面が出来ました。MailKitもちゃんと動くしいまのところ問題は出ていません。ただ、実際に運用するのは微妙ですが、無料で.Net Coreのアプリケーションが公開出来るのは素晴らしいですね。SSL対応出来てよかった。

(雑談)
ITunEsTooL - あなたのiTunesを快適に
このソフトなんですが、毎月それなりのディナーが食べられるぐらいまでの収入が入るようになりました。完全放置気味+ソース汚すぎ+終わってる技術なのでだれか10万ぐらいで買ってくれないですかね(笑)