1

I was needing to make a post or put a validation on the server side to check if the email is unique.

In the research I have always done the example was a traditional MVC application and never an api.

In many cases I saw that the [Remote] https://learn.microsoft.com/pt-br/aspnet/core/mvc/models/validation?view=aspnetcore-2.2#remote-attribute . I tried to implement according to the documentation, but debugging verified that the function in the controller is neither called.

User.cs

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.AspNetCore.Mvc;

namespace Base.Models
{
    [Table("users")]
    public partial class User
    {
        ...
        [Required]
        [EmailAddress]
        [Remote(action: "VerifyEmail", controller: "UserController",ErrorMessage="Email already in use")]
        [Column("email", TypeName = "varchar(254)")]
        public string Email { get; set; }
        ...
    }
}

UserController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Base.Models;

namespace Base.Controllers
{
    [Route("api/users")]
    [ApiController]
    public class UserController : Controller
    {
        ...
        [AcceptVerbs("Get")]
        public IActionResult VerifyEmail(string email)
        {
            //forcing it to go wrong
            return Json($"Email {email} is already in use.");
        }
        ...
    }
}

Anyone have any idea how to implement this?

2 Answers 2

9

Seeing that it was not progressing, I decided to do a personalized validation.

EmailUserUniqueAttribute.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using Base.Models;

namespace Core.Models
{
    public class EmailUserUniqueAttribute : ValidationAttribute
    {
        protected override ValidationResult IsValid(
            object value, ValidationContext validationContext)
        {
            var _context = (AppDbContext)validationContext.GetService(typeof(AppDbContext));
            var entity = _context.Users.SingleOrDefault(e => e.Email == value.ToString());

            if (entity != null)
            {
                return new ValidationResult(GetErrorMessage(value.ToString()));
            }
            return ValidationResult.Success;
        }

        public string GetErrorMessage(string email)
        {
            return $"Email {email} is already in use.";
        }
    }
}

User.cs

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.AspNetCore.Mvc;

namespace Base.Models
{
    [Table("users")]
    public partial class User
    {
        ...
        [Required]
        [EmailAddress]
        [EmailUserUnique]
        [Column("email", TypeName = "varchar(254)")]
        public string Email { get; set; }
    }
}
        ...

It works, but I don't know if this is the best way to do this.

Sign up to request clarification or add additional context in comments.

2 Comments

This is a fine and reusable way to do this validation. When I saw this, I was looking to see that you found the "GetService" method on the validation context. This method is a good example of "Service Locator" that is often considered an Anti-Pattern. However, because of the way DI works with validation filters, it is perfectly pragmatic to use.
This was really helpful. Easy to customize. Thank you for the answer.
1

I cant comment cause I lack the sufficient reputation, and if I had time I would had gone and verified but I think your issue here is the [Route("api/users")]. You are giving your decorator the Controller name but that controller is behind a different route. Easy debug of that will be to remove the Route temporarily OR put the "VerifyEmail" action to another controller that you arent altering its route.

1 Comment

Thanks for the help, but I have already tested it with no route and it did not work, I think this Remote is only for client side validation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.