似た顔のFANZA女優を顔写真から検索出来るAJAX実装に苦労した

この記事は [驚異のFANZA女優検索](https://fanza-actress-bust-search.azurewebsites.net/) Advent Calendar 2021 の 4 日目の記事です。


#顔検索でAjaxを使いたい
似た顔のFANZA女優を顔写真から検索出来るようにした。実際に顔検索する部分は一旦postで作ってみたが画面切り替えが入るのがUX的にイマイチだった。画像選択から結果表示まで画面切り替えなしにしたい。そうなるとAJAXか。そういえば自分はASP.NET MVCではもう記憶にないぐらい前にちょっとしたAJAXを書いたことしかなかった。


#ASP.NET MVCでAJAXを実装するには？
と言うわけで、asp.net mvc ajaxでググってっみたが、例えばキーワードで出たのは以下のような方法。

[ASP.NET MVC : jQuery.ajaxを使った非同期処理](https://qiita.com/lusf/items/fdcfc0396514f64adc67)、
[ASP.NET MVCでAjaxでPOST](https://shuhelohelo.hatenablog.com/entry/2019/11/25/230250)、
[jQuery と ASP.NET で AJAX なページを作る基本](https://aspnet.keicode.com/basic/jquery-ajax-basics.php)
[ASP.NET MVC の Ajax 通信で例外を処理する](https://gooner.hateblo.jp/entry/2015/07/30/ajax-exception/)

ここでまず個人的には遥か昔formでAjaxみたいな書き方をしたような記憶があったので、最近ではJavaScriptでやるのが普通なのかなと思った。また、AJAXなら実際にajaxというキーワードで書くのかと思っていたけれど、ajaxもあるけれど普通にgetやpostしている例もあって、どっちが正しいのかなと思った。もう一つ気になったのは書かれた記事が1年から6年ぐらい前のものまでJQueryで書いてあって、これ書き方的に古くないのかな？と思った。あとControllerも戻り値も普通にViewを返すケースとJSONを返すケース、さらにViewでもPartialViewで返すケースがあって、結局2021年現在ASP.NET MVCでAjaxをどうやれば良いのかは良く分からなくなってきた。

#ASP.NET MVC AJAXで使うべきライブラリは？
さらに使うべきライブラリも良く分からない。jquery.unobtrusive-ajax.jsを使えとかMicrosoft.jQuery.Unobtrusive.Ajaxを使えとか出てくる。特にjsはこれ使わないと何が出来ないのか良く分からない。そもそもさっきみたサンプルでは使ってないものもありそう。さらにjquery.unobtrusive-ajax.jsでこれでググると今度はAjax.BeginFormというキーワードがでた。あれやっぱり記憶通りFormで何とかするのかなと思った。正しい書き方も正しいライブラリも良く分からない状態だ。
[ASP.NET Core MVC と Ajax ライブラリ](http://www.surferonwww.info/BlogEngine/post/2020/03/06/use-jquery-unobtrusive-ajax-library-in-aspnet-core-ajax-mvc.aspx)
[Ajaxを利用して非同期更新を行う。](https://haronoid.hatenablog.com/entry/2016/09/02/173422)
[MVC5 Ajax.BeginForm()を利用するための手順](https://qiita.com/m-otoguro/items/1250015aed6907a8d9e7)

#ASP.NET MVCの愚痴
そもそもASP.NET自体が正しい検索キーワードが難しい。NETだと.NET Frameworkもヒットしてまうので.NET Coreに限定したい。ただ正式名称は.NET 5から.NETだ。そうなるとASP.NET .NETで検索する？.net被ってるし。.NET 5だとMVC5とも数字被っているし。もう.NET Coreを使ってないのに.NET Coreと打たなければならないし、一体どうググったら正解なのだ！というのがある。結局自分が正しいと思うasp.net mvc core 5 ajaxぐらいでググっても新しい情報は少しずつしかでない。あと凄い不満なのがMicrosoftの公式の情報が殆どヒットしない。正直関係ありそうなキーワードで大体TOP30ぐらいは広めに見たけれど、まあ正解が分からない。調べるほどに混乱する状態で非常に良くないと思う。

#自分なりのASP.NET MVC AJAXの実装
先ず参考にしたのはこの書き方、ちょっと古い気もしたが、なんとなく自分が昔書いた書き方に近いので一旦これで実装してみて徐々に直しておけば良いやと思った。送信はJavaScriptの中でajaxで行って、コントローラーはそれを受け取って、PartialViewで返すという実装だ。一旦これでやろうとしていることは動くようになった。
[ASP.NET MVC の Ajax 通信で例外を処理する](https://gooner.hateblo.jp/entry/2015/07/30/ajax-exception/)

実装してから情報を整理するためjQueryのajaxでググったら以下の記事が出た。
[jQuery ajaxのsuccess、error、completeは非推奨って知ってた？](https://medium.com/@bbxxuw/jquery-ajax%E3%81%AEsuccess-error-complete%E3%81%AF%E9%9D%9E%E6%8E%A8%E5%A5%A8%E3%81%A3%E3%81%A6%E7%9F%A5%E3%81%A3%E3%81%A6%E3%81%9F-797da1b8adc7)

ああ、この書き方って古いのかと思って、そもそもajaxっていうJQueryをJavaScriptで書くとどうなるんだと思って、ここから見直し。
[JavascriptのAjaxについての基本まとめ](https://qiita.com/katsunory/items/9bf9ee49ee5c08bf2b3d)

ただif (req.readyState == 4) とかこの書き方は流石にキツイと思ったので、もう少し調べた。これは公式に正しい方法が出てきた。良かった！Fetch API を使ってPOSTで書くのが良いのかなと思った。で結局jquery.unobtrusive-ajax.jsとかいらなくないかな？と思って抜いても正しく動いた。ただこれでもasync,await使ってないのが気になる。
[チュートリアル: JavaScript を使用して ASP.NET Core Web API を呼び出す](https://docs.microsoft.com/ja-jp/aspnet/core/tutorials/web-api-javascript?view=aspnetcore-5.0)

#自分なりのASP.NET MVC AJAXの実装
最終的に書いたコード。先ずjsはこう。この状態で自分の情報が正しくて最新ですとはとても書けない。TypeScriptも使ってない。が、色々な情報をつなぎ合わせて自分なりに作って新しめで書いた実装方法はまとめておきたいと思った。というわけでコードで〆

先ずjsで顔写真をnearFaceActressListFaceUrlにajax通知。

```js:js
    document.getElementById('faceUrlForm').addEventListener('submit', async (event) => {
        //Now Loadinの表示などここでUIを色々対応する
        document.getElementById('Loading').style.display = 'none';

        //設定された画像URLをPOST
        const faceUrl = document.getElementById('faceUrl').value;
        const response = await fetch('/nearFaceActressListFaceUrl', {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ faceUrl: faceUrl })
        });
        if (!response.ok) return;

        //UIの表示の切り替えなど…
        document.getElementById('Loading').style.display = 'none';
        //レスポンスで返った来たcshtmlの内容を描画
        document.getElementById('actressList').innerHTML = await response.text();
    });
```
postをcsで受けてサーバー側で顔認識された女優リスト作り結果をPartialViewで返す

```cs:cs
    [HttpPost]
    [Route("/nearFaceActressListFaceUrl")]
    public async Task<IActionResult> NearFaceActressListFaceUrl([FromBody]FaceUrlRequest request)
    {
        if (!ModelState.IsValid) return null;
        …
        …
        …
        return PartialView("NearFaceActressList", viewRecommendActressList);
    }
```
渡ってきた女優リストをcshtmlで描画

```cs:NearFaceActressList.cshtml
@model List<FanzaActressSearch.ViewModel.ViewActress>
<div>
    <section>
        <div class="my-3">
…
…
…
```