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.
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...
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.
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.
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.
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
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With