Marcin Jahn | Dev Notebook
  • Home
  • Programming
  • Technologies
  • Projects
  • About
  • Home
  • Programming
  • Technologies
  • Projects
  • About
  • An icon of the Core section Core
    • Programs Execution
    • Stack and Heap
    • Asynchronous Programming
      • Overview
      • Event Queues
      • Fibers
      • Stackless Coroutines
  • An icon of the .NET section .NET
    • HTTPClient
    • Async
      • How Async Works
      • TAP Tips
    • Equality
    • Comparisons
    • Enumerables
    • Unit Tests
    • Generic Host
    • Logging
    • Configuration
    • Records
    • Nullability
    • Garbage Collector
    • IL and Allocations
    • gRPC
    • Source Generators
    • Platform Invoke
    • ASP.NET Core
      • Overview
      • Middleware
      • Razor Pages
      • Routing in Razor Pages
      • Web APIs
      • Filters
      • Identity
      • Validation
      • Tips
    • Entity Framework Core
      • Overview
      • Testing
      • Tips
  • An icon of the Angular section Angular
    • Overview
    • Components
    • Directives
    • Services and DI
    • Routing
    • Observables (RxJS)
    • Forms
    • Pipes
    • HTTP
    • Modules
    • NgRx
    • Angular Universal
    • Tips
    • Standalone Components
  • An icon of the JavaScript section JavaScript
    • OOP
    • JavaScript - The Weird Parts
    • JS Functions
    • ES Modules
    • Node.js
    • Axios Tips
    • TypeScript
      • TypeScript Environment Setup
      • TypeScript Tips
    • React
      • React Routing
      • MobX
    • Advanced Vue.js Features
  • An icon of the Rust section Rust
    • Overview
    • Cargo
    • Basics
    • Ownership
    • Structures
    • Enums
    • Organization
    • Collections
    • Error Handling
    • Generics
    • Traits
    • Lifetimes
    • Closures
    • Raw Pointers
    • Smart Pointers
    • Concurrency
    • Testing
    • Tips
  • An icon of the C/C++ section C/C++
    • Compilation
    • Structures
    • OOP in C
    • Pointers
    • Strings
    • Dynamic Memory
    • argc and argv Visualization
  • An icon of the GTK section GTK
    • Overview
    • GObject
    • GJS
  • An icon of the CSS section CSS
    • Responsive Design
    • CSS Tips
    • CSS Pixel
  • An icon of the Unity section Unity
    • Unity
  • An icon of the Functional Programming section Functional Programming
    • Fundamentals of Functional Programming
    • .NET Functional Features
    • Signatures
    • Function Composition
    • Error Handling
    • Partial Application
    • Modularity
    • Category Theory
      • Overview
      • Monoid
      • Other Magmas
      • Functors
  • An icon of the Algorithms section Algorithms
    • Big O Notation
    • Array
    • Linked List
    • Queue
    • Hash Table and Set
    • Tree
    • Sorting
    • Searching
  • An icon of the Architecture section Architecture
    • What is architecture?
    • Domain-Driven Design
    • ASP.NET Core Projects
  • An icon of the Core section Core
    • Programs Execution
    • Stack and Heap
    • Asynchronous Programming
      • Overview
      • Event Queues
      • Fibers
      • Stackless Coroutines
  • An icon of the .NET section .NET
    • HTTPClient
    • Async
      • How Async Works
      • TAP Tips
    • Equality
    • Comparisons
    • Enumerables
    • Unit Tests
    • Generic Host
    • Logging
    • Configuration
    • Records
    • Nullability
    • Garbage Collector
    • IL and Allocations
    • gRPC
    • Source Generators
    • Platform Invoke
    • ASP.NET Core
      • Overview
      • Middleware
      • Razor Pages
      • Routing in Razor Pages
      • Web APIs
      • Filters
      • Identity
      • Validation
      • Tips
    • Entity Framework Core
      • Overview
      • Testing
      • Tips
  • An icon of the Angular section Angular
    • Overview
    • Components
    • Directives
    • Services and DI
    • Routing
    • Observables (RxJS)
    • Forms
    • Pipes
    • HTTP
    • Modules
    • NgRx
    • Angular Universal
    • Tips
    • Standalone Components
  • An icon of the JavaScript section JavaScript
    • OOP
    • JavaScript - The Weird Parts
    • JS Functions
    • ES Modules
    • Node.js
    • Axios Tips
    • TypeScript
      • TypeScript Environment Setup
      • TypeScript Tips
    • React
      • React Routing
      • MobX
    • Advanced Vue.js Features
  • An icon of the Rust section Rust
    • Overview
    • Cargo
    • Basics
    • Ownership
    • Structures
    • Enums
    • Organization
    • Collections
    • Error Handling
    • Generics
    • Traits
    • Lifetimes
    • Closures
    • Raw Pointers
    • Smart Pointers
    • Concurrency
    • Testing
    • Tips
  • An icon of the C/C++ section C/C++
    • Compilation
    • Structures
    • OOP in C
    • Pointers
    • Strings
    • Dynamic Memory
    • argc and argv Visualization
  • An icon of the GTK section GTK
    • Overview
    • GObject
    • GJS
  • An icon of the CSS section CSS
    • Responsive Design
    • CSS Tips
    • CSS Pixel
  • An icon of the Unity section Unity
    • Unity
  • An icon of the Functional Programming section Functional Programming
    • Fundamentals of Functional Programming
    • .NET Functional Features
    • Signatures
    • Function Composition
    • Error Handling
    • Partial Application
    • Modularity
    • Category Theory
      • Overview
      • Monoid
      • Other Magmas
      • Functors
  • An icon of the Algorithms section Algorithms
    • Big O Notation
    • Array
    • Linked List
    • Queue
    • Hash Table and Set
    • Tree
    • Sorting
    • Searching
  • An icon of the Architecture section Architecture
    • What is architecture?
    • Domain-Driven Design
    • ASP.NET Core Projects

