Merge pull request #22453 from dotnet-maestro-bot/merge/release/5.0-preview6-to-master
[automated] Merge branch 'release/5.0-preview6' => 'master'
This commit is contained in:
commit
912ab2bcb3
|
|
@ -0,0 +1,7 @@
|
|||
<Project>
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory)..\, Directory.Build.props))\Directory.Build.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<Nullable>annotations</Nullable>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
</PropertyGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'">
|
||||
<Compile Include="Microsoft.AspNetCore.Http.Extensions.netcoreapp.cs" />
|
||||
<Compile Include="../src/Properties/AssemblyInfo.cs" />
|
||||
<Reference Include="Microsoft.AspNetCore.Http.Abstractions" />
|
||||
<Reference Include="Microsoft.Net.Http.Headers" />
|
||||
<Reference Include="Microsoft.Extensions.FileProviders.Abstractions" />
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@ namespace Microsoft.AspNetCore.Http
|
|||
{
|
||||
public static string GetServerVariable(this Microsoft.AspNetCore.Http.HttpContext context, string variableName) { throw null; }
|
||||
}
|
||||
public static partial class HttpRequestExtensions
|
||||
{
|
||||
public static bool HasJsonContentType(this Microsoft.AspNetCore.Http.HttpRequest request) { throw null; }
|
||||
}
|
||||
public static partial class ResponseExtensions
|
||||
{
|
||||
public static void Clear(this Microsoft.AspNetCore.Http.HttpResponse response) { }
|
||||
|
|
@ -127,3 +131,29 @@ namespace Microsoft.AspNetCore.Http.Headers
|
|||
public void SetList<T>(string name, System.Collections.Generic.IList<T> values) { }
|
||||
}
|
||||
}
|
||||
namespace Microsoft.AspNetCore.Http.Json
|
||||
{
|
||||
public static partial class HttpRequestJsonExtensions
|
||||
{
|
||||
[System.Diagnostics.DebuggerStepThroughAttribute]
|
||||
public static System.Threading.Tasks.ValueTask<object?> ReadFromJsonAsync(this Microsoft.AspNetCore.Http.HttpRequest request, System.Type type, System.Text.Json.JsonSerializerOptions? options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
|
||||
public static System.Threading.Tasks.ValueTask<object?> ReadFromJsonAsync(this Microsoft.AspNetCore.Http.HttpRequest request, System.Type type, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
|
||||
[System.Diagnostics.DebuggerStepThroughAttribute]
|
||||
public static System.Threading.Tasks.ValueTask<TValue> ReadFromJsonAsync<TValue>(this Microsoft.AspNetCore.Http.HttpRequest request, System.Text.Json.JsonSerializerOptions? options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
|
||||
public static System.Threading.Tasks.ValueTask<TValue> ReadFromJsonAsync<TValue>(this Microsoft.AspNetCore.Http.HttpRequest request, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
|
||||
}
|
||||
public static partial class HttpResponseJsonExtensions
|
||||
{
|
||||
public static System.Threading.Tasks.Task WriteAsJsonAsync(this Microsoft.AspNetCore.Http.HttpResponse response, object? value, System.Type type, System.Text.Json.JsonSerializerOptions? options, string? contentType, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
|
||||
public static System.Threading.Tasks.Task WriteAsJsonAsync(this Microsoft.AspNetCore.Http.HttpResponse response, object? value, System.Type type, System.Text.Json.JsonSerializerOptions? options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
|
||||
public static System.Threading.Tasks.Task WriteAsJsonAsync(this Microsoft.AspNetCore.Http.HttpResponse response, object? value, System.Type type, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
|
||||
public static System.Threading.Tasks.Task WriteAsJsonAsync<TValue>(this Microsoft.AspNetCore.Http.HttpResponse response, [System.Diagnostics.CodeAnalysis.AllowNullAttribute] TValue value, System.Text.Json.JsonSerializerOptions? options, string? contentType, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
|
||||
public static System.Threading.Tasks.Task WriteAsJsonAsync<TValue>(this Microsoft.AspNetCore.Http.HttpResponse response, [System.Diagnostics.CodeAnalysis.AllowNullAttribute] TValue value, System.Text.Json.JsonSerializerOptions? options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
|
||||
public static System.Threading.Tasks.Task WriteAsJsonAsync<TValue>(this Microsoft.AspNetCore.Http.HttpResponse response, [System.Diagnostics.CodeAnalysis.AllowNullAttribute] TValue value, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
|
||||
}
|
||||
public partial class JsonOptions
|
||||
{
|
||||
public JsonOptions() { }
|
||||
public System.Text.Json.JsonSerializerOptions SerializerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Microsoft.AspNetCore.Http
|
||||
{
|
||||
public static class HttpRequestExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks the Content-Type header for JSON types.
|
||||
/// </summary>
|
||||
/// <returns>true if the Content-Type header represents a JSON content type; otherwise, false.</returns>
|
||||
public static bool HasJsonContentType(this HttpRequest request)
|
||||
{
|
||||
return request.HasJsonContentType(out _);
|
||||
}
|
||||
|
||||
internal static bool HasJsonContentType(this HttpRequest request, out StringSegment charset)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
}
|
||||
|
||||
if (!MediaTypeHeaderValue.TryParse(request.ContentType, out var mt))
|
||||
{
|
||||
charset = StringSegment.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Matches application/json
|
||||
if (mt.MediaType.Equals(JsonConstants.JsonContentType, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
charset = mt.Charset;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Matches +json, e.g. application/ld+json
|
||||
if (mt.Suffix.Equals("json", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
charset = mt.Charset;
|
||||
return true;
|
||||
}
|
||||
|
||||
charset = StringSegment.Empty;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Microsoft.AspNetCore.Http.Json
|
||||
{
|
||||
public static class HttpRequestJsonExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Read JSON from the request and deserialize to the specified type.
|
||||
/// If the request's content-type is not a known JSON type then an error will be thrown.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">The type of object to read.</typeparam>
|
||||
/// <param name="request">The request to read from.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the operation.</param>
|
||||
/// <returns>The task object representing the asynchronous operation.</returns>
|
||||
public static ValueTask<TValue> ReadFromJsonAsync<TValue>(
|
||||
this HttpRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return request.ReadFromJsonAsync<TValue>(options: null, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read JSON from the request and deserialize to the specified type.
|
||||
/// If the request's content-type is not a known JSON type then an error will be thrown.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">The type of object to read.</typeparam>
|
||||
/// <param name="request">The request to read from.</param>
|
||||
/// <param name="options">The serializer options use when deserializing the content.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the operation.</param>
|
||||
/// <returns>The task object representing the asynchronous operation.</returns>
|
||||
public static async ValueTask<TValue> ReadFromJsonAsync<TValue>(
|
||||
this HttpRequest request,
|
||||
JsonSerializerOptions? options,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
}
|
||||
|
||||
if (!request.HasJsonContentType(out var charset))
|
||||
{
|
||||
throw CreateContentTypeError(request);
|
||||
}
|
||||
|
||||
options ??= ResolveSerializerOptions(request.HttpContext);
|
||||
|
||||
var encoding = GetEncodingFromCharset(charset);
|
||||
var (inputStream, usesTranscodingStream) = GetInputStream(request.HttpContext, encoding);
|
||||
|
||||
try
|
||||
{
|
||||
return await JsonSerializer.DeserializeAsync<TValue>(inputStream, options, cancellationToken);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (usesTranscodingStream)
|
||||
{
|
||||
await inputStream.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read JSON from the request and deserialize to the specified type.
|
||||
/// If the request's content-type is not a known JSON type then an error will be thrown.
|
||||
/// </summary>
|
||||
/// <param name="request">The request to read from.</param>
|
||||
/// <param name="type">The type of object to read.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the operation.</param>
|
||||
/// <returns>The task object representing the asynchronous operation.</returns>
|
||||
public static ValueTask<object?> ReadFromJsonAsync(
|
||||
this HttpRequest request,
|
||||
Type type,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return request.ReadFromJsonAsync(type, options: null, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read JSON from the request and deserialize to the specified type.
|
||||
/// If the request's content-type is not a known JSON type then an error will be thrown.
|
||||
/// </summary>
|
||||
/// <param name="request">The request to read from.</param>
|
||||
/// <param name="type">The type of object to read.</param>
|
||||
/// <param name="options">The serializer options use when deserializing the content.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the operation.</param>
|
||||
/// <returns>The task object representing the asynchronous operation.</returns>
|
||||
public static async ValueTask<object?> ReadFromJsonAsync(
|
||||
this HttpRequest request,
|
||||
Type type,
|
||||
JsonSerializerOptions? options,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
}
|
||||
if (type == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
}
|
||||
|
||||
if (!request.HasJsonContentType(out var charset))
|
||||
{
|
||||
throw CreateContentTypeError(request);
|
||||
}
|
||||
|
||||
options ??= ResolveSerializerOptions(request.HttpContext);
|
||||
|
||||
var encoding = GetEncodingFromCharset(charset);
|
||||
var (inputStream, usesTranscodingStream) = GetInputStream(request.HttpContext, encoding);
|
||||
|
||||
try
|
||||
{
|
||||
return await JsonSerializer.DeserializeAsync(inputStream, type, options, cancellationToken);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (usesTranscodingStream)
|
||||
{
|
||||
await inputStream.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static JsonSerializerOptions ResolveSerializerOptions(HttpContext httpContext)
|
||||
{
|
||||
// Attempt to resolve options from DI then fallback to default options
|
||||
return httpContext.RequestServices?.GetService<IOptions<JsonOptions>>()?.Value?.SerializerOptions ?? JsonOptions.DefaultSerializerOptions;
|
||||
}
|
||||
|
||||
private static InvalidOperationException CreateContentTypeError(HttpRequest request)
|
||||
{
|
||||
return new InvalidOperationException($"Unable to read the request as JSON because the request content type '{request.ContentType}' is not a known JSON content type.");
|
||||
}
|
||||
|
||||
private static (Stream inputStream, bool usesTranscodingStream) GetInputStream(HttpContext httpContext, Encoding? encoding)
|
||||
{
|
||||
if (encoding == null || encoding.CodePage == Encoding.UTF8.CodePage)
|
||||
{
|
||||
return (httpContext.Request.Body, false);
|
||||
}
|
||||
|
||||
var inputStream = Encoding.CreateTranscodingStream(httpContext.Request.Body, encoding, Encoding.UTF8, leaveOpen: true);
|
||||
return (inputStream, true);
|
||||
}
|
||||
|
||||
private static Encoding? GetEncodingFromCharset(StringSegment charset)
|
||||
{
|
||||
if (charset.Equals("utf-8", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// This is an optimization for utf-8 that prevents the Substring caused by
|
||||
// charset.Value
|
||||
return Encoding.UTF8;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// charset.Value might be an invalid encoding name as in charset=invalid.
|
||||
return charset.HasValue ? Encoding.GetEncoding(charset.Value) : null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to read the request as JSON because the request content type charset '{charset}' is not a known encoding.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Microsoft.AspNetCore.Http.Json
|
||||
{
|
||||
public static partial class HttpResponseJsonExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Write the specified value as JSON to the response body. The response content-type will be set to
|
||||
/// <c>application/json; charset=utf-8</c> and the status code set to <c>200</c>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">The type of object to write.</typeparam>
|
||||
/// <param name="response">The response to write JSON to.</param>
|
||||
/// <param name="value">The value to write as JSON.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the operation.</param>
|
||||
/// <returns>The task object representing the asynchronous operation.</returns>
|
||||
public static Task WriteAsJsonAsync<TValue>(
|
||||
this HttpResponse response,
|
||||
[AllowNull] TValue value,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return response.WriteAsJsonAsync<TValue>(value, options: null, contentType: null, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the specified value as JSON to the response body. The response content-type will be set to
|
||||
/// <c>application/json; charset=utf-8</c> and the status code set to <c>200</c>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">The type of object to write.</typeparam>
|
||||
/// <param name="response">The response to write JSON to.</param>
|
||||
/// <param name="value">The value to write as JSON.</param>
|
||||
/// <param name="options">The serializer options use when serializing the value.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the operation.</param>
|
||||
/// <returns>The task object representing the asynchronous operation.</returns>
|
||||
public static Task WriteAsJsonAsync<TValue>(
|
||||
this HttpResponse response,
|
||||
[AllowNull] TValue value,
|
||||
JsonSerializerOptions? options,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return response.WriteAsJsonAsync<TValue>(value, options, contentType: null, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the specified value as JSON to the response body. The response content-type will be set to
|
||||
/// the specified content-type and the status code set to <c>200</c>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">The type of object to write.</typeparam>
|
||||
/// <param name="response">The response to write JSON to.</param>
|
||||
/// <param name="value">The value to write as JSON.</param>
|
||||
/// <param name="options">The serializer options use when serializing the value.</param>
|
||||
/// <param name="contentType">The content-type to set on the response.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the operation.</param>
|
||||
/// <returns>The task object representing the asynchronous operation.</returns>
|
||||
public static Task WriteAsJsonAsync<TValue>(
|
||||
this HttpResponse response,
|
||||
[AllowNull] TValue value,
|
||||
JsonSerializerOptions? options,
|
||||
string? contentType,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (response == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(response));
|
||||
}
|
||||
|
||||
options ??= ResolveSerializerOptions(response.HttpContext);
|
||||
|
||||
response.ContentType = contentType ?? JsonConstants.JsonContentTypeWithCharset;
|
||||
response.StatusCode = StatusCodes.Status200OK;
|
||||
return JsonSerializer.SerializeAsync<TValue>(response.Body, value!, options, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the specified value as JSON to the response body. The response content-type will be set to
|
||||
/// <c>application/json; charset=utf-8</c> and the status code set to <c>200</c>.
|
||||
/// </summary>
|
||||
/// <param name="response">The response to write JSON to.</param>
|
||||
/// <param name="value">The value to write as JSON.</param>
|
||||
/// <param name="type">The type of object to write.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the operation.</param>
|
||||
/// <returns>The task object representing the asynchronous operation.</returns>
|
||||
public static Task WriteAsJsonAsync(
|
||||
this HttpResponse response,
|
||||
object? value,
|
||||
Type type,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return response.WriteAsJsonAsync(value, type, options: null, contentType: null, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the specified value as JSON to the response body. The response content-type will be set to
|
||||
/// <c>application/json; charset=utf-8</c> and the status code set to <c>200</c>.
|
||||
/// </summary>
|
||||
/// <param name="response">The response to write JSON to.</param>
|
||||
/// <param name="value">The value to write as JSON.</param>
|
||||
/// <param name="type">The type of object to write.</param>
|
||||
/// <param name="options">The serializer options use when serializing the value.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the operation.</param>
|
||||
/// <returns>The task object representing the asynchronous operation.</returns>
|
||||
public static Task WriteAsJsonAsync(
|
||||
this HttpResponse response,
|
||||
object? value,
|
||||
Type type,
|
||||
JsonSerializerOptions? options,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return response.WriteAsJsonAsync(value, type, options, contentType: null, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the specified value as JSON to the response body. The response content-type will be set to
|
||||
/// the specified content-type and the status code set to <c>200</c>.
|
||||
/// </summary>
|
||||
/// <param name="response">The response to write JSON to.</param>
|
||||
/// <param name="value">The value to write as JSON.</param>
|
||||
/// <param name="type">The type of object to write.</param>
|
||||
/// <param name="options">The serializer options use when serializing the value.</param>
|
||||
/// <param name="contentType">The content-type to set on the response.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the operation.</param>
|
||||
/// <returns>The task object representing the asynchronous operation.</returns>
|
||||
public static Task WriteAsJsonAsync(
|
||||
this HttpResponse response,
|
||||
object? value,
|
||||
Type type,
|
||||
JsonSerializerOptions? options,
|
||||
string? contentType,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (response == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(response));
|
||||
}
|
||||
if (type == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
}
|
||||
|
||||
options ??= ResolveSerializerOptions(response.HttpContext);
|
||||
|
||||
response.ContentType = contentType ?? JsonConstants.JsonContentTypeWithCharset;
|
||||
response.StatusCode = StatusCodes.Status200OK;
|
||||
return JsonSerializer.SerializeAsync(response.Body, value, type, options, cancellationToken);
|
||||
}
|
||||
|
||||
private static JsonSerializerOptions ResolveSerializerOptions(HttpContext httpContext)
|
||||
{
|
||||
// Attempt to resolve options from DI then fallback to default options
|
||||
return httpContext.RequestServices?.GetService<IOptions<JsonOptions>>()?.Value?.SerializerOptions ?? JsonOptions.DefaultSerializerOptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.Http
|
||||
{
|
||||
internal static class JsonConstants
|
||||
{
|
||||
public const string JsonContentType = "application/json";
|
||||
public const string JsonContentTypeWithCharset = "application/json; charset=utf-8";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Json;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Microsoft.AspNetCore.Http.Json
|
||||
{
|
||||
public class JsonOptions
|
||||
{
|
||||
internal static readonly JsonSerializerOptions DefaultSerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web)
|
||||
{
|
||||
// Web defaults don't use the relex JSON escaping encoder.
|
||||
//
|
||||
// Because these options are for producing content that is written directly to the request
|
||||
// (and not embedded in an HTML page for example), we can use UnsafeRelaxedJsonEscaping.
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||
};
|
||||
|
||||
// Use a copy so the defaults are not modified.
|
||||
public JsonSerializerOptions SerializerOptions { get; } = new JsonSerializerOptions(DefaultSerializerOptions);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Http.Extensions.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Xunit;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Microsoft.AspNetCore.Http.Extensions.Tests
|
||||
{
|
||||
public class HttpRequestExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(null, false)]
|
||||
[InlineData("", false)]
|
||||
[InlineData("application/xml", false)]
|
||||
[InlineData("text/json", false)]
|
||||
[InlineData("text/json; charset=utf-8", false)]
|
||||
[InlineData("application/json", true)]
|
||||
[InlineData("application/json; charset=utf-8", true)]
|
||||
[InlineData("application/ld+json", true)]
|
||||
[InlineData("APPLICATION/JSON", true)]
|
||||
[InlineData("APPLICATION/JSON; CHARSET=UTF-8", true)]
|
||||
[InlineData("APPLICATION/LD+JSON", true)]
|
||||
public void HasJsonContentType(string contentType, bool hasJsonContentType)
|
||||
{
|
||||
var request = new DefaultHttpContext().Request;
|
||||
request.ContentType = contentType;
|
||||
|
||||
Assert.Equal(hasJsonContentType, request.HasJsonContentType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http.Json;
|
||||
using Xunit;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Microsoft.AspNetCore.Http.Extensions.Tests
|
||||
{
|
||||
public class HttpRequestJsonExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task ReadFromJsonAsyncGeneric_NonJsonContentType_ThrowError()
|
||||
{
|
||||
// Arrange
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.ContentType = "text/json";
|
||||
|
||||
// Act
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(async () => await context.Request.ReadFromJsonAsync<int>());
|
||||
|
||||
// Assert
|
||||
var exceptedMessage = $"Unable to read the request as JSON because the request content type 'text/json' is not a known JSON content type.";
|
||||
Assert.Equal(exceptedMessage, ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadFromJsonAsyncGeneric_NoBodyContent_ThrowError()
|
||||
{
|
||||
// Arrange
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.ContentType = "application/json";
|
||||
|
||||
// Act
|
||||
var ex = await Assert.ThrowsAsync<JsonException>(async () => await context.Request.ReadFromJsonAsync<int>());
|
||||
|
||||
// Assert
|
||||
var exceptedMessage = $"The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0.";
|
||||
Assert.Equal(exceptedMessage, ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadFromJsonAsyncGeneric_ValidBodyContent_ReturnValue()
|
||||
{
|
||||
// Arrange
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.ContentType = "application/json";
|
||||
context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("1"));
|
||||
|
||||
// Act
|
||||
var result = await context.Request.ReadFromJsonAsync<int>();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadFromJsonAsyncGeneric_WithOptions_ReturnValue()
|
||||
{
|
||||
// Arrange
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.ContentType = "application/json";
|
||||
context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("[1,2,]"));
|
||||
|
||||
var options = new JsonSerializerOptions();
|
||||
options.AllowTrailingCommas = true;
|
||||
|
||||
// Act
|
||||
var result = await context.Request.ReadFromJsonAsync<List<int>>(options);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result,
|
||||
i => Assert.Equal(1, i),
|
||||
i => Assert.Equal(2, i));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadFromJsonAsyncGeneric_Utf8Encoding_ReturnValue()
|
||||
{
|
||||
// Arrange
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.ContentType = "application/json; charset=utf-8";
|
||||
context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("[1,2]"));
|
||||
|
||||
// Act
|
||||
var result = await context.Request.ReadFromJsonAsync<List<int>>();
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result,
|
||||
i => Assert.Equal(1, i),
|
||||
i => Assert.Equal(2, i));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadFromJsonAsyncGeneric_Utf16Encoding_ReturnValue()
|
||||
{
|
||||
// Arrange
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.ContentType = "application/json; charset=utf-16";
|
||||
context.Request.Body = new MemoryStream(Encoding.Unicode.GetBytes(@"{""name"": ""激光這兩個字是甚麼意思""}"));
|
||||
|
||||
// Act
|
||||
var result = await context.Request.ReadFromJsonAsync<Dictionary<string, string>>();
|
||||
|
||||
// Assert
|
||||
Assert.Equal("激光這兩個字是甚麼意思", result["name"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadFromJsonAsyncGeneric_WithCancellationToken_CancellationRaised()
|
||||
{
|
||||
// Arrange
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.ContentType = "application /json";
|
||||
context.Request.Body = new TestStream();
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
// Act
|
||||
var readTask = context.Request.ReadFromJsonAsync<List<int>>(cts.Token);
|
||||
Assert.False(readTask.IsCompleted);
|
||||
|
||||
cts.Cancel();
|
||||
|
||||
// Assert
|
||||
await Assert.ThrowsAsync<TaskCanceledException>(async () => await readTask);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadFromJsonAsyncGeneric_InvalidEncoding_ThrowError()
|
||||
{
|
||||
// Arrange
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.ContentType = "application/json; charset=invalid";
|
||||
|
||||
// Act
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(async () => await context.Request.ReadFromJsonAsync<object>());
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Unable to read the request as JSON because the request content type charset 'invalid' is not a known encoding.", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadFromJsonAsync_ValidBodyContent_ReturnValue()
|
||||
{
|
||||
// Arrange
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.ContentType = "application/json";
|
||||
context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("1"));
|
||||
|
||||
// Act
|
||||
var result = (int?)await context.Request.ReadFromJsonAsync(typeof(int));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadFromJsonAsync_Utf16Encoding_ReturnValue()
|
||||
{
|
||||
// Arrange
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.ContentType = "application/json; charset=utf-16";
|
||||
context.Request.Body = new MemoryStream(Encoding.Unicode.GetBytes(@"{""name"": ""激光這兩個字是甚麼意思""}"));
|
||||
|
||||
// Act
|
||||
var result = (Dictionary<string, string>?)await context.Request.ReadFromJsonAsync(typeof(Dictionary<string, string>));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("激光這兩個字是甚麼意思", result!["name"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadFromJsonAsync_InvalidEncoding_ThrowError()
|
||||
{
|
||||
// Arrange
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.ContentType = "application/json; charset=invalid";
|
||||
|
||||
// Act
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(async () => await context.Request.ReadFromJsonAsync(typeof(object)));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("Unable to read the request as JSON because the request content type charset 'invalid' is not a known encoding.", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadFromJsonAsync_WithOptions_ReturnValue()
|
||||
{
|
||||
// Arrange
|
||||
var context = new DefaultHttpContext();
|
||||
context.Request.ContentType = "application/json";
|
||||
context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("[1,2,]"));
|
||||
|
||||
var options = new JsonSerializerOptions();
|
||||
options.AllowTrailingCommas = true;
|
||||
|
||||
// Act
|
||||
var result = (List<int>?)await context.Request.ReadFromJsonAsync(typeof(List<int>), options);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(result,
|
||||
i => Assert.Equal(1, i),
|
||||
i => Assert.Equal(2, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,279 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http.Json;
|
||||
using Xunit;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Microsoft.AspNetCore.Http.Extensions.Tests
|
||||
{
|
||||
public class HttpResponseJsonExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task WriteAsJsonAsyncGeneric_SimpleValue_JsonResponse()
|
||||
{
|
||||
// Arrange
|
||||
var body = new MemoryStream();
|
||||
var context = new DefaultHttpContext();
|
||||
context.Response.Body = body;
|
||||
|
||||
// Act
|
||||
await context.Response.WriteAsJsonAsync(1);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(JsonConstants.JsonContentTypeWithCharset, context.Response.ContentType);
|
||||
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
|
||||
|
||||
var data = body.ToArray();
|
||||
Assert.Collection(data, b => Assert.Equal((byte)'1', b));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAsJsonAsyncGeneric_NullValue_JsonResponse()
|
||||
{
|
||||
// Arrange
|
||||
var body = new MemoryStream();
|
||||
var context = new DefaultHttpContext();
|
||||
context.Response.Body = body;
|
||||
|
||||
// Act
|
||||
await context.Response.WriteAsJsonAsync<Uri>(value: null);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(JsonConstants.JsonContentTypeWithCharset, context.Response.ContentType);
|
||||
|
||||
var data = Encoding.UTF8.GetString(body.ToArray());
|
||||
Assert.Equal("null", data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAsJsonAsyncGeneric_WithOptions_JsonResponse()
|
||||
{
|
||||
// Arrange
|
||||
var body = new MemoryStream();
|
||||
var context = new DefaultHttpContext();
|
||||
context.Response.Body = body;
|
||||
|
||||
// Act
|
||||
var options = new JsonSerializerOptions();
|
||||
options.Converters.Add(new IntegerConverter());
|
||||
await context.Response.WriteAsJsonAsync(new int[] { 1, 2, 3 }, options);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(JsonConstants.JsonContentTypeWithCharset, context.Response.ContentType);
|
||||
|
||||
var data = Encoding.UTF8.GetString(body.ToArray());
|
||||
Assert.Equal("[false,true,false]", data);
|
||||
}
|
||||
|
||||
private class IntegerConverter : JsonConverter<int>
|
||||
{
|
||||
public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteBooleanValue(value % 2 == 0);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAsJsonAsyncGeneric_WithContentType_JsonResponseWithCustomContentType()
|
||||
{
|
||||
// Arrange
|
||||
var body = new MemoryStream();
|
||||
var context = new DefaultHttpContext();
|
||||
context.Response.Body = body;
|
||||
|
||||
// Act
|
||||
await context.Response.WriteAsJsonAsync(1, options: null, contentType: "application/custom-type");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("application/custom-type", context.Response.ContentType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAsJsonAsyncGeneric_WithCancellationToken_CancellationRaised()
|
||||
{
|
||||
// Arrange
|
||||
var context = new DefaultHttpContext();
|
||||
context.Response.Body = new TestStream();
|
||||
|
||||
var cts = new CancellationTokenSource();
|
||||
|
||||
// Act
|
||||
var writeTask = context.Response.WriteAsJsonAsync(1, cts.Token);
|
||||
Assert.False(writeTask.IsCompleted);
|
||||
|
||||
cts.Cancel();
|
||||
|
||||
// Assert
|
||||
await Assert.ThrowsAsync<TaskCanceledException>(async () => await writeTask);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAsJsonAsyncGeneric_ObjectWithStrings_CamcelCaseAndNotEscaped()
|
||||
{
|
||||
// Arrange
|
||||
var body = new MemoryStream();
|
||||
var context = new DefaultHttpContext();
|
||||
context.Response.Body = body;
|
||||
var value = new TestObject
|
||||
{
|
||||
StringProperty = "激光這兩個字是甚麼意思"
|
||||
};
|
||||
|
||||
// Act
|
||||
await context.Response.WriteAsJsonAsync(value);
|
||||
|
||||
// Assert
|
||||
var data = Encoding.UTF8.GetString(body.ToArray());
|
||||
Assert.Equal(@"{""stringProperty"":""激光這兩個字是甚麼意思""}", data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAsJsonAsync_SimpleValue_JsonResponse()
|
||||
{
|
||||
// Arrange
|
||||
var body = new MemoryStream();
|
||||
var context = new DefaultHttpContext();
|
||||
context.Response.Body = body;
|
||||
|
||||
// Act
|
||||
await context.Response.WriteAsJsonAsync(1, typeof(int));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(JsonConstants.JsonContentTypeWithCharset, context.Response.ContentType);
|
||||
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
|
||||
|
||||
var data = body.ToArray();
|
||||
Assert.Collection(data, b => Assert.Equal((byte)'1', b));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAsJsonAsync_NullValue_JsonResponse()
|
||||
{
|
||||
// Arrange
|
||||
var body = new MemoryStream();
|
||||
var context = new DefaultHttpContext();
|
||||
context.Response.Body = body;
|
||||
|
||||
// Act
|
||||
await context.Response.WriteAsJsonAsync(value: null, typeof(int?));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(JsonConstants.JsonContentTypeWithCharset, context.Response.ContentType);
|
||||
|
||||
var data = Encoding.UTF8.GetString(body.ToArray());
|
||||
Assert.Equal("null", data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAsJsonAsync_NullType_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var body = new MemoryStream();
|
||||
var context = new DefaultHttpContext();
|
||||
context.Response.Body = body;
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(async () => await context.Response.WriteAsJsonAsync(value: null, type: null!));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAsJsonAsync_NullResponse_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var body = new MemoryStream();
|
||||
var context = new DefaultHttpContext();
|
||||
context.Response.Body = body;
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(async () => await HttpResponseJsonExtensions.WriteAsJsonAsync(response: null!, value: null, typeof(int?)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WriteAsJsonAsync_ObjectWithStrings_CamcelCaseAndNotEscaped()
|
||||
{
|
||||
// Arrange
|
||||
var body = new MemoryStream();
|
||||
var context = new DefaultHttpContext();
|
||||
context.Response.Body = body;
|
||||
var value = new TestObject
|
||||
{
|
||||
StringProperty = "激光這兩個字是甚麼意思"
|
||||
};
|
||||
|
||||
// Act
|
||||
await context.Response.WriteAsJsonAsync(value, typeof(TestObject));
|
||||
|
||||
// Assert
|
||||
var data = Encoding.UTF8.GetString(body.ToArray());
|
||||
Assert.Equal(@"{""stringProperty"":""激光這兩個字是甚麼意思""}", data);
|
||||
}
|
||||
|
||||
public class TestObject
|
||||
{
|
||||
public string? StringProperty { get; set; }
|
||||
}
|
||||
|
||||
private class TestStream : Stream
|
||||
{
|
||||
public override bool CanRead { get; }
|
||||
public override bool CanSeek { get; }
|
||||
public override bool CanWrite { get; }
|
||||
public override long Length { get; }
|
||||
public override long Position { get; set; }
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<int>();
|
||||
cancellationToken.Register(s => ((TaskCompletionSource<int>)s!).SetCanceled(), tcs);
|
||||
return new ValueTask<int>(tcs.Task);
|
||||
}
|
||||
|
||||
public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<int>();
|
||||
cancellationToken.Register(s => ((TaskCompletionSource<int>)s!).SetCanceled(), tcs);
|
||||
return new ValueTask(tcs.Task);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved. See License.txt in the project root for license information.
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.IO;
|
||||
using System.IO.Pipelines;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Http.Extensions.Tests
|
||||
{
|
||||
public class TestStream : Stream
|
||||
{
|
||||
public override bool CanRead { get; }
|
||||
public override bool CanSeek { get; }
|
||||
public override bool CanWrite { get; }
|
||||
public override long Length { get; }
|
||||
public override long Position { get; set; }
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<int>();
|
||||
cancellationToken.Register(s => ((TaskCompletionSource<int>)s).SetCanceled(), tcs);
|
||||
return new ValueTask<int>(tcs.Task);
|
||||
}
|
||||
|
||||
public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<int>();
|
||||
cancellationToken.Register(s => ((TaskCompletionSource<int>)s).SetCanceled(), tcs);
|
||||
return new ValueTask(tcs.Task);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue