My story of contributing to the ASP.NET Core repository

Introduction

Christmas is just around the corner, and I wanted to show you something that is a specialty on this blog: contributing to open-source projects and, this case, to the official ASP.NET Core repository. As I had recognized, this is really popular around the holidays (by looking at the issue comments and commits of open-source libraries), as probably most people have some free time from work to do something for the greater good. Based on this article, this is indeed a real thing.

The issue

We were working on a project that consisted of an API and two separate React applications built using CRA – one of them was public-facing and the other was the admin application. We wanted to keep it simple and let the ASP.NET Core application host the API, and the two React applications, by using the static files middleware. This was working perfectly for the production build, and we set up our build jobs to put one of the applications to a different folder.

However, during development, we faced an issue that we couldn’t host the application in the subdirectory due to some incomprehensible 404 errors. So, we used the code below:

Seems correct, right? The admin application was not able to load at all, but it was not a problem with the React app itself, because opening the URL http://localhost:3001/admin/, application loaded properly as we set the base Url property of it correctly.

After an hour of hopeless debugging, I decided to take a look at the proxy code, because this code should work. The part that builds the URL to the destination also seemed fine:

LINK

The baseUri variable holds the parameter that we pushed to the proxy (http://localhost:3000 and http://localhost:3001/admin/). That was the point when it all made sense :if you use the Map middleware, the HTTP context will not have the prefix that is provided in the first parameter – which was also not straightforward at first glance, but this turned out after some debugging too.

And one also should not forget that the concatenation of URI segments also does not work the same as they work in the case of strings. So, the issue was clear now: the problem is in the proxy as http://localhost:3001/admin + /js/1.abcdef.js = http://localhost:3001/js/1.abcdef.js). I found an open GitHub issue for this problem without any activity. We were able to work around that, but we were not 100% satisfied with this solution. We did not have any other choice though, and this code only ran in debug mode anyway:

When the admin site started to work, I left a comment on the GitHub issue that we also saw this problem, and sent ahigh-level fix for that. Our project then went live, but there was no activity on the issue, so I felt encouraged to take a look at it and try to fix it myself.

Please note that this is not the canonical way to host an ASP.NET Core Web API and a React application together, as it is mentioned on a different, but related GitHub issue (see below). The standard way is the other way around: the React application proxies the request to the API. But since the code base does not work properly for certain scenarios, I decided to give it a try.

First try – running the framework

The first thing I realized is that a single solution contains all the NuGet packages and framework code, so you need to build the framework in order to try out changes for the related packages at the same time. The new framework version can also use some new language features, so you must have a preview version of Visual Studio installed that has references to the freshly built framework using some environment variables. The full documentation can be found here, which seems to have simplified since I needed to this in August 2022 – just 4 months since this article was written.

The code change seemed to be pretty straight forward, I opened my PR, but the integration tests failed for some reason. I have looked up the issues and the failed test were not related to the change that I had submitted – it turned out that there is no automated test for this piece of code – this is not a code that runs in production in most cases after all. Merging the latest main to the code solved the issue, I got my approval, and I was waiting to get it to the main branch, and have it released with .NET7.0.

Another issue comes into picture

A couple of weeks went by, and still there was no sign of intention to complete this pull request. It turned out that there is a different issue with the same line of code, which is odd, because I have not encountered this and have been using this proxy for quite awhile now. The issue is similar (by using URI-s, https://www.google.com + //path = https://path), but the fix that I have provided does not fix this one. I have decided to fix both issues at the same time, because if that code gets into the repository, it would be replaced anyway, and I took some inspiration from the competing PR (which was not perfect, too).

My first try failed – I did not get the intention correctly (my bad). Then, I was able to fix it properly, but the owners of the code had some feedback on it – which is fine, that’s why we have code review. After that, the owners asked for some help for manually testing it, because there were no unit tests for it. Testing it manually was actually very hard, because I needed to build the package and the framework at the same time, then build my test project with the built framework (it’s easier to use VS Code for that, in my opinion, it’s much faster and does not take too much space on the hard drive),and manually call some URLs. This is when I decided to help them out by writing unit tests for it, because it can speed up the testing, and also would be good for the future as well.

Unit testing

Writing unit tests for this piece of code seemed complex for the first time, because it is an extension method for the Http Context class. But it wasn’t that bad, I just needed an HTTP Client that has a mocked Http Message Handler returning some static response and catches the message that is going to be sent. Then I can verify that the URL of the message is correct.

The only interesting thing is that I needed to override a protected method of the Http Client – the Send Async method. I would think twice about doing so on a project built on the stable version of ASP.NET, because I might not want to refactor my unit tests when the framework changes something internally. But it is totally fine in this case, because in case the implementation of Http Client changed, the unit test would fail in a repository that is deeply connected to the framework, and people can maintain it.

After my tests passed with flying colors, I received some feedback on the test cases and also some refactoring of the same single line of code that we had discussed for a couple of weeks. I quickly made the changes, and finally my code got into the main branch. It will probably be part of ASP.NET 8 unless somebody changes it 😊. At least, my test will be there anyway.

Conclusion

With this blog post, I wanted to show you that you might need a different mindset when contributing to open-source projects. You need to be careful with all the changes, because you probably spotted a mistake in one line of code or in a source file, but you also need to maintain the functionality of other parts of the library. That’s why semantic versioning and automated tests are a must for libraries maintained by the community. As they definitely do not want to rewrite the framework every couple of years, it totally makes sense that they review the code so precisely and I’m grateful for the discussion over that single line of code. We are talking about the most complex open-source project of the .NET world, after all.