ASP.NET Core

Microsoftが開発したクロスプラットフォーム対応のモダンWebフレームワーク。高性能なKestrelサーバーを搭載し、マイクロサービスに最適。

C#フレームワークBackendWeb.NETMVCAPI

GitHub概要

dotnet/aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.

ホームページ:https://asp.net
スター36,872
ウォッチ1,434
フォーク10,394
作成日:2014年3月11日
言語:C#
ライセンス:MIT License

トピックス

aspnetcoredotnethacktoberfesthelp-wanted

スター履歴

dotnet/aspnetcore Star History
データ取得日時: 2025/7/17 10:32

フレームワーク

ASP.NET Core

概要

ASP.NET Coreは、Microsoftが開発したクロスプラットフォーム対応の高性能Webフレームワークです。

詳細

ASP.NET Core(エーエスピー・ドット・ネット・コア)は、Microsoftが開発したオープンソースのクロスプラットフォーム対応Webフレームワークです。2016年にリリースされ、従来のASP.NET Frameworkを完全に再設計・再構築したものです。Windows、macOS、Linuxで動作し、高いパフォーマンスと拡張性を提供します。モジュラー設計により必要な機能のみを組み込むことができ、軽量で高速なアプリケーション開発が可能です。MVC、Web API、Razor Pages、Blazorなど複数の開発モデルをサポートし、企業レベルのWebアプリケーション開発に最適化されています。組み込みの依存性注入、設定システム、ロギング、認証・認可機能により、堅牢で保守性の高いアプリケーションを構築できます。Docker対応、クラウドネイティブ設計により、現代的なマイクロサービスアーキテクチャにも適しています。

メリット・デメリット

メリット

  • 高いパフォーマンス: 最適化されたランタイムで高速な処理を実現
  • クロスプラットフォーム: Windows、macOS、Linuxで動作
  • 統合開発環境: Visual Studioによる強力な開発サポート
  • 企業レベル対応: 大規模アプリケーション開発に最適化
  • 豊富な機能: 認証、承認、キャッシュ、ロギングなど標準搭載
  • 強力な型システム: C#による型安全性とIntelliSense
  • 活発なエコシステム: NuGetによる豊富なライブラリ群

デメリット

  • 学習コスト: C#言語とフレームワーク概念の習得が必要
  • Microsoftエコシステム: Microsoft技術スタックへの依存
  • メモリ使用量: JVMやNode.jsと比較して多めのメモリ消費
  • デプロイの複雑さ: .NETランタイムのインストールが必要
  • ライセンス: 一部の開発ツールが有償

主要リンク

書き方の例

Hello World(基本的なWebアプリ)

// Program.cs - Minimal API
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");
app.MapGet("/hello/{name}", (string name) => $"Hello {name}!");

app.Run();
// Program.cs - MVC
var builder = WebApplication.CreateBuilder(args);

// サービスの登録
builder.Services.AddControllersWithViews();

var app = builder.Build();

// ミドルウェアパイプライン
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

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

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

コントローラーとアクション

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        public IActionResult About()
        {
            ViewData["Message"] = "ASP.NET Coreアプリケーションです。";
            return View();
        }

        [HttpPost]
        public IActionResult Contact(ContactModel model)
        {
            if (ModelState.IsValid)
            {
                // フォーム処理ロジック
                return RedirectToAction("ContactSuccess");
            }
            return View(model);
        }

        [HttpGet]
        public JsonResult GetUser(int id)
        {
            var user = new { Id = id, Name = "太郎", Email = "[email protected]" };
            return Json(user);
        }
    }

    public class ContactModel
    {
        public string Name { get; set; } = string.Empty;
        public string Email { get; set; } = string.Empty;
        public string Message { get; set; } = string.Empty;
    }
}

依存性注入

// サービスインターフェース
public interface IUserService
{
    Task<User> GetUserAsync(int id);
    Task<IEnumerable<User>> GetAllUsersAsync();
}

// サービス実装
public class UserService : IUserService
{
    private readonly ILogger<UserService> _logger;
    private readonly HttpClient _httpClient;

    public UserService(ILogger<UserService> logger, HttpClient httpClient)
    {
        _logger = logger;
        _httpClient = httpClient;
    }

    public async Task<User> GetUserAsync(int id)
    {
        _logger.LogInformation("ユーザー取得: {UserId}", id);
        
        var response = await _httpClient.GetAsync($"/api/users/{id}");
        response.EnsureSuccessStatusCode();
        
        var json = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<User>(json) ?? new User();
    }

    public async Task<IEnumerable<User>> GetAllUsersAsync()
    {
        var response = await _httpClient.GetAsync("/api/users");
        response.EnsureSuccessStatusCode();
        
        var json = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<IEnumerable<User>>(json) ?? Enumerable.Empty<User>();
    }
}

// Program.csでの登録
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddHttpClient<UserService>(client =>
{
    client.BaseAddress = new Uri("https://api.example.com");
});

// コントローラーでの使用
public class UsersController : Controller
{
    private readonly IUserService _userService;

    public UsersController(IUserService userService)
    {
        _userService = userService;
    }

    public async Task<IActionResult> Index()
    {
        var users = await _userService.GetAllUsersAsync();
        return View(users);
    }
}

Entity Framework Core

// モデル定義
public class Blog
{
    public int Id { get; set; }
    public string Title { get; set; } = string.Empty;
    public string Content { get; set; } = string.Empty;
    public DateTime CreatedAt { get; set; }
    public List<Post> Posts { get; set; } = new();
}

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; } = string.Empty;
    public string Content { get; set; } = string.Empty;
    public int BlogId { get; set; }
    public Blog Blog { get; set; } = null!;
}

