Skip to main content

Posts

Showing posts with the label ASP.NET Web API

Detecting breaking changes in your OpenAPI metadata

For the last 2 days I have been struggling with a breaking change I had in my ASP.NET Core web api that caused the consuming application to fail. I had a header parameter that was optional but became required after changing the nullability of my project to enabled . Although I found the issue and was able to fix it quite fast, I was not happy with my current process and was wondering how I could prevent this from happening again. This brought me to a final solution where I introduced some extra tests that compared the OpenAPI metadata between different implementations. Let me show you how I did it… Generate OpenAPI documents at build time To compare 2 OpenAPI metadata documents we first need to get them. For the already released version of the API, I can download the document through the OpenAPI endpoint ( /openapi/v1.json by default). But what about the new version of the API that is still in development? I solve this by generating the OpenAPI document at build time. This m...

VSCode - Expose a local API publicly using port forwarding

I’m currently working on building my own Copilot agent(more about this in another post). As part of the process, I needed to create an API and expose it publicly so it is accessible publicly through a GitHub app. During local development and debugging I don't want to have to publish my API, so let's look at how we can use the VS Code Port Forwarding feature to expose a local API publicly. Port forwarding Port forwarding is a networking technique that redirects communication requests from one address and port combination to another. In the context of web development and VS Code, here's what it means: When you run a web application or API locally, it's typically only accessible from your own machine at addresses like localhost:3000 or 127.0.0.1:8080 . Port forwarding creates a tunnel that takes requests coming to a publicly accessible address and forwards them to your local port. For example, if you have an API running locally on port 3000: Without port forw...

ASP.NET Core -Automatically add middleware through IStartupFilter

While browsing through the ASP.NET Core documentation , I accidently stumbled over the IStartupFilter interface. I had no idea what it did but it triggered my interest. Let's find out together in this post what this interface does and when it can be useful. What is IStartupFilter? IStartupFilter is an interface that allows you to intercept and modify how the application's request pipeline is built. It's particularly useful when you need to: Ensure certain middleware runs before any other middleware Add middleware consistently across multiple applications Create reusable middleware configurations Modify the order of middleware registration Here's the interface definition: How IStartupFilter Works When ASP.NET Core builds the middleware pipeline, it executes all registered IStartupFilter instances in the order they were registered. Each filter can add middleware before and after calling the next delegate, creating a wrapper around the exis...

.NET 8– System.Text.Json serializer error

I really like the System.Text.Json source generator as a way to optimize your API performance by reducing the serialization/deserialization cost. However after upgrading to .NET 8 I not only got some warnings , one of my applications failed during execution with the following error message: System.NotSupportedException: JsonTypeInfo metadata for type 'System.Collections.Generic.List`1[OrderDto]' was not provided by TypeInfoResolver of type ‘JsonContext'. If using source generation, ensure that all root types passed to the serializer have been annotated with 'JsonSerializableAttribute', along with any types that might be serialized polymorphically.    at System.Text.Json.ThrowHelper.ThrowNotSupportedException_NoMetadataForType(Type type, IJsonTypeInfoResolver resolver)    at System.Text.Json.JsonSerializerOptions.GetTypeInfoInternal(Type type, Boolean ensureConfigured, Nullable`1 ensureNotNull, Boolean resolveIfMutable, Boolean fallBackToNearestAncestorType)...

Using Problem Details in .NET 7

When comparing API's, I see a lot of different ways how error messages are returned. With the introduction of Problem Details for HTTP APIs ( https://tools.ietf.org/html/rfc7807 ) , we finally have a standardized error payload to return when an unhandled exception occurs. Although the standard was introduced before .NET 7, there was no out-of-the-box way to introduce the ProblemDetails spec into your ASP.NET Core application. A solution was to use the third party Hellang.Middleware.ProblemDetails nuget package: Starting from .NET 7 this nuget package is no longer necessary. You only need to add the following line to your service configuration: If someone now calls your API and an exception occurs, the returned result will look like this: We can further customize the behavior through CustomizeProblemDetails : More information Handle errors in ASP.NET Core web APIs | Microsoft Learn

Angular–Generate your OpenAPI client model

Most Angular applications need some kind of data typically provided through an OpenAPI or GraphQL API. Manually creating all the necessary model classes and client can be a time-consuming and error-prone task. In this post we have a look at ng-openapi-gen to help you automate this process. We start by installing the ng-openapi-gen module by executing the following command: npm install -g ng-openapi-gen Now we can generate our models and web client in the Angular application using the following command: ng-openapi-gen --input <path-to-openapi-json> --output <angular-app-path>/src/app/shared/api If you look at the command above you see that it requires to an OpenAPI specification file. This can be in JSON or YAML format. So we first need a way to get this specification file. If you are using ASP.NET Core with the OpenAPI integration , you can either download the OpenAPI file manually by going to the swagger UI and download it there or you can generate the OpenAPI...

