kr.bbon.AspNetCore

Helper for ASP.NET Core


Keywords
aspnet, aspnetcore, bbon, bbon.kr, dotnet, kr.bbon, net5, net6, net7, nuget, webapi
Install
Install-Package kr.bbon.AspNetCore -Version 1.0.5

Documentation

kr.bbon.AspNetCore Package

publish to nuget

πŸ“’ Overview

.NET 5 μ—μ„œ API μ›Ή μ‘μš©ν”„λ‘œκ·Έλž¨ ν”„λ‘œμ νŠΈ (webapi ν…œν”Œλ¦Ώ κΈ°μ€€)λ₯Ό μ‹œμž‘ν•  λ•Œ, 반볡적으둜 μž‘μ„±ν•˜λ˜ 사항을 νŒ¨ν‚€μ§€λ‘œ μ •λ¦¬ν–ˆμŠ΅λ‹ˆλ‹€.

🌈 Namespace

kr.bbon.AspNetCore

μ›Ή μ‘μš©ν”„λ‘œκ·Έλž¨μ—μ„œ μ‚¬μš©λ˜λŠ” κΈ°λŠ₯κ³Ό ν™•μž₯ λ©”μ„œλ“œλ₯Ό ν¬ν•¨ν•©λ‹ˆλ‹€.

kr.bbon.Core νŒ¨ν‚€μ§€λ‘œ μ΄λ™λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

kr.bbon.AspNetCore.Filters

ASP.NET Core Mvc Filtersλ₯Ό μ§€μ›ν•˜λŠ” κΈ°λŠ₯을 ν¬ν•¨ν•©λ‹ˆλ‹€.

kr.bbon.AspNetCore.Mvc

ASP.NET Core MVCλ₯Ό μ§€μ›ν•˜λŠ” κΈ°λŠ₯을 ν¬ν•¨ν•©λ‹ˆλ‹€.

🎯 Features

Exception classes

HttpStatusException class

kr.bbon.Core νŒ¨ν‚€μ§€λ‘œ μ΄λ™λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

HTTP μ˜ˆμ™Έλ₯Ό ν‘œν˜„ν•˜κΈ° μœ„ν•΄ μ‚¬μš©λ©λ‹ˆλ‹€.

kr.bbon.Core νŒ¨ν‚€μ§€λ‘œ μ΄μ „λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

// Exception handling in action method
try
{
    // ...
}
catch(HttpStatusException ex)
{
    return StatusCode(ex.StatusCode, ex.Message, ex.GetDetails());
}
catch(Exception ex)
{
    // ...
}

HttpStatusException class

kr.bbon.Core νŒ¨ν‚€μ§€λ‘œ μ΄λ™λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

HTTP μ˜ˆμ™Έμ™€ 상세 정보λ₯Ό ν‘œν˜„ν•˜κΈ° μœ„ν•΄ μ‚¬μš©λ©λ‹ˆλ‹€.

kr.bbon.Core νŒ¨ν‚€μ§€λ‘œ μ΄μ „λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

throw new HttpStatusException(HttpStatusCode.BadRequest, new SomeDetails
{
    Id = "err111",
    Message = "AAA μ†μ„±μ˜ 값이 μ •μˆ˜κ°€ μ•„λ‹™λ‹ˆλ‹€.",
});

SomethingWrongException

kr.bbon.Core νŒ¨ν‚€μ§€λ‘œ μ΄λ™λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

μ‚¬μš©μž μ •μ˜ μ˜ˆμ™Έλ₯Ό ν‘œν˜„ν•˜κΈ° μœ„ν•΄ μ‚¬μš©ν•©λ‹ˆλ‹€.

kr.bbon.Core νŒ¨ν‚€μ§€λ‘œ μ΄μ „λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

// Exception handling in action method 
try
{
    // ...
}
catch(SomethingWrongException ex)
{
    return StatusCode(HttpStatusCode.Forbidden, ex.Message, ex.GetDetails());
}
catch(Exception ex)
{
    // ...
}

SomethingWrongException

kr.bbon.Core νŒ¨ν‚€μ§€λ‘œ μ΄λ™λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

μ‚¬μš©μž μ •μ˜ μ˜ˆμ™Έμ™€ 상세 정보λ₯Ό ν‘œν˜„ν•˜κΈ° μœ„ν•΄ μ‚¬μš©ν•©λ‹ˆλ‹€.

kr.bbon.Core νŒ¨ν‚€μ§€λ‘œ μ΄μ „λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

