Web Api Обязательный параметр
Использование ASP.NET Web API. Есть ли способ автоматически возвращать код состояния 400, если параметр имеет значение null? Я нашел это question, но это глобальное решение, применяемое ко всем методам, я хочу сделать это по каждому методу на каждый параметр.
Итак, например, это то, что я сейчас делаю:
public HttpResponseMessage SomeMethod(SomeNullableParameter parameter)
{
if (parameter == null)
throw new HttpResponseException(HttpStatusCode.BadRequest);
// Otherwise do more stuff.
}
Я бы просто хотел сделать что-то вроде этого (обратите внимание на требуемый атрибут):
public HttpResponseMessage SomeMethod([Required] SomeNullableParameter parameter)
{
// Do stuff.
}
Ответы
Ответ 1
Подход, который я в итоге использовал, заключался в создании настраиваемого фильтра, который я зарегистрировал глобально. Фильтр проверяет все параметры запроса для RequiredAttribute
. Если атрибут найден, он проверяет, был ли передан параметр с запросом (не нулевым), и возвращает код состояния 400, если он был нулевым. Я также добавил к фильтру кэш, в котором хранятся необходимые параметры для каждого запроса, чтобы избежать попадания отражения в будущие вызовы. Я был приятно удивлен, обнаружив, что это работает и для типов значений, так как контекст действия хранит параметры как объекты.
РЕДАКТИРОВАТЬ - Обновленное решение на основе комментариев Tecfield
public class RequiredParametersFilter : ActionFilterAttribute
{
// Cache used to store the required parameters for each request based on the
// request http method and local path.
private readonly ConcurrentDictionary<Tuple<HttpMethod, string>, List<string>> _Cache =
new ConcurrentDictionary<Tuple<HttpMethod, string>, List<string>>();
public override void OnActionExecuting(HttpActionContext actionContext)
{
// Get the request required parameters.
List<string> requiredParameters = this.GetRequiredParameters(actionContext);
// If the required parameters are valid then continue with the request.
// Otherwise, return status code 400.
if(this.ValidateParameters(actionContext, requiredParameters))
{
base.OnActionExecuting(actionContext);
}
else
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
}
private bool ValidateParameters(HttpActionContext actionContext, List<string> requiredParameters)
{
// If the list of required parameters is null or containst no parameters
// then there is nothing to validate.
// Return true.
if (requiredParameters == null || requiredParameters.Count == 0)
{
return true;
}
// Attempt to find at least one required parameter that is null.
bool hasNullParameter =
actionContext
.ActionArguments
.Any(a => requiredParameters.Contains(a.Key) && a.Value == null);
// If a null required paramter was found then return false.
// Otherwise, return true.
return !hasNullParameter;
}
private List<string> GetRequiredParameters(HttpActionContext actionContext)
{
// Instantiate a list of strings to store the required parameters.
List<string> result = null;
// Instantiate a tuple using the request http method and the local path.
// This will be used to add/lookup the required parameters in the cache.
Tuple<HttpMethod, string> request =
new Tuple<HttpMethod, string>(
actionContext.Request.Method,
actionContext.Request.RequestUri.LocalPath);
// Attempt to find the required parameters in the cache.
if (!this._Cache.TryGetValue(request, out result))
{
// If the required parameters were not found in the cache then get all
// parameters decorated with the 'RequiredAttribute' from the action context.
result =
actionContext
.ActionDescriptor
.GetParameters()
.Where(p => p.GetCustomAttributes<RequiredAttribute>().Any())
.Select(p => p.ParameterName)
.ToList();
// Add the required parameters to the cache.
this._Cache.TryAdd(request, result);
}
// Return the required parameters.
return result;
}
}
Ответ 2
Установите [Required]
для свойства в вашей модели, а затем проверьте ModelState
чтобы увидеть, является ли оно IsValid
.
Это позволит одновременно протестировать все необходимые свойства.
См. Раздел " Недостаточная публикация " @Проверка модели в WebAPI
Ответ 3
Решение для ядра asp.net...
[AttributeUsage(AttributeTargets.Method)]
public sealed class CheckRequiredModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var requiredParameters = context.ActionDescriptor.Parameters.Where(
p => ((ControllerParameterDescriptor)p).ParameterInfo.GetCustomAttribute<RequiredModelAttribute>() != null).Select(p => p.Name);
foreach (var argument in context.ActionArguments.Where(a => requiredParameters.Contains(a.Key, StringComparer.Ordinal)))
{
if (argument.Value == null)
{
context.ModelState.AddModelError(argument.Key, $"The argument '{argument.Key}' cannot be null.");
}
}
if (!context.ModelState.IsValid)
{
var errors = context.ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage);
context.Result = new BadRequestObjectResult(errors);
return;
}
base.OnActionExecuting(context);
}
}
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class RequiredModelAttribute : Attribute
{
}
services.AddMvc(options =>
{
options.Filters.Add(typeof(CheckRequiredModelAttribute));
});
public async Task<IActionResult> CreateAsync([FromBody][RequiredModel]RequestModel request, CancellationToken cancellationToken)
{
//...
}
Ответ 4
Принятое решение берет на себя ответственность сообщать о любых ошибках. Более подходящий подход для MVC5 состоит в том, чтобы позволить контроллеру обрабатывать (посредством проверки модели) сообщение о любых ошибках, что-то вроде этого:
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using System.Web.Http.ModelBinding;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public sealed class ValidateParametersAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext context)
{
var descriptor = context.ActionDescriptor;
if (descriptor != null)
{
var modelState = context.ModelState;
foreach (var parameterDescriptor in descriptor.GetParameters())
{
EvaluateValidationAttributes(
suppliedValue: context.ActionArguments[parameterDescriptor.ParameterName],
modelState: modelState,
parameterDescriptor: parameterDescriptor
);
}
}
base.OnActionExecuting(context);
}
static private void EvaluateValidationAttributes(HttpParameterDescriptor parameterDescriptor, object suppliedValue, ModelStateDictionary modelState)
{
var parameterName = parameterDescriptor.ParameterName;
parameterDescriptor
.GetCustomAttributes<object>()
.OfType<ValidationAttribute>()
.Where(x => !x.IsValid(suppliedValue))
.ForEach(x => modelState.AddModelError(parameterName, x.FormatErrorMessage(parameterName)));
}
}
Затем вы можете подключить его повсеместно через WebApiConfig.cs:
config.Filters.Add(new ValidateParametersAttribute());
Ответ 5
В Asp.Net Core 2.1 есть встроенный параметр [BindRequired], который выполняет эту проверку автоматически.
public async Task<ActionResult<string>> CleanStatusesAsync([BindRequired,
FromQuery]string collection, [BindRequired, FromQuery]string repository,
[BindRequired, FromQuery]int pullRequestId)
{
// all parameters are bound and valid
}
Если вы вызываете этот метод без параметров, возвращается ошибка ModelState:
{
"collection": [
"A value for the 'collection' parameter or property was not provided."
],
"repository": [
"A value for the 'repository' parameter or property was not provided."
],
"pullRequestId": [
"A value for the 'pullRequestId' parameter or property was not provided."
],
}
Более подробную информацию вы можете найти в этой превосходной статье.