あきちゃんの飽き飽き備忘録

ほぼ死んでるブログ

Blazor Serverでチャットアプリ

初めまして、アキちゃんです。

備忘録をかねてブログを書くことにしました。

今回はBlazorでチャットアプリについて書きます。

それでは早速・・・Blazroでチャットアプリ!

Lesson.1 事の発端

C#でチャットアプリ作りたいなあと思っていたところ、以下のような記事を見つけました。

基本なぞるだけですが、せっかくなのでやってみようと思います。

learn.microsoft.com

Lesson.2 プロジェクト作成(SignalRChat)

プロジェクト名は SignalRChatで、Blazor Server プロジェクト(.Net6)で作成します。

基本デフォルトのまま次へ押しとけば大丈夫ですが一応以下記事にまとめています。

akr9915.hatenablog.com

Lesson.3 実装

プロジェクトを右クリック→Nugetパッケージの管理 からMicrosoft.AspNetCore.SignalR.Clientをインストールします。

プロジェクトを右クリック→追加→新しいフォルダーの追加 からHubsフォルダを作成します。

Hubsフォルダを右クリック→追加→クラス からChatHub.csを作成します。

Hubs/ChatHub.csを以下のように修正します。

using Microsoft.AspNetCore.SignalR;
namespace SignalRChat.Hubs // SignalRChatはプロジェクト名に合わせる
{
    public class ChatHub : Hub
    {
        public async Task SendMessage(string user, string message)
        {
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }
    }
}

Program.csを以下のように修正します。

using SignalRChat.Data;
using Microsoft.AspNetCore.ResponseCompression;
using SignalRChat.Hubs;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.AddResponseCompression(opts =>
{
    opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
        new[] { "application/octet-stream" });
});

var app = builder.Build();
app.UseResponseCompression();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.MapBlazorHub();
app.MapHub<ChatHub>("/chathub");
app.MapFallbackToPage("/_Host");
app.Run();

以下リンクの チャット用の Razor コンポーネント コードを追加する の手順通りにPages/Index.razorを修正します。

learn.microsoft.com

ここまできたら実行して出てきたタブを複製して2画面にします。
任意のUserとMessageを送信して画面に表示されたら動作は完了です。

Lesson.4 あとがき

総PV数が100超えました、うれしくて目からソースコードがでてきました。

以上!

Blazor Serverプロジェクトの作り方

初めまして、アキちゃんです。

備忘録をかねてブログを書くことにしました。

今回はBlazor Serverプロジェクトの作り方について書きます。

それでは早速・・・Blazor Serverプロジェクトの作り方!

Lesson.1 事の発端

毎回記事書くたびにおんなじ文章を載せるのは、コードを書く者としてよろしくないと思ったため、記事全般で使用するテンプレートプロジェクトの作り方を以下にまとめます。

環境は Visual Studio 2022 Community を使用します。

Visual Studioに関して、Visual Studio 2019以前のものでは.Net 6に非対応のため恐らくデバックできないとおもいます。

なのでVisual Studio 2022以降のものを使うことを推奨します。

Lesson.2 プロジェクトの作成

Visual Studio 2022 Comunityを開き ファイル→新規作成→プロジェクト を押下し、新しいプロジェクトの作成 を押下します。

Blazor Server アプリを選択し、次へを押下します。

プロジェクト名に任意の名前を入力(今回はサンプルとしてSampleTemplate)し次へを押下します。

僕の記事では特に凝ったことをする予定はないためデフォルトのまま作成を押下します。

以下画面がでたら画面上部の任意のプロジェクト名(今回はSampleTemplate)のボタンをクリックしプログラムを実行します。

以下画面がでたらプロジェクトの作成の完了です。

Lesson.3 あとがき?

毎回このページ開くのも面倒なので覚えておくといいかもしれません。

以上!

【Qiita Advent Calender 2022】Blazor+Radzen & みんなの翻訳でLine風翻訳あぷりつくってみたよ

初めまして、アキちゃんです。

備忘録をかねてブログを書くことにしました。

今回は初の試みで、Qiita Advent Calender 2022に記事を書いてみることにしました。

内容は「Blazor+Radzen & みんなの自動翻訳@TexTraでLine風翻訳あぷりつくってみたよ」です。

