Есть ли способ прочитать тело запроса непосредственно в действии без промежуточного программного обеспечения?C#

Место общения программистов C#
Anonymous
Есть ли способ прочитать тело запроса непосредственно в действии без промежуточного программного обеспечения?

Сообщение Anonymous »

Я хочу иметь возможность читать тело запроса в действии, одновременно используя поведение MVC по умолчанию, которое включает публикацию с параметром [FromBody]. Насколько я знаю, для этого мне нужно промежуточное программное обеспечение. Мои действия:

Код: Выделить всё

[HttpPost]
public async Task ValidateTestFormAsync([FromBody] ValidationRequest
 personValidationRequest)
{
Request.EnableBuffering();
Request.Body.Seek(0, SeekOrigin.Begin);
string requestContent;
using (var reader = new StreamReader(Request.Body, Encoding.UTF8, true, 1024, true))
{
requestContent = await reader.ReadToEndAsync();
}

return Json(requestContent);
}
Это не работает, и requestContent всегда имеет значение null, поскольку, как только запрос уже находится внутри действия, поток тела запроса был прочитан и не может быть прочитан снова. Если я удалю [FromBody], это сработает. Я знаю, что могу написать промежуточное ПО в файле program.cs вот так:

Код: Выделить всё

app.Use((context, next) =>
{
context.Request.EnableBuffering();
return next();
});
Но каковы же соображения по этому поводу? Я не хочу, чтобы каждый запрос был буферизуемым, и я все равно хочу передать модель через [FromBody], чтобы придерживаться стандарта MVC.
По сути, я просто пытаюсь запустить пользовательская проверка, при которой я удаляю любые сообщения об ошибках для полей/свойств, которые не были отправлены в сообщении, поскольку MVC всегда проверяет полную модель, и я пытаюсь выполнить POST поле за полем, чтобы объединить их с клиентской библиотекой, которую я пишу.< /p>
РЕДАКТИРОВАТЬ:
Я пытался сфокусировать этот вопрос, но вот полный объем:

Код: Выделить всё

[HttpPost]
public async Task ValidateTestFormAsync([FromBody] ValidationRequest
 personValidationRequest)
{
return Json(personValidationRequest.ToValidationResponse(ModelState));
}

public static class ValidationResponeFormatter
{
/// 
///  Takes a validation request of a model along with the current validated modelState and performs validation on the entire model including
///  calling .Validate (nullifying the default MVC short circuiting). TBD: Remove validation errors that weren't actually posted.
/// 
/// The model of the data to be validated, which has either validation attributes, a custom validation method via implementing
/// IValidatableObject, or both.
/// The validation request formatted as required for svl to use.
/// The model state supplied from the controller action.
/// 
public static ValidationResponse ToValidationResponse(this ValidationRequest request, ModelStateDictionary modelState) where T : class
{
var response = new ValidationResponse
{
Request = request
};

if(modelState.IsValid)
{
return response;
}

foreach (var validationError in modelState)
{
//trims the first nested key since the keys will always be pre-pended with Fields.  due to the nesting of the model inside a ValidationRequest
var responseError = new FieldValidationError() {  Field = validationError.Key.Split('.')[1] };

foreach (var error in validationError.Value.Errors)
{
if (!string.IsNullOrEmpty(error.ErrorMessage))
{
responseError.Failures.Add(error.ErrorMessage);
}
if (error.Exception != null)
{
responseError.Failures.AddRange(error.Exception.GetNestedExceptions());
}
}

response.Errors.Add(responseError);
}
//Default validation short circuits if any validation attributes fail, the modelState would then only have attribute validation errors.
//The below block manually includes any custom validations, nullifying that short circuiting.
if (typeof(T).GetInterface(nameof(IValidatableObject)) != null)
{
var validationContext = new ValidationContext(request.Fields);
var manualValidations = ((IValidatableObject)request.Fields).Validate(validationContext).ToList();

foreach (var validatedField in manualValidations)
{
//get error for only the first member name, otherwise the same error would be shown for two fields.
var memberName = validatedField.MemberNames.FirstOrDefault();
var existingFieldError = response.Errors.FirstOrDefault(x => x.Field == memberName);
if(existingFieldError == null)
{
//all errors not assigned to a specific property/field get categorized under a _default area
//to avoid this be sure to declare a membername when yielding ValidationResults in your custom Validate method
existingFieldError = new FieldValidationError() { Field = memberName ?? "_default" };
response.Errors.Add(existingFieldError);
}

//adds the validation message if it doesn't already exist, which is possible if the default model validation did not encounter
//any attribute validation failures, then the custom validate method would have already run
if(existingFieldError.Failures.All(x => x != validatedField.ErrorMessage) && !string.IsNullOrEmpty(validatedField.ErrorMessage))
{
existingFieldError.Failures.Add(validatedField.ErrorMessage);
}
}
}

return response;
}

public static IEnumerable GetNestedExceptions(this Exception ex)
{
if (ex == null)
{
throw new ArgumentNullException("GetNestedExceptions exception parameter is null");
}

var innerException = ex;
do
{
yield return innerException.Message;
innerException = innerException.InnerException;
}
while (innerException != null);
}
}
ValidationRequest/ValidationResponse — это просто пользовательские классы, отформатированные таким образом, что моя клиентская библиотека выполняет POSTing/способна получать ответ. Я аннулирую короткое замыкание логики MVC по умолчанию «если проверка атрибута не удалась, пропустите вызов .Validate()», но я хочу удалить все ошибки из моего ValidationResponse для полей, которые фактически не были опубликованы. Я думал, что чтение request.body может сделать это, но это оказалось более обременительным, чем я хотел (например, регистрация промежуточного программного обеспечения для всех запросов).


Подробнее здесь: https://stackoverflow.com/questions/787 ... out-middle

Вернуться в «C#»