ハロの外部記憶インターフェイス

そろそろ覚える努力が必要かも… と思った時もあったが、もうメモる必要もない時代かも…

ディスクをゴミデータでいっぱいにする。

ディスクを捨てる前にデータ復帰できなくするため、ゴミデータでいっぱいにする。

Windowsの場合、PowerShellを利用し、コマンド実行する。

$out = [System.IO.File]::Create("D:\dummy.dat")
$bytes = New-Object Byte[] 1MB
(New-Object Random).NextBytes($bytes)
for ($i = 0; $i -lt 100000; $i++) { $out.Write($bytes, 0, $bytes.Length) }
$out.Close()
  • 100GB分のゴミデータを作成するため、少々時間がかかる。

※ただ容量だけ消費したい場合は

fsutil file createnew D:\dummy.dat 107374182400
  • 100GB(107,374,182,400 バイト)

Mac/Linuxの場合、

dd if=/dev/urandom of=~/Desktop/dummy.dat bs=1M count=100000

※ ただ容量だけ消費したい場合は

mkfile 100g ~/Desktop/dummy.dat

Blazor Appでローカルでログイン情報を保存し認証状態を管理する。

プロジェクト作成

dotnet new blazor -n BlazorApp1

Nuget追加

cd BlazorApp1
dotnet add package Microsoft.AspNetCore.Components.Authorization

CustomAuthStateProvider追加

AuthenticationStateProviderを継承し、ログイン情報を取得・保存処理を行う。 ユーザ情報のUserSessionはjsonにし、保存する形

using System.Security.Claims;
using System.Text.Json;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.JSInterop;

namespace BlazorApp1
{
  public record UserSession(string UserId, string UserName, string Role);

  public class CustomAuthStateProvider : AuthenticationStateProvider
  {
    private ClaimsPrincipal _anonymous = new(new ClaimsIdentity());
    private ClaimsPrincipal _currentUser;
    private IJSRuntime _jsRuntime;
    private const string StrorageKey = "authToken";
    public CustomAuthStateProvider(IJSRuntime jSRuntime)
    {
      _jsRuntime = jSRuntime;
      _currentUser = _anonymous;
     }
       
     public override async Task<AuthenticationState> GetAuthenticationStateAsync()
     {
       try
       {
         var json = await _jsRuntime.InvokeAsync<string>("localStorage.getItem", StrorageKey);
         if (!string.IsNullOrEmpty(json))
         {
           var session = JsonSerializer.Deserialize<UserSession>(json);
           if (session != null)
           {
             var identity = new ClaimsIdentity(
             [
               new Claim(ClaimTypes.Name, session.UserId),
               new Claim(ClaimTypes.Role, session.Role)
             ], "Custom Authentication");
             _currentUser = new ClaimsPrincipal(identity);
          }
        }
      }
      catch (Exception ex)
      {
        Console.WriteLine($"Error in GetAuthenticationStateAsync: {ex.Message}");
        _currentUser = _anonymous;
      }

      return await Task.FromResult(new AuthenticationState(_currentUser));
    }

    public async Task MakeAuthenticateUser(string userIdentifier)
    {

       var session = new UserSession(userIdentifier, userIdentifier, "User");
       var json = JsonSerializer.Serialize(session);
       var identity = new ClaimsIdentity(
                [
                new Claim(ClaimTypes.Name, session.UserId),
                new Claim(ClaimTypes.Role, session.Role)
            ], "Custom Authentication");

       _currentUser = new ClaimsPrincipal(identity);
       await _jsRuntime.InvokeVoidAsync("localStorage.setItem", StrorageKey, json);
       NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(_currentUser)));
   }

   public async Task MakeUserAsLoggedOut()
   {
      _currentUser = _anonymous;
       await _jsRuntime.InvokeVoidAsync("localStorage.removeItem", StrorageKey);
        NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(_currentUser)));
    }
  }
}
  • UserSession : ログインユーザー情報のオブジェクト
  • GetAuthenticationStateAsync : 画面ロード時に呼び出され現在ユーザー情報をセットする。
  • MakeAuthenticateUser : ログイン処理
  • MakeUserAsLoggedOut : ログアウト処理