それでは早速・・・

Blazor+Radzen & みんなの自動翻訳@TexTraでLine風翻訳あぷり

(ピクチャインピクチャじゃないと潰れてほぼみえませんね・・・)

Lesson.0 事前準備

ある程度の準備はしておきたいと思います。

1.Blazor Serverのプロジェクトの作成し、Radzen導入だけ済ませたものを用意します。本記事では、プロジェクトの名前は"Honya-kun"として作成します。

akr9915.hatenablog.com

2.みんなの自動翻訳@TexTraのAPIを使いたいため、以下サイトでユーザー登録を行います。

mt-auto-minhon-mlt.ucri.jgn-x.jp

3.2で登録したユーザー情報で、みんなの自動翻訳@TexTraにログインしユーザーの設定からWeb APIAPI keyとAPI secretを確認します。(併せてユーザーIDも後で使うので覚えておきます)

Lesson.1 みんなの自動翻訳API

まずみんなの自動翻訳のAPIを使えるようにしたいため、サイトから公開されているソースコードを取得したいとおもいます。

Dataフォルダを右クリックし"追加"→"クラス"Textra.csを追加し、作成されたファイルのソースコードをすべて消します。

みんなの自動翻訳のページに行きログイン→Web APIのページに行きます。

Web API一覧から自動翻訳リクエスト-一覧をクリック

汎用NT 【 英語 - 日本語 】のAPIのインフォメーションアイコンをクリックします。

自動翻訳 WebAPIという画面が表示されるので、下の方にスクロールしていくとアクセス例がでてきます。OAuth→C#を選択し少し下にスクロールしusing System;・・以下をまるまるコピーして先ほど追加したTextra.csにまるまるペーストします。

貼り付けたソースコードの13行目、724行目、764行目を削除します。

13行目 不要な参照のため削除
using System.Windows.Forms;
724行目 設定は明示的にしたいため削除
private static TexTraPlugin.Properties.Settings MySettings = TexTraPlugin.Properties.Settings.Default;
764行目 ASP.Netでは使えない関数のため削除
if (show_message) MessageBox.Show("XMLの読込に失敗しました。\n\n" + e.Message);