Reading a connectionstring from secrets.json

As we are still using SQL accounts to connect to our databases locally, I wanted to avoid accidently checking in the SQL account information. So instead of storing my connectionstring directly in the appsettings.json file I wanted to use the secrets.json file instead. Let us find out how to achieve this... When storing your connectionstring inside your appsettings.json you can use the GetConnectionString() method to fetch a connectionstring: The same technique also works when using the built-in secret manager tool and the corresponding secrets.json. By default user secrets are loaded when your environmentname is set to Development . You can explicitly enable this by adding the following code: User Secrets are stored in a separate secrets.json. You can edit the secrets using the Secret Manager tool (dotnet user-secrets) or directly in Visual Studio by right clicking on the web project and choosing ‘Manage User Secrets’ : Remark: You can find the secrets.json file here at %...

Executing HTTP requests through Visual Studio

In Visual Studio 2022 v17.5, a new feature was introduced that allowed you to execute HTTP requests directly from the Visual Studio IDE. This is great of you want to test an API endpoint without leaving your IDE. To use this feature, open a Visual Studio project, right click on it and choose Add –> New Item . Search for http to find the HTTP file template , specify a name and click on Add . Now we can start writing our HTTP requests inside this file. You even get Intellisense while building up your requests. Once you are done, you can either click on the play icon next to the line or right click on the line and choose Send Request from the context menu. It is possible to have multiple calls in the same file, therefore separate your requests with a comment line using three hashes: You can also create variables by prefixing them with an @ and use these variables using double curly braces:  

Applying Postel’s law in ASP.NET Core–Part I

An important aspect in building robust systems is applying ‘Postel’s law’. Postel's Law was formulated by Jon Postel, an early pioneer of the Internet. The law was really a guideline for creators of software protocols. The idea was that different implementations of the protocol should interoperate. The law is today paraphrased as follows: Be conservative in what you do, be liberal in what you accept from others. It is also called the Robustness principle and it is crucial in building evolvable systems. In this post I want to see how this law applies when building ASP.NET Core Web API’s. A typical use case in ASP.NET Core is the following: A client serializes the model in JSON and sends it over HTTP to our ASP.NET Core server. On the other side, the server gets the message, extracts the body of the requests and deserializes it back to a model. It’s this second step specifically I want to focus on, how will ASP.NET Core (or more specifically the System.Text.Json s...

ASP.NET Core - Required query string parameters

Typically when designing your API, query string parameters should be reserved for optional arguments. So for example if ID is a required parameter, it is better to use this as the URI: https://example.api.com/product/1234 instead of this: https://example.api.com/product?id=1234 where 1234 is the ID of the requested product. Of course there are always exceptions and I had a situation where I wanted to use a required query parameter. ASP.NET Core can help you there by using the [Required] or [BindRequired] attributes on your action method parameters: Both controllers will return a validation error when invoked without the id parameter:

Swashbuckle - Free form query parameters

I’m creating an api where an arbitrary number of query parameters can be added. The API looks something like this: GET /api/example?param1=value1&param2=value2&param3=value3 By default when Swashbuckle generates the Swagger UI it only includes parameters that are explicitly defined at the action method level. Luckily the Open API spec supports this starting from version 3 through ‘free form query parameters’. At the spec level this looks like this: To achieve this using Swashbuckle you can use an operation filter: This will generate the following UI:

ASP.NET Core - InvalidOperationException: 'VaryByQueryKeys' requires the response cache middleware.

Yes, I’m still rewriting an existing ASP.NET Web API application to ASP.NET Core. I ported an existing action method where I was using response caching. I previously ported other action methods that used caching but this one was a little bit different because I had to take the querystring values into account for the caching. This is easily arranged by specifying the name of the query string parameter using the VaryByQueryKeys property on the ResponseCache attribute. Small tip: If you want to take all query string parameters into account, you can use “*” as a single value. When I tried to call this method I got a 500 error. Inside the logs I noticed the following error message: InvalidOperationException: 'VaryByQueryKeys' requires the response cache middleware. Although caching seemed to work for other scenario’s, when I used the VaryByQueryKeys property I had to add the response caching middleware. Here is my Startup.ConfigureServices(): And my Startup.Conf...

ASP.NET Core–Route arguments are case sensitive

I’m currently rewriting an existing ASP.NET Web API application to ASP.NET Core. Along the way I encountered some issues, here is one specific lesson I learned… After porting an ASP.NET Web API controller to .NET Core, a specific action method looked like this: Looked OK to me. But when I tried to invoke this action method, I got a 400 error back: {   "type" : " https://tools.ietf.org/html/rfc7231#section-6.5.1 " ,   "title" : "One or more validation errors occurred." ,   "status" : 400 ,   "traceId" : "00-3c02d7d0541cbb49a1790b11a71d871a-885c9350d2c4864c-00" ,   "errors" : {     "ID" : [       "The value '{id}' is not valid."     ]   } } It wasn’t immediatelly obvious to me what I did wrong. Maybe you spot my mistake? Let’s h...