Programs.cs修正

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Components.Authorization;

var builder = WebApplication.CreateBuilder(args);
// ログインが必要な画面の場合、signin画面を飛ばす処理
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
    .AddCookie(options =>
    {
        options.LoginPath = "/signin";
    });

// ログイン状態管理用のAuthenticationStateProviderをDIする。
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
builder.Services.AddScoped<CustomAuthStateProvider>(sp => (CustomAuthStateProvider)sp.GetRequiredService<AuthenticationStateProvider>());

var app = builder.Build();
// 上記、builder.Services.AddAuthenticationと対で必要
app.UseAuthentication();
app.UseAuthorization();

_Imports.razorにusing追加

@using Microsoft.AspNetCore.Components.Authorization

App.razor修正

rendermodeを統一しておかないと、認証情報取得時不一致になる。

<head>
    <HeadOutlet @rendermode="InteractiveServer" />
</head>
<body>
    <Routes @rendermode="InteractiveServer" />
</body>

Routes.razor修正

認証状態の場合のみ表示するようにする。

<Router AppAssembly="typeof(Program).Assembly">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="routeData" DefaultLayout="@typeof(Layout.MainLayout)">
            <NotAuthorized>
                <p role="alert">このページを表示する権限がありません。</p>
            </NotAuthorized>
        </AuthorizeRouteView>

        <FocusOnNavigate RouteData="routeData" Selector="h1" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(Layout.MainLayout)">
            <p role="alert">ページが見つかりません。</p>
        </LayoutView>
    </NotFound>
</Router>

各Pageに認証設定する。

これにより、Programs.csの「options.LoginPath = "/signin";」が動作し、初期ページへ移動する。

@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@attribute [Authorize(Roles = "Admin,User")]
  • Rolesは必要に応じて設定する。

SignIn.razor追加

@page "/signin"
@rendermode InteractiveServer
@inject AuthenticationStateProvider AuthenticationStateProvider

<input @bind="userIdentifier" />
<button @onclick="OnSignIn">Sign in</button>
<button @onclick="OnSignOut">Sign out</button>

<AuthorizeView>
    <Authorized>
        <p>Hello, @context.User.Identity?.Name!</p>
    </Authorized>
    <NotAuthorized>
        <p>You're not authorized.</p>
    </NotAuthorized>
</AuthorizeView>

@code {
    public string userIdentifier = string.Empty;

    private async Task OnSignIn() => await ((CustomAuthStateProvider)AuthenticationStateProvider).MakeAuthenticateUser(userIdentifier);

    private async Task OnSignOut() => await ((CustomAuthStateProvider)AuthenticationStateProvider).MakeUserAsLoggedOut();
}

とりあえずここまで

GitHub - haronoid/BlazorApp1 · GitHub

今後の課題

  • ブラウザを閉じた時にログアウトはどうする?
  • Tokenで認証に有効期間設定

Api側のJson Serializeを標準からNewtonsoft.Jsonへ変更

標準JsonSerializeではNull項目などすべての項目をJsonにして返しているため、通信量に無駄が多くなる。

なのでNewtonsoft.Jsonを適用し通信量を節約する

Microsoft.AspNetCore.Mvc.NewtonsoftJsonを追加

Nugetから「Microsoft.AspNetCore.Mvc.NewtonsoftJson」を追加
ただし、.NETバージョンによって対応バージョンにちがいが存在。

.NET8では8.0.21をインストールする。
.NET9以降では最新で問題ないと思われる。

Program.cs

AddNewtonsoftJsonを追加し、シリアライズ設定を追加する。

