in src/Core/Services/RequestValidator.cs [351:425]
public void ValidateUpsertRequestContext(UpsertRequestContext upsertRequestCtx)
{
ISqlMetadataProvider sqlMetadataProvider = GetSqlMetadataProvider(upsertRequestCtx.EntityName);
IEnumerable<string> fieldsInRequestBody = upsertRequestCtx.FieldValuePairsInBody.Keys;
bool isRequestBodyStrict = IsRequestBodyStrict();
SourceDefinition sourceDefinition = TryGetSourceDefinition(upsertRequestCtx.EntityName, sqlMetadataProvider);
// Each field that is checked against the DB schema is removed
// from the hash set of unvalidated fields.
// At the end, if we end up with extraneous unvalidated fields, we throw error.
HashSet<string> unValidatedFields = new(fieldsInRequestBody);
foreach (KeyValuePair<string, ColumnDefinition> column in sourceDefinition.Columns)
{
// if column is not exposed we skip
if (!sqlMetadataProvider.TryGetExposedColumnName(
entityName: upsertRequestCtx.EntityName,
backingFieldName: column.Key,
out string? exposedName))
{
continue;
}
if (upsertRequestCtx.FieldValuePairsInBody.ContainsKey(exposedName!) && column.Value.IsReadOnly)
{
if (isRequestBodyStrict)
{
throw new DataApiBuilderException(
message: $"Field '{exposedName}' cannot be included in the request body.",
statusCode: HttpStatusCode.BadRequest,
subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest);
}
unValidatedFields.Remove(exposedName!);
}
// Primary Key(s) should not be present in the request body. We do not fail a request
// if a PK is autogenerated here, because an UPSERT request may only need to update a
// record. If an insert occurs on a table with autogenerated primary key,
// a database error will be returned.
if (sourceDefinition.PrimaryKey.Contains(column.Key))
{
continue;
}
// Request body must have value defined for included non-nullable columns
if (!column.Value.IsNullable && fieldsInRequestBody.Contains(exposedName))
{
if (upsertRequestCtx.FieldValuePairsInBody[exposedName!] is null)
{
throw new DataApiBuilderException(
message: $"Invalid value for field {exposedName} in request body.",
statusCode: HttpStatusCode.BadRequest,
subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest);
}
}
bool isReplacementUpdate = (upsertRequestCtx.OperationType == EntityActionOperation.Upsert) ? true : false;
if (ValidateColumn(column.Value, exposedName!, fieldsInRequestBody, isReplacementUpdate, isRequestBodyStrict))
{
unValidatedFields.Remove(exposedName!);
}
}
// There may be unvalidated fields remaining because of extraneous fields in request body
// which are not mapped to the table. We throw an exception only when we operate in strict mode,
// i.e. when extraneous fields are not allowed.
if (unValidatedFields.Any() && isRequestBodyStrict)
{
throw new DataApiBuilderException(
message: "Invalid request body. Either insufficient or extra fields supplied.",
statusCode: HttpStatusCode.BadRequest,
subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest);
}
}