Tech for good

[Blazor, C#] Blazor WebAssembly Blog Series 5 본문

IT/Computer Science

[Blazor, C#] Blazor WebAssembly Blog Series 5

Diana Kang 2022. 2. 24. 10:40

* 해당 영상을 공부하며 정리한 자료입니다.

https://www.youtube.com/playlist?list=PLF1jhYUTnHo5XFX9lgS0YsNSDJHpYnRxK 

 

Blazor WebAssembly Blog Series

 

www.youtube.com

 

✔ 기본세팅

: Blazor WebAssembly, .Net6

 

 

✔ 학습목표
: Create 페이지를 생성하고 동작을 구현한다.

 


5. Forms, Validation & HTTP POST with Blazor WebAssembly

5.1. Create new component in Pages folder and Change some code in NavMenu.razor file

// BlazorBlog.Client/Pages/Create.razor

@page "/create"

<h3>Create a New Blog Post</h3>

// Add new controls or built-in components of blazor web assembly
<EditForm Model="@newBlogPost" OnValidSubmit="CreateNewBlogPost">
    <button type="submit" class="btn btn-primary">Create</button>
</EditForm>

@code {
    BlazorBlog.Shared.BlogPost newBlogPost = new BlazorBlog.Shared.BlogPost();

    void CreateNewBlogPost()
    {
        Console.WriteLine("Create that blog post!");
    }
}
// BlazorBlog.Client/Pages/Create.razor

@page "/create"

<h3>Create a New Blog Post</h3>

// Add new controls or built-in components of blazor web assembly
<EditForm Model="@newBlogPost" OnValidSubmit="CreateNewBlogPost">
    <button type="submit" class="btn btn-primary">Create</button>
</EditForm>

@code {
    BlazorBlog.Shared.BlogPost newBlogPost = new BlazorBlog.Shared.BlogPost();

    void CreateNewBlogPost()
    {
        Console.WriteLine("Create that blog post!");
    }
}

 

5.2. Add some components to the forms

// BlazorBlog.Client/Pages/Create.razor

@page "/create"

<h3>Create a New Blog Post</h3>

<EditForm Model="@newBlogPost" OnValidSubmit="CreateNewBlogPost">
    <div class="form-group">
        <label for="title">Title</label>
        <InputText id="title" @bind-Value="newBlogPost.Title" class="form-control" />
    </div>
    <button type="submit" class="btn btn-primary">Create</button>
</EditForm>

@code {
    BlazorBlog.Shared.BlogPost newBlogPost = new BlazorBlog.Shared.BlogPost();

    void CreateNewBlogPost()
    {
        Console.WriteLine("Create that blog post!");
    }
}

 

Title에 텍스트 입력 후, Create 버튼 누르면 Validate되어 입력창 테두리가 초록색으로 바뀜

 

// BlazorBlog.Client/Pages/Create.razor

@page "/create"

<h3>Create a New Blog Post</h3>

<EditForm Model="@newBlogPost" OnValidSubmit="CreateNewBlogPost">
    <div class="form-group">
        <label for="title">Title</label>
        <InputText id="title" @bind-Value="newBlogPost.Title" class="form-control" />
    </div>
    <div class="form-group">
        <label for="url">Url</label>
        <InputText id="url" @bind-Value="newBlogPost.Url" class="form-control" />
    </div>
    <div class="form-group">
        <label for="description">Description</label>
        <InputText id="description" @bind-Value="newBlogPost.Description" class="form-control" />
    </div>
    <div class="form-group">
        <label for="content">Content</label>
        <InputTextArea id="content" @bind-Value="newBlogPost.Content" class="form-control" />
    </div>
    <div class="form-group">
        <label for="date">Date</label>
        <InputDate id="date" @bind-Value="newBlogPost.DateCreated" class="form-control" />
    </div>
    <div class="form-check">
        <InputCheckbox id="isPublished" @bind-Value="newBlogPost.IsPublished" class="form-check-input" />
        <label for="isPublished">Publish</label>
    </div>
    <button type="submit" class="btn btn-primary">Create</button>
</EditForm>

@code {
    BlazorBlog.Shared.BlogPost newBlogPost = new BlazorBlog.Shared.BlogPost();

    void CreateNewBlogPost()
    {
        Console.WriteLine("Create that blog post!");
    }
}

 

마찬가지로 Url, Description, Content, Date, Publish 창도 생성한다.

 

5.3. Validation (Forms에 입력한 데이터 값을 db에 등록하는 것)

// BlazorBlog.Shared/BlogPost.cs

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BlazorBlog.Shared
{
    public class BlogPost
    {
        public int Id { get; set; }
	[Required, StringLength(20)]
        public string Url { get; set; }
        [Required] 
	// [Required] Attribute 추가 => 위의 텍스트 입력 후 빨간 줄 떴을 때 => ctrl + . 눌러서 using 구문 추가
        public string Title { get; set; }
        public string Content { get; set; }
        public string Description { get; set; }
        public string Author { get; set; }
        public DateTime DateCreated { get; set; } = DateTime.Now;
        public bool IsPublished { get; set; } = true;
        public bool IsDeleted { get; set; } = false;

    }
}
// BlazorBlog.Client/Pages/Create.razor

@page "/create"

<h3>Create a New Blog Post</h3>

<EditForm Model="@newBlogPost" OnValidSubmit="CreateNewBlogPost">
    <DataAnnotationsValidator />
    <div class="form-group">
        <label for="title">Title</label>
        <InputText id="title" @bind-Value="newBlogPost.Title" class="form-control" />
    </div>
    <div class="form-group">
        <label for="url">Url</label>
        <InputText id="url" @bind-Value="newBlogPost.Url" class="form-control" />
    </div>
    <div class="form-group">
        <label for="description">Description</label>
        <InputText id="description" @bind-Value="newBlogPost.Description" class="form-control" />
    </div>
    <div class="form-group">
        <label for="content">Content</label>
        <InputTextArea id="content" @bind-Value="newBlogPost.Content" class="form-control" />
    </div>
    <div class="form-group">
        <label for="date">Date</label>
        <InputDate id="date" @bind-Value="newBlogPost.DateCreated" class="form-control" />
    </div>
    <div class="form-check">
        <InputCheckbox id="isPublished" @bind-Value="newBlogPost.IsPublished" class="form-check-input" />
        <label for="isPublished">Publish</label>
    </div>
    <button type="submit" class="btn btn-primary">Create</button>
		<ValidationSummary />
</EditForm>

@code {
    BlazorBlog.Shared.BlogPost newBlogPost = new BlazorBlog.Shared.BlogPost();

    void CreateNewBlogPost()
    {
        Console.WriteLine("Create that blog post!");
    }
}

 

빈칸으로 둔 상태에서 Create 버튼 누르면 입력창 테두리가 빨간색으로 표시되며 하단의 경고 문구를 확인할 수 있다.

 

 

// BlazorBlog.Shared/BlogPost.cs

namespace BlazorBlog.Shared
{
    public class BlogPost
    {
        public int Id { get; set; }
        [Required, StringLength(20, ErrorMessage = "Please use only 20 characters.")]
        public string Url { get; set; }
        [Required]
        public string Title { get; set; }
        public string Content { get; set; }
        public string Description { get; set; }
        public string Author { get; set; }
        public DateTime DateCreated { get; set; } = DateTime.Now;
        public bool IsPublished { get; set; } = true;
        public bool IsDeleted { get; set; } = false;

    }
}

 

Custom Message도 입력하여 보여줄 수 있다.

 

// BlazorBlog.Client/Pages/Create.razor

@page "/create"

<h3>Create a New Blog Post</h3>

<EditForm Model="@newBlogPost" OnValidSubmit="CreateNewBlogPost">
    <DataAnnotationsValidator />
    <div class="form-group">
        <label for="title">Title</label>
        <InputText id="title" @bind-Value="newBlogPost.Title" class="form-control" />
        <ValidationMessage For="@(() => newBlogPost.Title)" />
    </div>
    <div class="form-group">
        <label for="url">Url</label>
        <InputText id="url" @bind-Value="newBlogPost.Url" class="form-control" />
        <ValidationMessage For="@(() => newBlogPost.Url)" />
    </div>
    <div class="form-group">
        <label for="description">Description</label>
        <InputText id="description" @bind-Value="newBlogPost.Description" class="form-control" />
    </div>
    <div class="form-group">
        <label for="content">Content</label>
        <InputTextArea id="content" @bind-Value="newBlogPost.Content" class="form-control" />
    </div>
    <div class="form-group">
        <label for="date">Date</label>
        <InputDate id="date" @bind-Value="newBlogPost.DateCreated" class="form-control" />
    </div>
    <div class="form-check">
        <InputCheckbox id="isPublished" @bind-Value="newBlogPost.IsPublished" class="form-check-input" />
        <label for="isPublished">Publish</label>
    </div>
    <button type="submit" class="btn btn-primary">Create</button>
    <ValidationSummary />
</EditForm>

@code {
    BlazorBlog.Shared.BlogPost newBlogPost = new BlazorBlog.Shared.BlogPost();

    void CreateNewBlogPost()
    {
        Console.WriteLine("Create that blog post!");
    }
}

 

ValidationMessage를 추가하면 각각의 입력창 아래에 메세지들을 확인할 수 있다.

 

 

https://docs.microsoft.com/en-us/aspnet/core/blazor/forms-validation?view=aspnetcore-6.0 

 

ASP.NET Core Blazor forms and validation

Learn how to use forms and field validation scenarios in Blazor.

docs.microsoft.com

 

// BlazorBlog.Client/Services/IBlogService.cs

namespace BlazorBlog.Client.Services
{
    interface IBlogService
    {
        Task<List<BlazorBlog.Shared.BlogPost>> GetBlogPosts();
        Task<BlazorBlog.Shared.BlogPost> GetBlogPostByUrl(string url);
				// To return the blog posts
        Task<BlazorBlog.Shared.BlogPost> CreateNewBlogPost(BlazorBlog.Shared.BlogPost request);
    }
}
// BlazorBlog.Client/Services/BlogService.cs

namespace BlazorBlog.Client.Services
{
    public class BlogService : IBlogService
    {
        private readonly HttpClient _http;

        public BlogService(HttpClient http)
        {
            _http = http;
        }

        public async Task<BlogPost> CreateNewBlogPost(BlogPost request)
        {
            var result = await _http.PostAsJsonAsync("api/Blog", request);
            return await result.Content.ReadFromJsonAsync<BlogPost>();
        }

...

    }
}
// BlazorBlog.Client/Pages/Create.razor

@page "/create"
@inject BlazorBlog.Client.Services.IBlogService BlogService
@inject NavigationManager NavigationManager

...

@code {
    BlazorBlog.Shared.BlogPost newBlogPost = new BlazorBlog.Shared.BlogPost();

    async Task CreateNewBlogPost()
    {
        var result = await BlogService.CreateNewBlogPost(newBlogPost);
        NavigationManager.NavigateTo($"posts/{result.Url}");
    }
}

 

 

5.4. Create a newBlogPost controller

// BlazorBlog.Server/Controllers/BlogController.cs

namespace BlazorBlog.Server.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class BlogController : ControllerBase
    {
        private readonly BlazorBlog.Server.Data.DataContext _context;

        // Inject data context
        public BlogController(BlazorBlog.Server.Data.DataContext context)
        {
            _context = context;
        }

        [HttpGet]
        public ActionResult<List<BlazorBlog.Shared.BlogPost>> GiveMeAllTheBlogPosts()
        {
            return Ok(_context.BlogPosts);
        }

        [HttpGet("{url}")]
        public ActionResult<BlazorBlog.Shared.BlogPost> GiveMeThatSingleBlogPost(string url)
        {
            var post = _context.BlogPosts.FirstOrDefault(p => p.Url.ToLower().Equals(url.ToLower()));
            if (post is null)
            {
                return NotFound("This post does not exist.");
            }

            return Ok(post);
        }

        [HttpPost]
        public async Task<ActionResult<BlazorBlog.Shared.BlogPost>> CreateNewBlogPost(BlazorBlog.Shared.BlogPost request)
        {
            _context.Add(request);
            await _context.SaveChangesAsync();

            return request;
        }
    }
}

 

위의 과정을 끝내고 Create 버튼을 누르면, 데이터가 db에 잘 저장이 된다!

 

※ 주의사항!!
.Net6의 경우, HTTP 통신할 때 객체에 null 값이 있으면 자동 HTTP 400 응답 에러를 뱉는다.

 

// BlazorBlog.Shared/BlogPost.cs

namespace BlazorBlog.Shared
{
    public class BlogPost
    {
        public int Id { get; set; }
        [Required, StringLength(20, ErrorMessage = "Please use only 20 characters.")]
	// .NET6는 NULL 값 허용을 안해주기 때문에 아래와 같이 .Empty를 붙여줘야함!!
        public string Url { get; set; } = String.Empty;
        [Required]
        public string Title { get; set; } = String.Empty;
        public string Content { get; set; } = String.Empty;
        public string Description { get; set; } = String.Empty;
        public string Author { get; set; } = String.Empty;
        public DateTime DateCreated { get; set; } = DateTime.Now;
        public bool IsPublished { get; set; } = true;
        public bool IsDeleted { get; set; } = false;

    }
}
// BlazorBlog.Server/Program.cs

using BlazorBlog.Server.Data;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// .NET6는 NULL 값 허용을 안해주기 때문에 해당 옵션을 추가해주어야함‼
builder.Services.AddControllersWithViews().ConfigureApiBehaviorOptions(options =>
{
    options.SuppressModelStateInvalidFilter = true;
});
builder.Services.AddRazorPages();
builder.Services.AddDbContext<DataContext>(x => x.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection")));

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseWebAssemblyDebugging();
}
else
{
    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.UseBlazorFrameworkFiles();
app.UseStaticFiles();

app.UseRouting();


app.MapRazorPages();
app.MapControllers();
app.MapFallbackToFile("index.html");

app.Run();

 

Create 버튼을 누른 결과, 다음과 같은 화면을 확인할 수 있다!

 

 

db에도 데이터가 잘 추가되었음을 확인할 수 있다.