builder.Services.AddControllers()
    .AddNewtonsoftJson(option =>
    {
        option.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
        option.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        option.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
    });

MS標準の場合、

{"menuId":1001,"menuName":"メニュー1","menuGroupCd":1,"displayOrder":1,"createdAt":"2025-10-06T00:00:00","createdBy":"SYSTEM","updatedAt":null,"updatedBy":null,"deletedAt":null,"deleteddBy":null,"versionNo":0}

NewtonsoftJsonの場合、

{"$id":"1","menuId":1001,"menuName":"メニュー1","menuGroupCd":1,"displayOrder":1,"createdAt":"2025-10-06T00:00:00","createdBy":"SYSTEM","versionNo":0}

SwaggerのVersionをUpしたらエラーになった件

Asp.net core Apiプロジェクトを作成してから、Nugetを見るとアップデートがあるとの知らせ!

もちろんアップだ

エラーになった

### Unable to render this definition

The provided definition does not specify a valid version field.

Please indicate a valid Swagger or OpenAPI version field. Supported version fields are `swagger: "2.0"` and those that match `openapi: 3.x.y` (for example, `openapi: 3.1.0`).

対策

色々調べると追加で色々設定してあげないとだめぽい。

とりあえず、成功したパターンの結果を残す。

Program.cs

// 省略
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new OpenApiInfo
    {
        Title = "MyPj.Api",
        Version = "v1"
    });
});
// 省略
if (app.Environment.IsDevelopment())
{
    app.UseSwagger(c =>
    {
        c.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi2_0;
    });
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "MyPj.Api V1");
}
// 省略

Versionによって色々違いがあるみたいだけど、とりあえず、9.0.6Versionではこれで解決した。

.net core でShift-JISでファイル出力

Asp.net core 開発でCsvhelperを利用して、Shift-JISで出力を試みた結果、Encodingが見つからないとエラーになる。

どうやら、.net coreからはUTF-8以外は拒否されるらしい

ネットを調べると以下のおまじないを実行してからだとかのうと出ていた。

System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

早速CsvHelperで使ってみる

System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

var config = new CsvConfiguration(System.Globalization.CultureInfo.CurrentCulture)
{
    HasHeaderRecord = true,
    Delimiter = ",",
    Encoding = Encoding.GetEncoding("Shift_JIS")
};

using TextWriter tw = new StreamWriter(filePath);
using var cw = new CsvWriter(tw, _config);
 foreach (var record in records)
 {
     cw.WriteRecord(record);
     cw.NextRecord();
 }

問題なく出力された

entityframework include実装

entityframeworkのinclude実装

Dbモデル

public class Author
{
    [Key]
    public int AuthorId { get; set; }
    public string Name { get; set; }
    public List<Book> Books { get; set; }
}
public class Book
{
    [Key]
    public int BookId { get; set; }
    public string Title { get; set; }
    public int AuthorId { get; set; }
    public Author Author { get; set; }
}

DbContext

public class MyDbContext : DbContext
{
    public DbSet<Book> Books { get; set; } = null!;
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Configure the model here if needed
        base.OnModelCreating(modelBuilder);
                modelBuilder.Entity<Book>()
                    .HasOne(r => r.Author)
                    .WithMany(p => p.Books)
                    .HasForeignKey(r => r.AuthorId );
    }
}

Contoller

var data= _context.Books.Include(r => r.Author).ToListAsync();

aspnet core apiでincudeするとcan't parse JSON.  Raw result:となる

aspnet core apiでincudeするとcan't parse JSON.  Raw result:となる

参照クラスの情報を取得するためincludeを実装したところ、apiのレスポンスが下記のエラーが発生

can't parse JSON.  Raw result:
An unexpected fault happened. Try again later.

調べたところJsonのSerialize処理でループ(親→子→親→子…)が発生してエラーになっているようだ 解決としてはループサイクルを無視するように設定したら動くようになった。

// Program.cs
builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles;
    });