Wes Thompson
Wes Thompson

Reputation: 473

ModelState.IsValid Returning Incorrect Type

I have an ASP.NET application and I'm trying to use ModelState.IsValid to return error messages if the query is flawed. I tried something like this:

[HttpGet]
[Route("")]
[Route("{id:int}")]
public IQueryable<ToDoTable> Get(int id = -1)
{
    if (!ModelState.IsValid)
    {
        var errorList = (from item in ModelState
                         where item.Value.Errors.Any()
                         select item.Value.Errors[0].ErrorMessage).ToList();
        return errorList.AsQueryable();
    }
    else
    {
        if (id == -1)
            return db.ToDoTables;
        else
            return db.ToDoTables.Where(lt => lt.ID == id);
    }
}

However, the problem is that errorList is of type string and the function is expecting a return type of ToDoTable, a class that I made. How do I get the correct return type? Or do I need to change the function's expectations? Do I have to add the method into the class instead (not even sure that would work)?

When ModelState.IsValid is true, the function is returning class objects with information gathered from the database I'm querying and outputs it as JSON. Example:

[
  {
    "ID": 11,
    "Title": "this is a test",
    "Description": "this is specifically to test the put method",
    "Due": null,
    "Completed": true
  },
  {
    "ID": 15,
    "Title": "date test",
    "Description": "datetime format",
    "Due": "2015-08-10T02:41:29",
    "Completed": true
  }
]

Upvotes: 0

Views: 107

Answers (2)

Fermin
Fermin

Reputation: 36081

An approach that I have used in the past is to return an HttpResponseMessage from the method.

This will allow you to return an 'error' type (for example https://datatracker.ietf.org/doc/html/draft-nottingham-http-problem) and appropriate response code (e.g. 400).

Your function then becomes:

[HttpGet]
[Route("")]
[Route("{id:int}")]
public HttpResponseMessage Get(int id = -1)
{
    if (!ModelState.IsValid)
    {
        var errorList = (from item in ModelState
                         where item.Value.Errors.Any()
                         select item.Value.Errors[0].ErrorMessage).ToList();
        return Request.CreateResponse(HttpStatusCode.BadRequest, errorList);
    }
    else
    {

        var tables = (id == -1) ? db.ToDoTables : db.ToDoTables.Where(lt => lt.ID == id);
        return Request.CreateResponse(HttpStatusCode.OK, tables);
    }
}

This means that you can cope with differing response types and statuses without throwing HttpResponseExceptions.

Upvotes: 1

CodeCaster
CodeCaster

Reputation: 151594

Think from your consumers. How are you going to communicate and document this? "This API call returns a Foo, except for in cases where it returns Bar".

That is possible, but then you need to either change the return type to an IHttpActionResult or throw new HttpResponseException.

(Arguably) better would be to introduce a response container with nullable properties, something like this:

public class ApiResponse<T>
{
    public bool Success { get; set; }

    public int? RecordCount { get; set; }

    public T Content { get; set; }

    public ErrorDetail Error { get; set; }
}

This way all your operations can benefit from being statically-typed (think unit tests) and return success and failure in the same way.

Related: Handle ModelState Validation in ASP.NET Web API, Best practice to return errors in ASP.NET Web API, Storing result of Web API call into Generic type class,

Upvotes: 0

Related Questions