ASP.NET Core Tips

Customizing URLs

By default, apps listen on port 5000 (HTTP) or 5001 (HTTPS). We can change it with:

  • command-line arguments
  • the ASPNETCORE_URLS env
  • launchSetings.json - it’s not included in a publish, it’s for development only

For example:

  • ASPNETCORE_URLS=http://localhost:8080 - just localhost hostname
  • ASPNETCORE_URLS=http://*:8080 - any hostname that maps to our machine
  • ASPNETCORE_URLS=http://localhost:8080;http://localhost:5000 - two ports

Reverse Proxy

When our app sits behind a reverse proxy we lose some information like:

  • caller IP
  • used protocol (http/https)

This information may be provided to our app by a reverse proxy via the X-Forwarded-* protocols. By default (using CreateDefaultBuilder), ASP.NET Core uses middleware that handles these headers, but that middleware is disabled. We can enable it with env ASPNETCORE_FORWARDEDHEADERS_ENABLED = true. This middleware overrides some of the HttpContext properties.

Cancelling request

Every API endpoint can have CancellationToken as one of its parameters. It’s cancelled when client cancells request. We can use this token when handling request. Example:

[HttpGet]
public async Task<ActionResult<List<Activity>>> List(CancellationToken ct)
{
return await _mediator.Send(new List.Query(), ct);
}

What’s the recommended way of actually handling the cancellation?

MediatR

It’s a good idea to use MediatR as a kind oif broker for requests. It makes controller quite simple and also encourages CQRS. It requires MediatR.Extensions.Microsoft.DependencyInjection package to be installed in the project where business logic is (asp.net seems to have it by default already). Then, controllers do this:

private readonly IMediator _mediator;
public ActivitiesController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet]
public async Task<ActionResult<List<Activity>>> List(CancellationToken ct)
{
return await _mediator.Send(new List.Query(), ct);
}

Example of a request handler:

public class Details
{
public class Query : IRequest<Activity>
{
public Guid Id { get; set; }
}
public class Handler : IRequestHandler<Query, Activity>
{
private readonly DataContext _dbContext;
public Handler(DataContext dbContext)
{
_dbContext = dbContext;
}
public async Task<Activity> Handle(Query request, CancellationToken cancellationToken)
{
var activity = await _dbContext.Activities.FindAsync(request.Id);
return activity;
}
}
}

What’s needed is an implementation of IRequest and IRequestHandler.

In order to registers handler with MediatR, we need to do this in ConfigureServices:

services.AddMediatR(typeof(Details.Handler).Assembly);

It uses reflection to find all handlers. We used Details.Handler here just to find its assembly where all other handlers reside.

Custom error middleware

Middleware:

public class ErrorHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
public ErrorHandlingMiddleware(RequestDelegate next, ILogger<ErrorHandlingMiddleware> logger)
{
_logger = logger;
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception e)
{
await HandleExceptionAsync(context, e);
}
}
private async Task HandleExceptionAsync(HttpContext context, Exception ex)
{
object errors = null;
switch (ex)
{
case RestException re:
_logger.LogError(ex, "REST ERROR");
errors = re.Errors;
context.Response.StatusCode = (int)re.Code;
break;
case Exception e:
_logger.LogError(ex, "SERVER ERROR");
errors = string.IsNullOrWhiteSpace(e.Message) ? "Error" : e.Message;
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
break;
}
context.Response.ContentType = "application/json";
if (errors != null)
{
var result = JsonSerializer.Serialize(new
{
errors
});
await context.Response.WriteAsync(result);
}
}
}

In Startup:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMiddleware<ErrorHandlingMiddleware>();
...
}

Putting that in the beginning is a good idea.

←  Validation
© 2023 Marcin Jahn | Dev Notebook | All Rights Reserved. | Built with Astro.