throw new SomethingWrongException("데이터λ₯Ό μ²˜λ¦¬ν•˜μ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€.", new SomeDetails 
{
    Id = "err111",
    Message = "AAA μ†μ„±μ˜ 값이 μ •μˆ˜κ°€ μ•„λ‹™λ‹ˆλ‹€.",
});

Extension methods

IApplicationBuilder.UseSwaggerUIWithApiVersioning() method

IApplicationBuilder μΈν„°νŽ˜μ΄μŠ€μ— UseSwaggerUIWithApiVersioning λ©”μ„œλ“œλ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€.

Swagger λ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄ ν•„μš”ν•œ μ½”λ“œ 쑰각이 μ •λ¦¬λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

// Configure() on Startup.cs
app.UseSwaggerUIWithApiVersioning();

Object.ToJson() method

Object ν΄λž˜μŠ€μ— ToJson λ©”μ„œλ“œλ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€.

객체의 μΈμŠ€ν„΄μŠ€λ₯Ό JSON λ¬Έμžμ—΄λ‘œ μ§λ ¬ν™”ν•˜λŠ” κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€.

kr.bbon.Core νŒ¨ν‚€μ§€λ‘œ μ΄μ „λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

var sample = getSampleModel();
sample.ToJson();

JsonSerializerOptions λ§€κ°œλ³€μˆ˜κ°€ μ§€μ •λ˜μ§€ μ•Šμ€ 경우 μ•„λž˜ 기본값을 μ‚¬μš©ν•©λ‹ˆλ‹€.

var defaultOptions = new JsonSerializerOptions
{
    WriteIndented = true,
    Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};

IServiceCollection.AddApiVersioningAndSwaggerGen() method

IServiceCollection μΈν„°νŽ˜μ΄μŠ€μ— AddApiVersioningAndSwaggerGen λ©”μ„œλ“œλ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€.

ApiVersioning, Swagger λ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄ ν•„μš”ν•œ μ½”λ“œμ‘°κ°μ΄ μ •λ¦¬λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

// ConfigureService() on Startup.cs
var defaultApiVersion = new ApiVersion(1, 0);
services.Configure<AppOptions>(Configuration.GetSection(AppOptions.Name));

services.AddApiVersioningAndSwaggerGen(defaultApiVersion);

Filter classes

ApiExceptionHandlerFilter class

μ»¨νŠΈλ‘€λŸ¬μ—μ„œ μ²˜λ¦¬λ˜μ§€ μ•Šμ€ μ˜ˆμ™Έκ°€ λ°œμƒν•˜λ©΄ μ˜ˆμ™Έλ₯Ό μ²˜λ¦¬ν•˜λŠ” ν•„ν„°λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

μ˜ˆμ™Έ 처리 ν•„ν„°λ₯Ό μ»¨νŠΈλ‘€λŸ¬μ—μ„œ μ‚¬μš©ν•˜λ €λ©΄ μ•„λž˜μ™€ 같이 클래슀 νŠΉμ„±μœΌλ‘œ μΆ”κ°€ν•©λ‹ˆλ‹€.

[ApiExceptionHandlerFilter]
// ...
public class SomeController : ApiControllerBase 
{
    // ...
}

μ˜ˆμ™Έ 처리 ν•„ν„°λ₯Ό μ•‘μ…˜μ—μ„œ μ‚¬μš©ν•˜λ €λ©΄ μ•„λž˜μ™€ 같이 클래슀 νŠΉμ„±μœΌλ‘œ μΆ”κ°€ν•©λ‹ˆλ‹€.

[HttpGet]
[ApiExceptionHandlerFilter]
public IActionResult SomeAction()
{
    // ...
}

μ˜ˆμ™Έ 처리 ν•„ν„°λ₯Ό μ „μ—­μœΌλ‘œ μ‚¬μš©ν•˜λ €λ©΄ μ•„λž˜μ™€ 같이 κ΅¬μ„±ν•©λ‹ˆλ‹€.

// ConfigureService() on Startup.cs
services.AddControllers(options => 
{
    options.Filters.Add<ApiExceptionHandlerFilter>();
});

Model classes

ApiResponseModel classes

λ™μΌν•œ 응닡을 μ œκ³΅ν•˜κΈ° μœ„ν•΄ μ‚¬μš©λ˜λŠ” 응닡 λͺ¨λΈμž…λ‹ˆλ‹€.

μ•„λž˜μ™€ 같은 ν˜•μ‹μœΌλ‘œ HTTP 응닡 본문을 μ œκ³΅ν•©λ‹ˆλ‹€.

{
    statusCode: number
    message: string
    instance: string
    path: string
    method: string
    data: T
}