724行目を削除した段階でおびただしい数のエラーが発生します、代わりの構造体を用意してあげるために22行目APIAccessor_jaの宣言の直下に以下のコードを追加します。

    public static class APIAccessor_ja
    {
        private static TexTraPlugin MySettings = new TexTraPlugin()
        {
          API_URL = "",
          API_KEY = "",
          API_SECRET = "",
          API_USER = ""
        };
        public class TexTraPlugin
        {
            public string API_自動翻訳 { get; set; }
            public string API_URL { get; set; }
            public string API_KEY { get; set; }
            public string API_SECRET { get; set; }
            public string API_USER { get; set; }
            public string proxy { get; set; }
            public string proxy_id { get; set; }
            public string proxy_password { get; set; }
            public int proxy_port { get; set; }
        }
      #region "API"

371行目辺りにinit_settings()という関数がありますが、この中に定義されているUrlが若干違うのでgeneral→generalNTに修正します。

"https://mt-auto-minhon-mlt.ucri.jgn-x.jp/api/mt/generalNT_zh-CN_ja/"
"https://mt-auto-minhon-mlt.ucri.jgn-x.jp/api/mt/generalNT_zh-TW_ja/"
"https://mt-auto-minhon-mlt.ucri.jgn-x.jp/api/mt/generalNT_ja_zh-CN/"
"https://mt-auto-minhon-mlt.ucri.jgn-x.jp/api/mt/generalNT_ja_zh-TW/"
"https://mt-auto-minhon-mlt.ucri.jgn-x.jp/api/mt/generalNT_ja_en/"
"https://mt-auto-minhon-mlt.ucri.jgn-x.jp/api/mt/generalNT_ja_ko/"
"https://mt-auto-minhon-mlt.ucri.jgn-x.jp/api/mt/generalNT_en_ja/"
"https://mt-auto-minhon-mlt.ucri.jgn-x.jp/api/mt/generalNT_ko_ja/"

実際のところは"https://mt-auto-minhon-mlt.ucri.jgn-x.jp/api/mt/generalNT_ja_en/"だけ変えればいいんですが、今後使う機会があるかもしれないので全部変えておきましょう。

24行目に追加したMySettingsに設定を入れようと思います。

API_URLは今回日本語→英語の想定なので"https://mt-auto-minhon-mlt.ucri.jgn-x.jp/api/mt/generalNT_ja_en/"を入力します。それ以外の項目はLesson.0で確認した値を入力します。

private static TexTraPlugin MySettings = new TexTraPlugin()
{
      API_URL = "https://mt-auto-minhon-mlt.ucri.jgn-x.jp/api/mt/generalNT_ja_en/",
      API_KEY = "ここは自分のAPI Keyを入れます",
      API_SECRET = "ここは自分のAPI Secretを入れます",
      API_USER = "ここは自分のUser Nameを入れます"
};

API Keyはユニークであり、第三者に知られると悪用されることもあるため絶対に自分以外の人間にはばれないようにしましょう。(というか知られたら120%悪用されると思ってください)

ここまできたら動くか確認しましょう、Pages/Counter.razorを以下のように修正します。

@page "/counter"
@using static TexTra.APIAccessor_ja
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
<RadzenTextBox @bind-Value=@orgValue></RadzenTextBox>
<RadzenTextBox @bind-Value=@newValue></RadzenTextBox>
@code {
    private int currentCount = 0;
    string orgValue = string.Empty;
    string newValue = string.Empty;
    private void IncrementCount()
    {
        currentCount++;
        var get = TexTra.APIAccessor_ja.get_auto_trans(orgValue, Language.ja, Language.en, out APIResponseBean aPIResponseBean);
        if (get.Any())
        {
            newValue = ((TexTra.APIAccessor_ja.AutoTransInfo)get[0].value).text_translated;
        }
    }
}

アプリケーションを起動しCounterに遷移します。

左側のテキストボックスに”野球”と入力し、Click meを押下後右側のテキストボックスにBaseballと表示されたらここまでは完了です。

Lesson.2 Line風導入

あとはLine風の送信画面を追加しようと思います。

RadzenにはHtmlEditorがあるので、これにCssを反映させてそれっぽい画面を作っていこうと思います。

Line風のCssは以下サイトを参考にさせてもらおうと思います。

jisuijisan.com

Pagesフォルダを右クリック後、追加→RadzenコンポーネントからChat.razorを追加し、Chat.razorに作成時書かれているコードを削除します。

取り敢えず上記のサイトように表示できることを確認したいため、HtmlとCssを拝借して以下のコードをChar.razorにコピペします。

Char.razor(クリックで展開) Char.razor(クリックで圧縮)
@page "/chat"
<RadzenHtmlEditor @bind-Value=@value></RadzenHtmlEditor>
@code {
    string value = string.Empty;
    string chatCss
    {
        get => "<style>" +
               "/*==============" +
               "LINE風フキダシ" +
               "===============*/" +
               "/*フレームとフォント*/" +
               ".kaiwa.line {" +
               "    width: 100%;" +
               "    max-width: 500px;" +
               "    margin: 0 auto;" +
               "    padding: 10px 0;" +
               "    background: #769ece;" +
               "    font-family: \"ヒラギノ角ゴ Pro W3\",\"Hiragino Kaku Gothic Pro\",\"Helvetica Neue\", \"Lucida Sans Unicode\", \"Arial\";" +
               "    font-size: 16px;" +
               "    color: #333;" +
               "    line-height: 1.4;" +
               "    overflow: hidden;" +
               "}" +
               "/*フキダシ共通*/" +
               ".kaiwa.line .fukidasi {" +
               "    position: relative;" +
               "    display: inline-block;" +
               "    max-width: 192px;" +
               "    margin: 8px 0 0;" +
               "    padding: 9px 14px;" +
               "    border-radius: 19px;" +
               "    overflow-wrap: break-word;" +
               "    clear: both;" +
               "    box-sizing: content-box;/*はてな用*/" +
               "}" +
               "/*フキダシ左*/" +
               ".kaiwa.line .fukidasi.left {" +
               "    float: left;" +
               "    margin-left: 62px;" +
               "    background: white;" +
               "}" +
               "/*グループのときのフキダシ*/" +
               ".kaiwa.line .name + .fukidasi.left {" +
               "    margin-top: 5px;" +
               "}" +
               "/*フキダシ右*/" +
               ".kaiwa.line .fukidasi.right {" +
               "    float: right;" +
               "    margin-right: 12px;" +
               "    background: #7adc40;" +
               "}" +
               "/*相手の名前*/" +
               ".kaiwa.line .name {" +
               "    clear: right;" +
               "    margin-left: 62px;" +
               "    color: white;" +
               "}" +
               "/*ユーザアイコン*/" +
               ".kaiwa.line .icon {" +
               "    position: absolute;" +
               "    width: 40px;" +
               "    height: 40px;" +
               "    left: -54px;" +
               "    top: -2px;" +
               "    border-radius: 20px;" +
               "}" +
               "/*グループのときのユーザアイコン*/" +
               ".kaiwa.line .name + .left .icon {" +
               "    top: -1.8em;" +
               "}" +
               "/*しっぽ共通*/" +
               ".kaiwa.line .fukidasi::after {" +
               "    position: absolute;" +
               "    content: \"\";" +
               "    width: 24px;" +
               "    height: 36px;" +
               "    top: -21px;" +
               "}" +
               "/*しっぽ左*/" +
               ".kaiwa.line .fukidasi.left:after {" +
               "    left: -10px;" +
               "    border-radius: 18px 0 6px 18px/18px 0 1px 18px;" +
               "    box-shadow: -3px -15px 0 -5px white inset;" +
               "}" +
               "/*しっぽ右*/" +
               ".kaiwa.line .fukidasi.right::after {" +
               "    right: -10px;" +
               "    border-radius: 0 18px 18px 6px/0 18px 18px 1px;" +
               "    box-shadow: inset 3px -15px 0 -5px #7adc40;" +
               "}" +
               "/*フキダシが続いてしっぽがないとき*/" +
               ".kaiwa.line .left + .left::after," +
               ".kaiwa.line .right + .right::after {" +
               "    content: none;" +
               "}"+
               "</style>" ;
    }
    protected override void OnParametersSet()
    {
        value = chatCss;
        value += "<div class=\"kaiwa line\">"+
        "    <div class=\"name\">"+
        "        おなまえ"+
        "    </div>"+
        "    <div class=\"fukidasi left\">"+
        "        <img class=\"icon\" src=\"img/icon.png\" alt=\"\">LINE風です。"+
        "    </div>"+
        "    <div class=\"fukidasi left\">"+
        "        続けて喋るとフキダシのしっぽはつきません。"+
        "    </div>"+
        "    <div class=\"fukidasi right\">"+
        "        HTMLとCSSでできます。"+
        "    </div>"+
        "    <br><!-- 次のフキダシにしっぽをつけたいときはbrを挿入 -->"+
        "    <div class=\"fukidasi right\">"+
        "        続けても、しっぽをつけたいときは間にbrを入れます。"+
        "    </div>"+
        "    <div class=\"fukidasi left\">"+
        "        <img class=\"icon\" src=\"img/icon.png\" alt=\"\">グループじゃないとき。" +
        "    </div>" +
        "</div>";
    }
}

Shared/NavMenu.razorのNavLinkに以下のコードを追加します。

  <div class="nav-item px-3">
          <NavLink class="nav-link" href="chat">
              <span class="oi oi-chat" aria-hidden="true"></span>Chat
            </NavLink>
  </div>

ここまできたら動くか確認しましょう、アプリを実行し左のタブからChatに移動し、以下画面が表示されたらここまでは完了です。

Lesson.3 Line風画面Component化

上記までである程度できてきましたが、もう少しそれっぽい画面にしたいと思います。

プロジェクトにComponentsフォルダを作成し、そのフォルダにLineChat.razorを作成します。

作成したLineChat.razorに以下のコードを貼り付けます。

<RadzenHtmlEditor @bind-Value=@Value class="w-100" style="font-family:Source Han Code JP;height:100%;" UploadUrl="upload/image" Disabled=@false>
    <RadzenHtmlEditorSeparator />
</RadzenHtmlEditor>
@code {
    string _Value = string.Empty;
    [Parameter]
    public string Value
    {
        get => _Value;
        set
        {
            if (_Value == value) return;
            _Value = value;
            ValueChanged.InvokeAsync(value);
        }
    }
    [Parameter]
    public EventCallback<string> ValueChanged { get; set; }
}

Pages/Chat.razorを以下のように修正します。

@page "/chat"
@using Radzen
<RadzenSplitter Orientation="Orientation.Vertical" Style="height:850px;">
    <RadzenSplitterPane Size="90%">
        <div class="h-100" style="overflow-y: scroll;">
            <Honya_kun.Components.LineChat @bind-Value=@value></Honya_kun.Components.LineChat>
        </div>
    </RadzenSplitterPane>
    <RadzenSplitterPane Size="10%">
        <RadzenSplitter Orientation="Orientation.Horizontal">
            <RadzenSplitterPane Size="70%">
                <RadzenTextArea class="w-100 h-100" @bind-Value=@sendStr></RadzenTextArea>
            </RadzenSplitterPane>
            <RadzenSplitterPane Size="30%">
                <RadzenButton class="w-100 h-100" Text="送信" Click=@SendClick></RadzenButton>
            </RadzenSplitterPane>
        </RadzenSplitter>
    </RadzenSplitterPane>
</RadzenSplitter>
@code {
    string value = string.Empty;
    string sendStr = string.Empty;
  string chatCss・・・(省略)
    protected override void OnParametersSet()
    {
        value = chatCss;
        value += "<div class=\"kaiwa line\">" +
        "    <div class=\"name\">" +
        "        ほんやくん" +
        "    </div>" +
        "    <div class=\"fukidasi left\">" +
        "        <img class=\"icon\" src=\"img/icon.png\" alt=\"\">LINE風です。" +
        "    </div>" +
        "    <div class=\"fukidasi left\">" +
        "        続けて喋るとフキダシのしっぽはつきません。" +
        "    </div>" +
        "    <div class=\"fukidasi right\">" +
        "        HTMLとCSSでできます。" +
        "    </div>" +
        "    <br><!-- 次のフキダシにしっぽをつけたいときはbrを挿入 -->" +
        "    <div class=\"fukidasi right\">" +
        "        続けても、しっぽをつけたいときは間にbrを入れます。" +
        "    </div>" +
        "    <div class=\"fukidasi left\">" +
        "        <img class=\"icon\" src=\"img/icon.png\" alt=\"\">グループじゃないとき。" +
        "    </div>" +
        "</div>";
    }
    async Task SendClick()
    {
        await Task.Delay(100);
    }
}

chatCss { get => を以下のように修正します。

 get => "<style>" +
               "/*==============" +
               "LINE風フキダシ" +
               "===============*/" +
               "/*フレームとフォント*/" +
               ".kaiwa.line {" +
               "    width: 100%;" +
               "    height:100%;" +
               //"    max-width: 500px;" +
               "    margin: 0 auto;" +
               "    padding: 10px 0;" +
               "    background: #769ece;" +
Console.WriteLine("Hello, World!");

ここまできたら実行してみて以下画面が表示されたらここまでは完成です。

Lesson.4 送信ボタン実装

任意のテキストを送り変換する送信ボタンを実装しようと思います。

まず、通信している間のBusyフラグを追加します。

@using static TexTra.APIAccessor_ja
<RadzenSplitter Orientation="Orientation.Vertical" Style="height:850px;">
    <RadzenSplitterPane Size="90%">
        <div class="h-100" style="overflow-y: scroll;">
            <Honya_kun.Components.LineChat @bind-Value=@value></Honya_kun.Components.LineChat>
        </div>
    </RadzenSplitterPane>
    <RadzenSplitterPane Size="10%">
        <RadzenSplitter Orientation="Orientation.Horizontal">
            <RadzenSplitterPane Size="70%">
                <RadzenTextArea class="w-100 h-100" @bind-Value=@sendStr></RadzenTextArea>
            </RadzenSplitterPane>
            <RadzenSplitterPane Size="30%">
              <RadzenButton class="w-100 h-100" Text="送信" Click=@SendClick  IsBusy=@IsBusy BusyText="通信中"></RadzenButton>
            </RadzenSplitterPane>
        </RadzenSplitter>
    </RadzenSplitterPane>
</RadzenSplitter>
@code {
    string value = string.Empty;
    string sendStr = string.Empty;
  bool IsBusy = false;

OnParameterSet()の中を以下のように修正します。

    protected override void OnParametersSet()
    {
        value = chatCss;
        value +=
        "<div class=\"kaiwa line\">" +
        "    <div class=\"name\">" +
        "        ほんやくん" +
        "    </div>" +
        "    <div class=\"fukidasi left\">" +
        "        <img class=\"icon\" src=\"img/icon.png\" alt=\"\">こんにちは!" +
        "    </div>" +
        "    <div class=\"fukidasi left\">" +
        "        日本語→英語の翻訳ができるほんや君です!" +
        "    </div>" +
        " </div>";
  }

SendClickの中を以下のように修正します。

    async Task SendClick()
    {
        if (string.IsNullOrEmpty(sendStr))
        {
            // Nothing
        }
        else
      {
            IsBusy = true;
            List<APIResponseBean> get = new List<APIResponseBean>();
            value = value.TrimEnd(new char[6] { '<', '/', 'd', 'i', 'v', '>' });
            value += "<div class=\"fukidasi right\">" + sendStr + "</div>";
            await Task.WhenAll(new Task[] {
                Task.Factory.StartNew(() => { get = TexTra.APIAccessor_ja.get_auto_trans(sendStr, Language.ja, Language.en, out APIResponseBean aPIResponseBean); }) });

            if (get.Any())
            {
                var newValue = ((TexTra.APIAccessor_ja.AutoTransInfo)get[0].value).text_translated;
              value += "<div class=\"fukidasi left\">><img class=\"icon\" src=\"img/icon.png\" alt=\"\">" + newValue + "</div>";
            }
            else
            {
                value += "<div class=\"fukidasi left\">" + "翻訳できませんでした・・・" + "</div>";
            }
            value += " </div>";
           IsBusy = false;
        }
  }

chatCssのフキダシ共通を以下のように修正します

"/*フキダシ共通*/" +
".kaiwa.line .fukidasi {" +
"    position: relative;" +
"    display: inline-block;" +
"    max-width: 500px;" +
"    margin: 8px 0 0;" +
"    padding: 9px 14px;" +
"    border-radius: 19px;" +
"    overflow-wrap: break-word;" +
"    clear: both;" +
"    box-sizing: content-box;/*はてな用*/" +
"}" +

ほんやくんのアイコンを追加したいのでwwwrootの直下にiconフォルダを作り、おなじみのイラストやさんの力をかりicon..pngをiconフォルダに入れます。

www.irasutoya.com

ここまできたら実行して任意の文字を入力し、送信ボタンを押すとほんやくんが返事してくれると思います!

Lesson.5 あとがき

おもったよりも長い記事になってしまいました、何かの役に立てれば幸いです。

以上!

github.com

Blazor+RadzenでUserControl

初めまして、アキちゃんです。

備忘録をかねてブログを書くことにしました。

今回はBlazor+RadzenでUserControlについて書きます。

それでは早速・・・Blazor+RadzenでUserControl

Lesson.1 事の発端

Blazor+RadzenでUserControl作ろうとしたらデータバインドでだいぶ苦戦しました。

なので書き残しておこうと思います。

Lesson.2 コンポーネントの作成

Blazorでは、UserControlという名称は出てきません。

代わりにコンポーネントというそうです、なので新しい項目の追加からRazorコンポーネントを追加します。

今回は外部から値を入力・出力だけするシンプルなものを作ろうと思うので追加したコンポーネントに以下の追加・修正をします。

<RadzenTextBox Value=@Note></RadzenTextBox>
@code {
    string _note;
    [Parameter]
    public string Note
    {
        get => _note;
        set
        {
            if (_note != value)
            {
                _note = value;
                NoteChanged.InvokeAsync(value);
            }
            else
            {
                return;
            }
        }
    }
    [Parameter]
    public EventCallback<string> NoteChanged { get; set; }
}

みそなのは属性として[Parameter]を追加しているところと、値が変わったときに宣言したコールバックを読んでいることです。

コールバックを呼ぶことで変更通知が発行されコンポーネントの値が変更されます。

Lesson.3 コンポーネントを使う

作成したコンポーネントはほかのコンポーネントと同様に扱えます。

適当なページ(今回はIndex.razor)を以下のように修正します。

@page "/"
<SampleComponent @bind-Note=@value></SampleComponent>
<RadzenTextBox @bind-Value=@input></RadzenTextBox>
<RadzenButton Text="転送" Click=OnClick></RadzenButton>
@code {
    string value = string.Empty;
    string input = string.Empty;
    async Task OnClick()
    {
        value = input;
    }
}

コンポーネントの中で宣言した[Parameter]属性を持つ変数は上記のように"@bind-[コンポーネント変数名]=@ページ内変数"でバインドし使うことができます。

ここまできたら実行してまず右側のテキストボックスに文字を入力し、転送ボタンを押下します。

すると左側のテキストボックスに入力した文字が入力されるとおもいます。

Lesson.4 あとがき

以上!

HttpRequestMessageでGet・Post使用

初めまして、アキちゃんです。

備忘録をかねてブログを書くことにしました。

今回はHttpRequestMessageでGet・Post使用について書きます。

それでは早速・・・HttpRequestMessageでGet・Post使用

Lesson.1 事の発端

僕はC#狂なので基本的にすべてをC#で実装したいです。

なのでBlazorでHttpRequestMessageを使用してGet・Postしようと思います。

Lesson.2 Program.cs

C#でGet・Postを行うにおいていろんな方法がありますがHttpClientについてあまりこみいった実装をしたくないといった思いがあります。

qiita.com

なのでかわりにIHttpClientFactoryを使用したいので以下のコードを追記します。

builder.Services.AddHttpClient();

Lesson.3 HttpRequestMessage - Get

Getはまあこれといった必要なことはありません。

ただ0000は各Post番号に左右されます。

@inject IHttpClientFactory ClientFactory

var request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:0000" + controllerPath);
var client = ClientFactory.CreateClient();
var response = await client.SendAsync(request);
var getVal = await response.Content.ReadAsStringAsync();

Lesson.4 HttpRequestMessage - Post

Postも基本は送り付けるだけなんですが、ファイル等をアップロードするときだけ注意が必要です。

@inject IHttpClientFactory ClientFactory

var request = new HttpRequestMessage(HttpMethod.Post, "https://localhost:0000" + controllerPath);
var client = ClientFactory.CreateClient();
{
    var content = new MultipartFormDataContent();
    content .Add(new StringContent(JsonSerializer.Serialize<JsonSample>(new JsonSample() { Number = docNumber, Content = value }), Encoding.UTF8, "application/json"), "file", "test.json");
    request.Content = content ;
    using (var response = await client.SendAsync(request)) ;
}

上で使っているJsonのクラス(JsonSample)は以下です。

public class JsonSample
{
    [JsonPropertyName("number")]
    public int Number { get; set; }
    [JsonPropertyName("content")]
    public string? Content { get; set; }
}

一つ気を付けなければいけないのはcontent.Add()をする際第二引数は"file"でないと上手く動きません。

また、送るものに応じてcontent .Add(new StringContent())のAddするContentが変わります。

Lesson.5 あとがき

以上!!

ControllerでGet・Post実装

初めまして、アキちゃんです。

備忘録をかねてブログを書くことにしました。

今回はにControllerでGet・Post実装ついて書きます。

それでは早速・・・ControllerでGet・Post実装

Lesson.1 事の発端

僕はC#狂なので基本的にすべてをC#で実装したいです。

なのでBlazorでApiの実装を試してみようと思います。

Lesson.2 Program.cs

BlazorではControllerでApiを実装する際、Program.csに追加の設定をしなければなりません。

app.UseEndpoints(userEndPpoints=>
{
   userEndPpoints.MapControllers();
});

Lesson.3 Controller - Get

Getの実装ではあんまり考えることはありません。

ざっくりこうです。

[Route("api/get")]
public class GetController : Controller
{
    private readonly IWebHostEnvironment environment;
    public GetController(IWebHostEnvironment environment)
    {
        this.environment = environment;
    }
    [HttpGet("string")]
    public IActionResult GetString()
    {
        try
        {
            return Content("Hello World");
        }
        catch (Exception ex)
        {
            return StatusCode(500, ex.Message);
        }
    }
    public class JsonSample
    {
        [JsonPropertyName("number")]
        public int Number { get; set; }
        [JsonPropertyName("content")]
      public string? Content { get; set; }
    }
    [HttpGet("json/{content}")]
    public IActionResult GetJson(string content)
    {
        try
        {
            return Content(JsonSerializer.Serialize(new JsonSample { Number = 1, Content = content }), "application/json", System.Text.Encoding.UTF8);
        }
        catch (Exception ex)
        {
            return StatusCode(500, ex.Message);
        }
    }
    [HttpGet("xlsx/{filePath}")]
    public IActionResult GetXlsxFile(string filePath)
    {
        try
        {
            var rslt = System.IO.File.ReadAllBytes(filePath);
            return File(rslt, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", Path.GetFileName(filePath));
        }
        catch (Exception ex)
        {
            return StatusCode(500, ex.Message);
        }
    }
}

Lesson.4 Controller - Post

Postで行うことでいちばんミソとなるのはFileのアップロードだと思います。

あと、稀に送られてきたJsonを書き込まずStreamでそのまま使いたい時だと思います。

ざっくりこうです。

[Route("api/post")]
public class PostController : Controller
{
    private readonly IWebHostEnvironment environment;
    public PostController(IWebHostEnvironment environment)
    {
        this.environment = environment;
    }
    [HttpPost("file")]
    public IActionResult SetFile(IFormFile file)
    {
        try
        {
            using (var stream = new FileStream(Path.Combine(environment.WebRootPath, file.Name), FileMode.Create))
            {
                file.CopyTo(stream);
            }
            return Ok();
        }
        catch (Exception ex)
        {
            return StatusCode(500, ex.Message);
        }
    }
    public class JsonSample
    {
        [JsonPropertyName("number")]
        public int Number { get; set; }
        [JsonPropertyName("content")]
        public string? Content { get; set; }
    }
    [HttpGet("json")]
    public async Task<IActionResult> SetJson(IFormFile file)
    {
        try
        {
            using (var memoryStream = new MemoryStream())
            {
                await file.CopyToAsync(memoryStream);
              byte[] buffer = memoryStream.ToArray();
                // getのなかに取得したJsonが入る
                // 新規で書き込んで作るもよし・既存のファイルをいじるもよし
                var get = JsonSerializer.Deserialize<JsonSample>(Encoding.ASCII.GetString(buffer));
            }
            return Ok();
        }
        catch (Exception ex)
        {
            return StatusCode(500, ex.Message);
        }
    }
}

Lesson.5 あとがき

*20221219追記

using System.Text.Json.Serialization;でJsonを操作する際は元となるクラスの変数をプロパティにしないとSerialize、Deserializeがうまく動かずJsonが生成されません

以上!

Blazor と 32KB以上の通信

初めまして、アキちゃんです。

備忘録をかねてブログを書くことにしました。

今回はBlazor と 32KB以上の通信について書きます。

それでは早速・・・Blazor と 32KB以上の通信

Lesson.1 事の発端

BlazorでRadzenのHtmlEditorを使用したWebアプリを運用していたとき、ある日から入力するためにコンポーネントをクリックすると以下メッセージ

"Attempting to reconnect to the server: 1 of 8"

と共に画面に再描画がかかるようになりました。

blazor.radzen.com

Lesson.2 原因と理由

SignalRの受信最大サイズがデフォルトでは32KBになっておりHtmlEditor内への入力データ量が32KBを超えてしまったためでした。

Lesson.3 対策

Program.csに以下のコードを追記しました。

// 32K超える通信のためのHubOption
builder.Services.Configure<HubOptions>(options =>
{
    options.MaximumReceiveMessageSize = null;
});

上記の記述をすることでSignalRの最大受信サイズを無制限にすることができるのでクリックごとの更新が起こらなくなりました。

Lesson.4 あとがき

以上!