// DbContext
using Microsoft.EntityFrameworkCore;

public class BlogContext : DbContext
{
    public BlogContext(DbContextOptions<BlogContext> options) : base(options) { }

    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>()
            .Property(b => b.Title)
            .IsRequired()
            .HasMaxLength(200);

        modelBuilder.Entity<Post>()
            .HasOne(p => p.Blog)
            .WithMany(b => b.Posts)
            .HasForeignKey(p => p.BlogId);
    }
}

// Program.csでの設定
builder.Services.AddDbContext<BlogContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

// コントローラーでの使用
public class BlogsController : Controller
{
    private readonly BlogContext _context;

    public BlogsController(BlogContext context)
    {
        _context = context;
    }

    public async Task<IActionResult> Index()
    {
        var blogs = await _context.Blogs
            .Include(b => b.Posts)
            .ToListAsync();
        return View(blogs);
    }

    [HttpPost]
    public async Task<IActionResult> Create(Blog blog)
    {
        if (ModelState.IsValid)
        {
            blog.CreatedAt = DateTime.Now;
            _context.Blogs.Add(blog);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
        return View(blog);
    }
}

ミドルウェア

// カスタムミドルウェア
public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestLoggingMiddleware> _logger;

    public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var startTime = DateTime.UtcNow;
        
        _logger.LogInformation("リクエスト開始: {Method} {Path}",
            context.Request.Method, context.Request.Path);

        await _next(context);

        var duration = DateTime.UtcNow - startTime;
        _logger.LogInformation("リクエスト完了: {Method} {Path} - {StatusCode} ({Duration}ms)",
            context.Request.Method, context.Request.Path, context.Response.StatusCode, duration.TotalMilliseconds);
    }
}

// 拡張メソッド
public static class MiddlewareExtensions
{
    public static IApplicationBuilder UseRequestLogging(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestLoggingMiddleware>();
    }
}

// Program.csでの使用
app.UseRequestLogging();

// エラーハンドリングミドルウェア
public class ErrorHandlingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<ErrorHandlingMiddleware> _logger;

    public ErrorHandlingMiddleware(RequestDelegate next, ILogger<ErrorHandlingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "未処理の例外が発生しました");

            context.Response.StatusCode = 500;
            context.Response.ContentType = "application/json";

            var response = new { error = "内部サーバーエラーが発生しました" };
            await context.Response.WriteAsync(JsonSerializer.Serialize(response));
        }
    }
}

Web APIとJSON処理

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IProductService _productService;
    private readonly ILogger<ProductsController> _logger;

    public ProductsController(IProductService productService, ILogger<ProductsController> logger)
    {
        _productService = productService;
        _logger = logger;
    }

    [HttpGet]
    public async Task<ActionResult<IEnumerable<Product>>> GetProducts()
    {
        try
        {
            var products = await _productService.GetAllProductsAsync();
            return Ok(products);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "商品一覧取得でエラーが発生");
            return StatusCode(500, "内部サーバーエラー");
        }
    }

    [HttpGet("{id}")]
    public async Task<ActionResult<Product>> GetProduct(int id)
    {
        var product = await _productService.GetProductByIdAsync(id);
        
        if (product == null)
        {
            return NotFound($"ID {id} の商品が見つかりません");
        }

        return Ok(product);
    }

    [HttpPost]
    public async Task<ActionResult<Product>> CreateProduct([FromBody] CreateProductRequest request)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var product = new Product
        {
            Name = request.Name,
            Price = request.Price,
            Description = request.Description,
            CreatedAt = DateTime.UtcNow
        };

        var createdProduct = await _productService.CreateProductAsync(product);
        
        return CreatedAtAction(
            nameof(GetProduct), 
            new { id = createdProduct.Id }, 
            createdProduct);
    }

    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateProduct(int id, [FromBody] UpdateProductRequest request)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var existingProduct = await _productService.GetProductByIdAsync(id);
        if (existingProduct == null)
        {
            return NotFound();
        }

        existingProduct.Name = request.Name;
        existingProduct.Price = request.Price;
        existingProduct.Description = request.Description;
        existingProduct.UpdatedAt = DateTime.UtcNow;

        await _productService.UpdateProductAsync(existingProduct);
        
        return NoContent();
    }

    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteProduct(int id)
    {
        var product = await _productService.GetProductByIdAsync(id);
        if (product == null)
        {
            return NotFound();
        }

        await _productService.DeleteProductAsync(id);
        return NoContent();
    }
}

// DTOクラス
public class CreateProductRequest
{
    [Required(ErrorMessage = "商品名は必須です")]
    [StringLength(100, ErrorMessage = "商品名は100文字以内で入力してください")]
    public string Name { get; set; } = string.Empty;
    
    [Range(0, double.MaxValue, ErrorMessage = "価格は0以上である必要があります")]
    public decimal Price { get; set; }
    
    [StringLength(500, ErrorMessage = "説明は500文字以内で入力してください")]
    public string Description { get; set; } = string.Empty;
}

public class UpdateProductRequest
{
    [Required]
    [StringLength(100)]
    public string Name { get; set; } = string.Empty;
    
    [Range(0, double.MaxValue)]
    public decimal Price { get; set; }
    
    [StringLength(500)]
    public string Description { get; set; } = string.Empty;
}