Validations in Webpapi

Asked

Viewed 108 times

2

I’m starting to study webapi with knockout, using a classe Basic as example I made my model, in the knockout I made the list and include it in my classe I have decorated the attributes with some data annotations, this part enters my doubt, I know that with the knockout I can do a lot of client-side validations, but I also want to have those validations on the server. What is the best way to show server-side errors when using webapi?

Below is code of what I have ready!

Model

public class Noticia
    {
        public int Id { get; set; }

        [Required(ErrorMessage = "Campo obrigatório!")]
        public string Titutlo { get; set; }

        [DataType(DataType.Date)]
        [Required(ErrorMessage = "Campo obrigatório!")]
        public DateTime DataCadastro { get; set; }

        [Required(ErrorMessage = "Campo obrigatório!")]
        public string Conteudo { get; set; }
    }

Controller

public IQueryable<Noticia> GetNoticias()
{
    return _db.Noticias;
}

[HttpPost]
[ResponseType(typeof(Noticia))]
public IHttpActionResult PostNoticia(Noticia noticia)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    if (noticia == null)
    {
        return BadRequest();
    }

    _db.Noticias.Add(noticia);
    _db.SaveChanges();

    return CreatedAtRoute("DefaultApi", new { id = noticia.Id }, noticia);
}

** VIEW **

<div class="lista-noticias">
    <h2>Lista de Notícias</h2>
    <hr />

    <table id="newsTable" class="table table-condensed">
        <thead>
            <tr>
                <th>Titulo</th>
                <th>Data de Cadastro</th>
            </tr>
        </thead>
        <tbody data-bind="foreach: noticias">
            <tr>
                <td>
                    <span data-bind="text: titutlo"></span>
                </td>
                <td>
                    <span data-bind="text: dataCadastro"></span>
                </td>
            </tr>
        </tbody>
    </table>
</div>

<div class="cadastra-noticias">

    <form id="AddNoticia">
        <div class="row form-group">
            <input type="hidden" data-bind="value : noticia.id" class="form-control input-lg" />

            <label class="control-label">Título</label>
            <input data-bind="value : NovaNoticia.titutlo" class="form-control input-lg" />

            <label class="control-label">Data Cadastro</label>
            <input data-bind="value : NovaNoticia.dataCadastro" class="form-control input-lg" />

            <label class="control-label">Conteúdo</label>
            <input data-bind="value : NovaNoticia.conteudo" class="form-control input-lg" />

            <input type="button" id="btnAddStudent" class="btn btn-primary" value="Add Noticia" data-bind="click: $root.AddNoticia" />
        </div>
    </form>
</div>

** JS of Knockout **

function noticia(id, titutlo, dataCadastro, conteudo) {
    return {
        id: ko.observable(id),
        titutlo: ko.observable(titutlo),
        dataCadastro: ko.observable(dataCadastro),
        conteudo: ko.observable(conteudo)
    };
}

function NoticiaViewModel() {
    var self = this;

    self.NovaNoticia =
       {
           id: ko.observable(0),
           titutlo: ko.observable("").extend({ required: true }),
           dataCadastro: ko.observable("").extend({ required: true }),
           conteudo: ko.observable("").extend({ required: true })
       };

    self.noticias = ko.observableArray([]);

    $.getJSON("/admin/api/noticiasapi", function (data) {
        self.noticias(data);
    });

    self.AddNoticia = function (model, event) {
        $.post("/admin/api/noticiasapi/PostNoticia", model.NovaNoticia)
            .complete(function (data) {
                self.noticias.push(data);
            }).fail(function (data) {
                $(".list-errors").removeClass("hide");

                //$(".list-errors ul").append("<li>" + data.message + "</li>");
            console.warn(data.responseText);

        });
    };
}

ko.applyBindings(new NoticiaViewModel());
  • You can please put what you already have of code, both Controller and Views?

  • @gypsy-Morrison-Mendez I edited the question with the code.

  • 1

    In short, if your idea is to use validations in your views with the attributes in the models, forget, there is no way, however, in your api, you can validate and return to msg via json...the example that the gypsy posted is correct

1 answer

3


From what I’ve researched, there are two ways:

  • Using Simple AJAX;
  • Using jQuery;

Moreover, in this other article, it is recommended to implement a Filter to return the ModelState for the request caller (an AJAX request opened by jQuery, for example):

using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using System.Web.Http.ModelBinding;

namespace MyApi.Filters
{
    public class ValidateModelAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if (actionContext.ModelState.IsValid == false)
            {
                actionContext.Response = actionContext.Request.CreateErrorResponse(
                    HttpStatusCode.BadRequest, actionContext.ModelState);
            }
        }
    }
}

The answer will be something like this:

HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Date: Tue, 16 Jul 2013 21:02:29 GMT
Content-Length: 331

{
  "Message": "The request is invalid.",
  "ModelState": {
    "noticia": [
      "Required property 'Titulo' not found in JSON. Path '', line 1, position 17."
    ],
    "noticia.Titulo": [
      "The Titulo field is required."
    ],
    "product.Conteudo": [
      "The Conteudo field is required."
    ]
  }
}

The call for validation would be something like this:

ko.validation.rules['remote'] = {
    async: true,
    validator: function ( val, params, callback ) { 
        var defaults = {
            url: '/Noticias',
            type: 'POST',
            success: callback
        };

        var options = $.extend( defaults, params );

        $.ajax( options );

        // Aqui possivelmente você terá que customizar o código, 
        // pra satisfazer o callback e o resto do funcionamento da validação.
    },
    message: 'Default Invalid Message'
};

Your controller would look like this:

[HttpPost]
[ResponseType(typeof(Noticia))]
public IHttpActionResult PostNoticia(Noticia noticia)
{
    {
        if (ModelState.IsValid)
        {
            _db.Noticias.Add(noticia);
            _db.SaveChanges();

            return CreatedAtRoute("DefaultApi", new { id = noticia.Id }, noticia);

            // return new HttpResponseMessage(HttpStatusCode.OK);
        }
        else
        {
            return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
        }
    }
}

I didn’t test that code, but this is the way.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.