Search This Blog

Showing posts with label WebApi. Show all posts
Showing posts with label WebApi. Show all posts

Sunday, 8 December 2019

Validate nested class in MVC with Validator.TryValidateObject()

using System.ComponentModel.DataAnnotations;


public class ValidateModel
{
    static string result = string.Empty;
    public static string Validate(object obj)
    {
        var context = new ValidationContext(obj, serviceProvider: null, items: null);
        var results = new List<ValidationResult>();
        var isValid = Validator.TryValidateObject(obj, context, results, true);
        if (!isValid)
            foreach (var validationResult in results)
                result += validationResult.ErrorMessage;
        foreach (var prop in obj.GetType().GetProperties())
        {
            if (prop.PropertyType == typeof(string) || prop.PropertyType.IsValueType) continue;
            var value = prop.GetValue(obj);
            if (value == null) continue;
            var isEnumerable = value as IEnumerable;
            if (isEnumerable == null)
                Validate(value);
            else
                foreach (var nestedModel in isEnumerable)
                    Validate(nestedModel);
        }
        return result;
    }
}

Note:- 
MVC ModelBinder is doing all complex type DataAnnotations validation itself when you checked ModelState.IsValid in the action but it's failed when you pass the object instead of model.

In that case, you need to call the Validator.TryValidateObject() function but it's also failed to validate the complex/nested class DataAnnotations.

The above example is the solution for overcome to this issue using recursive function call.


=>Sample Model
public class RegistrationRequest
{
    public object body { get; set; }
}
public class Registration
{
    public int Id { get; set; }
    [Required]
    public string FirstName { get; set; }
    public string MiddleName { get; set; }
    [Required]
    public string LastName { get; set; }
    public Address address { get; set; }
    public List<PastHistory> pastHistory { get; set; }
}
public class Address
{
    [Required]
    public string Address1 { get; set; }
    [Required]
    public string Address2 { get; set; }
    public string Address3 { get; set; }
    [Required]
    public string State { get; set; }
    [Required]
    public string City { get; set; }
    [Required]
    public string Pincode { get; set; }
}
public class PastHistory
{
    [Required]
    public string ComapnyName { get; set; }
}

=>Sample Controller
[HttpPost]
public HttpResponseMessage Registration(RegistrationRequest reg)
{
    Registration registration = JsonConvert.DeserializeObject<Registration>(reg.body.ToString());

    string validateResult = ValidateModel.Validate(registration);
    if (!string.IsNullOrEmpty(validateResult))
        return Request.CreateErrorResponse(HttpStatusCode.BadRequest, validateResult);
    else
        return Request.CreateResponse(HttpStatusCode.OK);
}

=>Sample JSON
   "body":{ 
      "Id":"1",
      "FirstName":"Ram",
      "MiddleName":"",
      "LastName":"",
      "Address":{ 
         "Address1":"",
         "Address2":"",
         "Address3":"",
         "State":"",
         "City":"",
         "Pincode":""
      },
      "PastHistory":[ 
         { 
            "ComapnyName":""
         }
      ]
   }
}



Sunday, 16 December 2018

Refresh Token in Web API using OWIN


Step 1: - Open Visual Studio 2015 => Go to File Menu => New => Project...

Step 2: - In the Installed Templates list, select Visual C# => Web

Step 3: - Select ASP.Net Web Application (.NET Framework) from the Web list => Type TokenAuthentication in the Name box and click OK

Step 4: - Select Empty template from ASP.NET Templates List and Check Web API check box under Add folders and core references for:































Step 5: - Right Click on Models folder => Add => New Items... => Expand Visual C# from Left Pane and Select Code => Select Class from Middle Pane => Type Author.cs in Name box => Click Add

Copy Past following code in Author.cs

namespace TokenAuthentication.Models
{
    public class Author
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Address { get; set; }
        public string Status { get; set; }

    }
}

Step 6: - Right Click on Controllers folder => Add => Controller... => Select Web API Controller - Empty => Click Add => Type DefaultController in Controller Name box => Click Add

Copy Past following code in DefaultController

using System.Collections.Generic;
using System.Web.Http;
using TokenAuthentication.Models;

namespace TokenAuthentication.Controllers
{
    public class DefaultController : ApiController
    {
        [HttpGet]
        [Authorize]
        public List<Author> GetAuthor()
        {
            List<Author> author = new List<Author>() {
                new Author() {Id=1,FirstName="Ram",LastName="G",Address="Malad",Status="A" },
                new Author() {Id=2,FirstName="Shyam",LastName="G",Address="Kandivali",Status="A" },
                new Author() {Id=3,FirstName="Ghanshyam",LastName="G",Address="Borivali",Status="A" }
            };
            return author;
        }
    }
}

Step 7: - Right click on References => Click on Manage NuGet Packages... => Click on Browse => Search bellow list one by one => Select result => Click on Install

Microsoft.Owin























Microsoft.Owin.Host.SystemWeb
























Microsoft.Owin.Security.OAuth
Microsoft.Owin.Security
























Microsoft.AspNet.Identity.Owin
























Microsoft.AspNet.WebApi.Cors
























Step 8: - Right Click on Project Root folder => Add => New Folder => Name "Provider"