ErrorModel class

μ‚¬μš©μž μ •μ˜ 였λ₯˜λ₯Ό ν‘œν˜„ν•©λ‹ˆλ‹€.

kr.bbon.Core νŒ¨ν‚€μ§€λ‘œ μ΄μ „λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

var error = new ErrorModel
{
    Code = "Some code",
    Message = "Some message",
    InnerError = new ErrorModel
    {
        Code = "Some inner code",
        Message = "Some inner message",
    }
});

ControllerBase classes

ApiControllerBase class

μ›Ή μ‘μš©ν”„λ‘œκ·Έλž¨ 응닡을 λ™μΌν•˜κ²Œ μ œκ³΅ν•˜κΈ° μœ„ν•œ μ½”λ“œμ‘°κ°μ΄ μ •λ¦¬λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

⚠ ApiControllerBase 클래슀λ₯Ό μƒμ†ν•˜λŠ” ApiController λŠ” ApiVersionAttribute, AreaAttribute, RouteAttribute λ₯Ό ν•„μˆ˜μ μœΌλ‘œ μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.

[ApiVersion(DefaultValues.ApiVersion)]
[ApiController]
[Area(DefaultValues.AreaName)]
[Route(DefaultValues.RouteTemplate)]
[ApiExceptionHandlerFilter]
public class WeatherForecastController : ApiControllerBase 
{
    // ...
}

컨트둀러의 μ•‘μ…˜ λ©”μ„œλ“œμ—μ„œ μ•„λž˜μ™€ 같이 응닡을 κ΅¬μ„±ν•©λ‹ˆλ‹€.

var responseData = GetResponseData();

return StatusCode(HttpStatusCode.OK, responseData)

HTTP 응닡본문은 μ•„λž˜μ™€ 같이 μ œκ³΅λ©λ‹ˆλ‹€.

{
    "statusCode": 200,
    "message": null,
    "data": {
        // ...responseData
    }
}

Etc classes

AppOptions class

μ‘μš© ν”„λ‘œκ·Έλž¨ ꡬ성값을 ν‘œν˜„ν•©λ‹ˆλ‹€.

appsettings.json 을 μ•„λž˜μ™€ 같이 κ΅¬μ„±ν•˜κ³ , μ„œλΉ„μŠ€ κ΅¬μ„±μ—μ„œ ꡬ성값을 μ½μ–΄μ„œ μ²˜λ¦¬ν•©λ‹ˆλ‹€.

OpenApiInfo ν΄λž˜μŠ€λ„ λ™μΌν•œ κ°’μœΌλ‘œ κ΅¬μ„±λ©λ‹ˆλ‹€.

// appsettings.json 
{
    "App": {
        "Title": "Awesome api",
        "Description": "My awesome api !!"
    },
    // ...
}
// ConfigureService() on Startup.cs
services.Configure<AppOptions>(Configuration.GetSection(AppOptions.Name));

ConfigureSwaggerOptionsBase class

Swagger ꡬ성값을 μœ„ν•œ κΈ°λ³Έ ν΄λž˜μŠ€μž…λ‹ˆλ‹€.

μ•„λž˜μ™€ 같이 ConfigureSwaggerOptionsBase λ₯Ό μƒμ†ν•˜λŠ” 본인의 ConfigureSwaggerOptions 클래슀λ₯Ό μ •μ˜ν•˜μ„Έμš”.

public class MyConfigureSwaggerOptions :  ConfigureSwaggerOptionsBase {
    public MyConfigureSwaggerOptions(IApiVersionDescriptionProvider provider, IOptionsMonitor<AppOptions> optionsAccessor)
        : base(provider) 
    {
        options = optionsAccessor.CurrentValue;
    }

    public override string AppTitle => options.Title;

    public override string AppDescription => options.Description;

    private readonly AppOptions options;
}

ConfigureSwaggerOptions class

ConfigureSwaggerOptionsBase 클래슀λ₯Ό κ΅¬ν˜„ν•˜λŠ” ν΄λž˜μŠ€μž…λ‹ˆλ‹€.

Constants

DefaultValues class

RouteTemplate: 클래슀 라우트 ν…œν”Œλ¦Ώμ˜ κΈ°λ³Έκ°’μž…λ‹ˆλ‹€. [area]/v{version:apiVersion}/[controller]

ApiVersion: κΈ°λ³Έ 버전 λ¬Έμžμ—΄ μž…λ‹ˆλ‹€. 1.0

AreaName: μ˜μ—­ κΈ°λ³Έκ°’μž…λ‹ˆλ‹€. api