ASP.NET Core - Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: Ambiguous HTTP method for action

I’m currently rewriting an existing ASP.NET Web API application to ASP.NET Core. I ported a controller from ASP.NET Web API to ASP.NET Core. Here is the end result: Everything looked OK at first sight, but when I tried to run the application I got the following error message: Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: Ambiguous HTTP method for action - Controllers.AuthTypeController.GetAll (VLM.IAM.Services). Actions require an explicit HttpMethod binding for Swagger/OpenAPI 3.0    at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperations(IEnumerable`1 apiDescriptions, SchemaRepository schemaRepository)    at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GeneratePaths(IEnumerable`1 apiDescriptions, SchemaRepository schemaRepository)    at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwagger(String documentName, String host, String basePath)    at Swashbuckle.AspNetCore.Swagge...

ASP.NET Core - ActionResult doesn’t work with IList

I’m currently rewriting an existing ASP.NET Web API application to ASP.NET Core. Yesterday I blogged about the use of the ActionResult<T> to combine the type safety of typed controllers with the list of out-of-the-box actionresults. While introducing the ActionResult<T> class everywhere, I stumbled over one use case where I got a compiler error. Here is the specific code: When you try to compile this code in Visual Studio it fails with the following error message: CS0029: Cannot implicitly convert type ‘System.Collection.Generic.IList<T> to Microsoft.AspNetCore.Mvc.ActionResult<System.Collection.Generic.IList<T>>. The reason is because C# doesn't support implicit cast operators on interfaces. Consequently, we need to convert the interface to a concrete type if we want to use it as our type argument for ActionResult<T>. An easy fix is to wrap the IList in a concrete type as I did in the example below:

ASP.NET Core - How to handle the ‘NotFound’ use case when using a typed controller

I’m currently rewriting an existing ASP.NET Web API application to ASP.NET Core. While doing that, I (re)discovered some ASP.NET Core features I forgot they existed. One of the features that ASP.NET Core offers are ‘typed controllers’. I don’t think that is the official name but you’ll know what I mean when you take a look at the example below: In the example above I created an action method GetTodoItem that returned a typed object of type ‘ TodoItem’ (or to be even more correct a Task<TodoItem> ).  This makes your life as a developer easy as you don’t have to think about the ins and outs of ASP.NET. It almost feels like that this action method is exactly the same as any other method you’ll find on a class. But what if wanted to return a 404 message when no TodoItem could be found for the specified id. Should I throw a specific exception? In ASP.NET Web API this was possible through the use of the HttpResponseException . In ASP.NET Core there is a better alternati...

NSwag error - System.InvalidOperationException: No service for type 'Microsoft.Extensions.DependencyInjection.IServiceProviderFactory`1[Autofac.ContainerBuilder]' has been registered.

In one of our projects we are using NSwag to generate our TypeScript DTO’s and services used in our Angular frontend. In this project we are using Autofac as our IoC container and have created a few extension methods that hook into the HostBuilder bootstrapping. Unfortunately our custom logic brought NSwag into trouble and caused our build to fail with the following error message: System.InvalidOperationException: No service for type 'Microsoft.Extensions.DependencyInjection.IServiceProviderFactory`1[Autofac.ContainerBuilder]' has been registered. NSwag adds an extra build target in your csproj file and uses that to run the NSwag codegenerator tool: While investigating the root cause of this issue, we introduced a small workaround where we used a separate program.cs and startup.cs specifically for the NSwag codegenerator. We added a minimal Program.cs file: And a minimal Startup.cs file: The magic to make this work is to change the nswag.json configurat...

.NET Core HttpClient–Testing redirections

When testing a specific API I had to check if the user was redirected to the correct location. However although I thought that I had written my api correctly, the response code didn’t match. Here is the related test code: Do you spot my mistake? Let’s have a look at the documentation: The default HttpClient will automatically handle redirects what makes it impossible to check for the 302 status code To change this behavior, you need to create your own HttpHandler and use it when building up your HttpClient instance:

Postman - Uploading multiple files through a multipart/form-data POST

 I had to test an ASP.NET Core API that was expecting multiple files through a multipart-formdata request. Here are the steps I had to take to get this done through Postman : Open Postman and click on the '+' sign to create a new request: Change the request type to 'POST' and enter the request URL: Click on the 'body' section and change the type to 'form-data': Enter a key name for the files you want to upload(in my case it was 'files'): Now comes the important part, go to the right border of the Key column. A dropdown appears where you can change the type from 'text' to 'file': Now the Values column changes and you get a 'Select Files' button that allows you to specify the files you want to upload: That should do the trick...