Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return a list of errors with BadRequest (WebApi)

As the title indicates all I'm trying to do is return a custom collection of errors if the "Model" is incomplete.

Whilst actively "SO'ing/Googling" I haven't found a solution to help with my problem.

I could of used "ModelState" but due to "Customisation", I'd like to do this manually.

Code as Follows:

API Level

// POST api/<controller>
[HttpPost]
[Route("")]
public async Task<IHttpActionResult> Post([FromBody]Order order)
{
    var modelResponse = new ModelResponse<Order>(order);
    if (order == null)
        return BadRequest("Unusable resource, object instance required.");

    //Check if all required properties contain values, if not, return response
    //with the details
    if (!modelResponse.IsModelValid())
        return this.PropertiesRequired(modelResponse.ModelErrors());

    try
    {
        await _orderService.AddAsync(order);
    }
    catch (System.Exception ex)
    {
        return InternalServerError();
    }
    finally
    {
        _orderService.Dispose();
    }

    return Ok("Order Successfully Processed.");
}

Properties Required Action Result

public List<string> Messages { get; private set; }
public HttpRequestMessage Request { get; private set; }

public PropertiesRequiredActionResult(List<string> message, 
    HttpRequestMessage request)
{
    this.Messages = message;
    this.Request = request;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
    return Task.FromResult(Execute());
}

public HttpResponseMessage Execute()
{
    HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
    response.Content = new ObjectContent()
        //new List<StringContent>(Messages); //Stuck here
    response.RequestMessage = Request;
    return response;
}

Finding incomplete properties, based on a custom Attribute

private T _obj;

public ModelResponse(T obj)
{
    _obj = obj;
}

private Dictionary<string, object> GetPropertyAttributes(PropertyInfo property)
{
    Dictionary<string, object> attribs = new Dictionary<string, object>();
    // look for attributes that takes one constructor argument
    foreach (CustomAttributeData attribData in property.GetCustomAttributesData())
    {

        if (attribData.ConstructorArguments.Count == 1)
        {
            string typeName = attribData.Constructor.DeclaringType.Name;
            if (typeName.EndsWith("Attribute")) typeName = typeName.Substring(0, typeName.Length - 9);
            attribs[typeName] = attribData.ConstructorArguments[0].Value;
        }

    }
    return attribs;
}
private IEnumerable<PropertyInfo> GetProperties()
{
    var props = typeof(T).GetProperties().Where(
            prop => Attribute.IsDefined(prop, typeof(APIAttribute)));

    return props;
}
public bool IsModelValid()
{
    var props = GetProperties();
    return props.Any(p => p != null);
}
public List<string> ModelErrors()
{
        List<string> errors = new List<string>();
        foreach (var p in GetProperties())
        {

            object propertyValue = _obj.GetType()
                .GetProperty(p.Name).GetValue(_obj, null);

            if (propertyValue == null)
            {
                errors.Add(p.Name + " - " + GetPropertyAttributes(p).FirstOrDefault());
            }
        }
        return errors;
}

Sample of Attribute

/// <summary>
/// The date and time when the order was created.
/// </summary>
[API(Required = "Order Created At Required")]
public DateTime Order_Created_At { get; set; }

So ignoring the two latter snippets, that was more to give a full process overview. I completely understand there are a few "out the box" techniques but I do enjoy crafting my own implementations.

To the point, Is it possible to return a list of errors with "BadRequest"?

Much appreciated.

like image 257
Tez Wingfield Avatar asked Feb 13 '17 10:02

Tez Wingfield


People also ask

How to get badrequestresult in web API 2?

If you are using ASP.NET Web API 2, the easiest way is to use the ApiController Short-Method. This will result in a BadRequestResult. For Web API 2 my methods consistently return IHttpActionResult so I use...

How to specify the errors in the API response?

Many people have different designs for specifying the errors in the API response - some just return status code without any response message, while some describe a simple text message in the body and others specify custom JSON schema for specifying the errors.

What is a bad request in Salesforce API?

When this API is called without passing in the required parameters of the model, a 400-Bad Request in the ProblemDetails format is generated. Custom bad request: Maybe the business rule failed or some other logic check failed and you need to return 400-Bad Request.

What is an example of a bad request?

For example, imagine an API that allows customers to book movie tickets online and this call returns a Bad request (400) response without any other additional info. The user doesn't understand why the Client UI is showing an error and the Client UI doesn't know why the API has failed.


2 Answers

You probably looking for using this method:

BadRequestObjectResult BadRequest(ModelStateDictionary modelState)

The usage of it is something like this, the example is from another question here in SO :

if (!ModelState.IsValid)
     return BadRequest(ModelState);

Depending on the model errors, you get this result:

{
   Message: "The request is invalid."
   ModelState: {
       model.PropertyA: [
            "The PropertyA field is required."
       ],
       model.PropertyB: [
             "The PropertyB field is required."
       ]
   }
}

Hope it helps

like image 168
Felix Av Avatar answered Nov 07 '22 06:11

Felix Av


In your custom implementation of IHttpActionResult use the request to create the response and pass the model and status code.

public List<string> Messages { get; private set; }
public HttpRequestMessage Request { get; private set; }

public HttpResponseMessage Execute() {
    var response = Request.CreateResponse(HttpStatusCode.BadRequest, Messages);
    return response;
}
like image 43
Nkosi Avatar answered Nov 07 '22 06:11

Nkosi