runtime: Unexpected breaking change in .NET 7

Description

The StringContent constructor:

public StringContent(string content, Encoding? encoding, string mediaType)

has changed behaviour from .NET6 to .NET7. We had a particular code-path that ended up passing null into this constructor for the mediaType, which worked as expected in .NET6 and set the mime type to “text/plain”. In .NET7 the same call throws ArgumentException.

The fix for this would be changing StringContent (lines 47-48) from:

public StringContent(string content, Encoding? encoding, string mediaType)
            : this(content, encoding, new MediaTypeHeaderValue(mediaType, (encoding ?? DefaultStringEncoding).WebName))

to:

public StringContent(string content, Encoding? encoding, string mediaType)
            : this(content, encoding, new MediaTypeHeaderValue(mediaType ?? DefaultMediaType, (encoding ?? DefaultStringEncoding).WebName))

(https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Http/src/System/Net/Http/StringContent.cs#L47)

Reproduction Steps

Create a unit test project, targeting both .NET6 and 7:

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <TargetFrameworks>net6.0;net7.0</TargetFrameworks>
        <Nullable>enable</Nullable>

        <IsPackable>false</IsPackable>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0"/>
        <PackageReference Include="NUnit" Version="3.13.2"/>
        <PackageReference Include="NUnit3TestAdapter" Version="4.0.0"/>
    </ItemGroup>

</Project>

And add the following test:

public class Tests
{
    [Test]
    public void Test1()
    {
        string mimeType = null;
        var sc = new StringContent("Hello world!", Encoding.Default, mimeType);
        
        Assert.AreEqual(sc.Headers.ContentType.MediaType, "text/plain");
    }
}

From the CLI run dotnet test. The unit test passes for .NET6, but throws an error in .NET7: image

Expected behavior

The behaviour on .NET6 and 7

Actual behavior

The .NET7 version throws an exception:

A total of 1 test files matched the specified pattern.
  Failed Test1 [28 ms]
  Error Message:
   System.ArgumentException : The value cannot be null or empty. (Parameter 'mediaType')
  Stack Trace:
     at System.Net.Http.Headers.MediaTypeHeaderValue.CheckMediaTypeFormat(String mediaType, String parameterName)
   at System.Net.Http.StringContent..ctor(String content, Encoding encoding, String mediaType)
   at StringContentChange.Tests.Test1() in C:\projects\git\StringContentChange\StringContentChange\UnitTest1.cs:line 13
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)

Regression?

Yes, works in .NET6

Known Workarounds

Call the constructor that only takes string content, Encoding encoding parameters.

Configuration

.NET SDK:
 Version:   7.0.102
 Commit:    4bbdd14480

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.19045
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\7.0.102\

Other information

No response

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 3
  • Comments: 27 (17 by maintainers)

Commits related to this issue

Most upvoted comments

This breaks my library RestClient.Net

The tests pass for 3.1 , 5, 6 but fail for 7. You can see the failing test here:

https://github.com/MelbourneDeveloper/RestClient.Net/blob/4395d4a7733b09a33bb59c4b1c09fabba0f6f96f/src/RestClient.Net.UnitTests/MainUnitTests.cs#L621

Fixed for 7.0.x servicing in #83425

@mcintyre321 I believe this fix is scheduled to be in release 7.0.6 (see https://github.com/dotnet/runtime/pull/83425). The latest version is currently 7.0.5.

This bug manifests itself in popular libs like Octokit https://github.com/octokit/octokit.net/issues/2659

@MelbourneDeveloper is the response also covered by the same PR, or would need a new one?

You can workaround the issue by passing "text/plain" instead of null to the StringContent constructor.

@karelz Can confirm that we are encountering this exact issue in my organization in the upgrade to .NET 7 from .NET 6.

@karelz Yes, I have encountered this problem, but after a long time, I can’t remember how I circumvented this problem in the first place.

Triage: Looks like 3 more people ran into it … that would make a solid case for backport as it is a regression. Waiting on confirmation in https://github.com/restsharp/RestSharp/issues/1976#issuecomment-1478353427 from @alexeyzimarev and in https://github.com/dotnet/runtime/pull/81722#issuecomment-1478348845 from @saber-wang and @kevinle-1

I’ve created a PR (https://github.com/dotnet/runtime/pull/81722). Unfortunately it turns out I am not smart* enough to actually get the framework libraries running locally 😵‍💫 , so although I added a unit test I don’t actually know if it passes… it really should though! (I’ll try it on linux at some point to see if I have any more luck on there).

I created the PR against the main branch, but wasn’t sure if it should really have been against the .NET7 branch? Please let me know if so and I can update it.

@stephentoub Yes, you are correct in that if you pass null as the MediaTypeHeaderValue you currently get a null ContentType - this test will fail:

        var sc = new StringContent("", Encoding.Default, (MediaTypeHeaderValue)null);
        Assert.AreEqual("plain/text", sc.Headers.ContentType);

However, that override was not available in .NET6, so it isn’t a breaking change. Agree that makes the API behaviour unexpected though.


*EDIT actually did managed to get the unit tests running in the end and it does pass.