Step 9: - Right Click on Provider folder => Add => New Items... => Expand Visual C# from Left Pane and Select Code => Select Class from Middle Pane => Type AuthorizationServerProvider.cs in Name box => Click Add

Copy Past following code in AuthorizationServerProvider.cs

using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web.Http.Cors;

namespace TokenAuthentication.Provider
{
    [EnableCors(origins: "*", headers: "*", methods: "*")]
    public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
    {
        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            context.Validated();
        }

        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

            //Write you DB logic here
            if (!context.UserName.Equals("Ram") && !context.Password.Equals("password@123"))
            {
                context.SetError("invalid_grant", "The user name or password is incorrect.");
            }
            else
            {
                var propertyDictionary = new Dictionary<string, string> {
                {
                    "userName", context.UserName
                }};
                var identity = new ClaimsIdentity(context.Options.AuthenticationType);
                var properties = new AuthenticationProperties(propertyDictionary);
                var ticket = new AuthenticationTicket(identity, properties);
                context.Validated(ticket);
            }
        }
    }
}

Step 10: - Right Click on Provider folder => Add => New Items... => Expand Visual C# from Left Pane and Select Code => Select Class from Middle Pane => Type RefreshTokenProvider.cs in Name box => Click Add

Copy Past following code in RefreshTokenProvider.cs

using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;

namespace TokenAuthentication.Provider
{
    public class RefreshTokenProvider : IAuthenticationTokenProvider
    {
        private static ConcurrentDictionary<string, AuthenticationTicket> _refreshTokens = new ConcurrentDictionary<string, AuthenticationTicket>();

        public void Create(AuthenticationTokenCreateContext context)
        {
            throw new NotImplementedException();
        }

        public async Task CreateAsync(AuthenticationTokenCreateContext context)
        {
            var guid = Guid.NewGuid().ToString();

            // Set lifetime of refresh token 
            var refreshTokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary)
            {
                IssuedUtc = context.Ticket.Properties.IssuedUtc,
                ExpiresUtc = DateTime.UtcNow.AddMinutes(60)
            };

            var refreshTokenTicket = new AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties);

            _refreshTokens.TryAdd(guid, refreshTokenTicket);

            context.SetToken(guid);
        }

        public void Receive(AuthenticationTokenReceiveContext context)
        {
            throw new NotImplementedException();
        }

        public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
        {
            AuthenticationTicket ticket;
            string header = context.OwinContext.Request.Headers["Authorization"];

            if (_refreshTokens.TryRemove(context.Token, out ticket))
            {
                context.SetTicket(ticket);
            }
        }
    }
}

Step 11: - Right Click on Project Root folder => Add => New Items... => Expand Visual C# from Left Pane and Select Code => Select Class from Middle Pane => Type Startup.cs in Name box => Click Add

Copy Past following code in Startup.cs

using Microsoft.Owin;
using Microsoft.Owin.Security.OAuth;
using Owin;
using System;
using System.Web.Http;
using TokenAuthentication.Provider;

namespace TokenAuthentication
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureAuth(app);
        }

        public void ConfigureAuth(IAppBuilder app)
        {
            var OAuthOptions = new OAuthAuthorizationServerOptions
            {
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(20),
                Provider = new AuthorizationServerProvider(),
  RefreshTokenProvider = new RefreshTokenProvider()
            };

            app.UseOAuthBearerTokens(OAuthOptions);
            app.UseOAuthAuthorizationServer(OAuthOptions);
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

            HttpConfiguration config = new HttpConfiguration();
            WebApiConfig.Register(config);
        }
    }
}

Step 12: - Open/Expand App_Start folder => Open WebApiConfig.cs file => Add following line

using System.Web.Http;
using System.Web.Http.Cors;

namespace TokenAuthentication
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
            EnableCorsAttribute cors = new EnableCorsAttribute("*", "*", "*");
            config.EnableCors(cors);

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}
All Done

Run Project

Step 13: - Launch Postman

Step 14: - Copy Past following URL in URI and Value in Body tab and Hit Send button

username   -    Ram
password   -    password@123
grant_type -    password



Note: -

If following error occurred open Manage NuGet Packages... and Search Newtonsoft.Json in Browse tab and update it.

Server Error in '/' Application.

Could not load file or assembly 'Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)





















Step 15: - Copy Past following URL in URI and Copy Past Step 13 access_token in Header tab and Hit Send button


Authorization   -   Bearer PJzq0Miq4T6vPMMg-SSRJGbKdNcD7Cscib1t_Sa1msY1lwzHV1Sm8QAwd1wk8e_PkTwLTnl6jDBcnTaCePYXaFxRC02Nfdt8zRT-b5wNX4MNAzFTSbUSECB-we_6ojI2acW01a6lJXAlpT6O1jVtaJvziRgNJGLo9GNrHrKLCm2u_0oK3G0YKxabGiq7MFCZuVwhv-ImlZCHxOrLJGQZSw























Note: -

Token expired in 20 minutes and Refresh Token expired in 60 minutes. After 20 minutes Token will be expired and you need to sign in again. In that case use refresh token to sign in instead of passing username and password again.

Step 16: - Copy Past following URL in URI and Value in Body tab and Hit Send button

username         -            Ram
refresh_token -            d343d805-341d-491d-b5dd-ff87ddf9b5d2
grant_type                 refresh_token