| File: Forms\ClientValidationDataWriter.cs | Web Access |
| Project: src\aspnetcore\src\Components\Endpoints\src\Microsoft.AspNetCore.Components.Endpoints.csproj (Microsoft.AspNetCore.Components.Endpoints) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; using System.Buffers.Text; using System.Text; using System.Text.Json; namespace Microsoft.AspNetCore.Components.Endpoints.Forms; internal sealed class ClientValidationDataWriter { private readonly ArrayBufferWriter<byte> _buffer; private readonly Utf8JsonWriter _writer; private string? _pendingFieldName; private bool _fieldOpen; private bool _paramsOpen; private bool _anyField; public ClientValidationDataWriter() { _buffer = new ArrayBufferWriter<byte>(initialCapacity: 256); _writer = new Utf8JsonWriter(_buffer); _writer.WriteStartObject(); _writer.WritePropertyName("fields"u8); _writer.WriteStartArray(); } // Records the field name. The field object is only written once its first rule arrives, so a // field with no rules produces no output. public void BeginField(string name) { _pendingFieldName = name; _fieldOpen = false; } // Starts a rule whose name is a compile-time UTF-8 literal (built-in rules). public void BeginRule(ReadOnlySpan<byte> nameUtf8, string message) { EnsureFieldOpen(); _writer.WriteStartObject(); _writer.WriteString("name"u8, nameUtf8); _writer.WriteString("message"u8, message); _paramsOpen = false; } // Starts a rule whose name comes from a user-defined adapter. public void BeginRule(string name, string message) { EnsureFieldOpen(); _writer.WriteStartObject(); _writer.WriteString("name"u8, name); _writer.WriteString("message"u8, message); _paramsOpen = false; } public void Param(ReadOnlySpan<byte> keyUtf8, string value) { EnsureParamsOpen(); _writer.WriteString(keyUtf8, value); } // Writes a numeric parameter as a string (all params are strings on the wire) without // allocating an intermediate string. public void Param(ReadOnlySpan<byte> keyUtf8, int value) { EnsureParamsOpen(); Span<byte> utf8Value = stackalloc byte[16]; Utf8Formatter.TryFormat(value, utf8Value, out var bytesWritten); _writer.WriteString(keyUtf8, utf8Value[..bytesWritten]); } public void Param(string key, string value) { EnsureParamsOpen(); _writer.WriteString(key, value); } public void EndRule() { if (_paramsOpen) { _writer.WriteEndObject(); // params _paramsOpen = false; } _writer.WriteEndObject(); // rule } public void EndField() { if (_fieldOpen) { _writer.WriteEndArray(); // rules _writer.WriteEndObject(); // field _fieldOpen = false; } _pendingFieldName = null; } // Finishes the document. Returns null when no field produced any rule, so the caller can skip // emitting the carrier element entirely. public string? Complete() { _writer.WriteEndArray(); // fields _writer.WriteEndObject(); // root _writer.Flush(); // Utf8JsonWriter's default encoder escapes <, >, &, ' - safe for HTML attribute/text content. var result = _anyField ? Encoding.UTF8.GetString(_buffer.WrittenSpan) : null; _writer.Dispose(); return result; } private void EnsureFieldOpen() { if (!_fieldOpen) { _writer.WriteStartObject(); _writer.WriteString("name"u8, _pendingFieldName!); _writer.WritePropertyName("rules"u8); _writer.WriteStartArray(); _fieldOpen = true; _anyField = true; } } private void EnsureParamsOpen() { if (!_paramsOpen) { _writer.WritePropertyName("params"u8); _writer.WriteStartObject(); _paramsOpen = true; } } }