All Comments
TopTalkedBooks posted at August 20, 2017

@Win: Well, that part is what I am developing. But currently basic authentication seems appropriate, where the username identifies the software contacting us and the password is a special key

Here is the sample code for BasicAuthenticationMessageHandler which uses message handler to support HTTP Basic Authentication.

You can read more at Page 121 of ASP.NET Web API 2: Building a REST Service from Start to Finish.

IBasicSecurityService

public interface IBasicSecurityService
{
    bool SetPrincipal(string username, string password);
}

BasicSecurityService

public class BasicSecurityService : IBasicSecurityService
{
    public bool SetPrincipal(string username, string password)
    {
        // Get user from database
        var user = GetUser(username);
        IPrincipal principal = null;
        if (user == null || (principal = GetPrincipal(user)) == null)
        {
            // System could not validate user
            return false;
        }

        Thread.CurrentPrincipal = principal;
        if (HttpContext.Current != null)
        {
            HttpContext.Current.User = principal;
        }

        return true;
    }

    public virtual IPrincipal GetPrincipal(User user)
    {
        var identity = new GenericIdentity(user.Username, Constants.SchemeTypes.Basic);

        identity.AddClaim(new Claim(ClaimTypes.GivenName, user.Firstname));
        identity.AddClaim(new Claim(ClaimTypes.Surname, user.Lastname));
        // Get authroized roles and add them as Role Claim.
        identity.AddClaim(new Claim(ClaimTypes.Role, "Manager"));

        return new ClaimsPrincipal(identity);
    }
}

BasicAuthenticationMessageHandler

public class BasicAuthenticationMessageHandler : DelegatingHandler
{
    public const char AuthorizationHeaderSeparator = ':';
    private const int UsernameIndex = 0;
    private const int PasswordIndex = 1;
    private const int ExpectedCredentialCount = 2;

    private readonly IBasicSecurityService _basicSecurityService;

    public BasicAuthenticationMessageHandler(IBasicSecurityService basicSecurityService)
    {
        _basicSecurityService = basicSecurityService;
    }

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        if (HttpContext.Current.User.Identity.IsAuthenticated)
        {
            // Already authenticated; passing on to next handler...
            return await base.SendAsync(request, cancellationToken);
        }

        if (!CanHandleAuthentication(request))
        {
            // Not a basic auth request; passing on to next handler...
            return await base.SendAsync(request, cancellationToken);
        }

        bool isAuthenticated;
        try
        {
            isAuthenticated = Authenticate(request);
        }
        catch (Exception e)
        {
            // Failure in auth processing
            return CreateUnauthorizedResponse();
        }

        if (isAuthenticated)
        {
            var response = await base.SendAsync(request, cancellationToken);
            return response;
        }

        return CreateUnauthorizedResponse();
    }

    public bool CanHandleAuthentication(HttpRequestMessage request)
    {
        return (request.Headers != null
                && request.Headers.Authorization != null
                && request.Headers.Authorization.Scheme.ToLowerInvariant() == Constants.SchemeTypes.Basic);
    }

    public bool Authenticate(HttpRequestMessage request)
    {
        // Attempting to authenticate...
        var authHeader = request.Headers.Authorization;
        if (authHeader == null)
        {
            return false;
        }

        var credentialParts = GetCredentialParts(authHeader);
        if (credentialParts.Length != ExpectedCredentialCount)
        {
            return false;
        }

        return _basicSecurityService.SetPrincipal(credentialParts[UsernameIndex], credentialParts[PasswordIndex]);
    }

    public string[] GetCredentialParts(AuthenticationHeaderValue authHeader)
    {
        var encodedCredentials = authHeader.Parameter;
        var credentialBytes = Convert.FromBase64String(encodedCredentials);
        var credentials = Encoding.ASCII.GetString(credentialBytes);
        var credentialParts = credentials.Split(AuthorizationHeaderSeparator);
        return credentialParts;
    }

    public HttpResponseMessage CreateUnauthorizedResponse()
    {
        var response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
        response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue(Constants.SchemeTypes.Basic));
        return response;
    }
}
TopTalkedBooks posted at August 20, 2017

ASP.NET Web API 2.1 have framework support for global handling of unhandled exceptions.

It allows use to customize the HTTP response that is sent when an unhandled application exception occurs.

So, do not catch exception in Class Library. If you are required to log exception in Class Library, then re-throw those exception to Presentation.

WebApiConfig

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // ...

        config.Services.Replace(typeof (IExceptionHandler), 
            new GlobalExceptionHandler());
    }
}

GlobalExceptionHandler

public class GlobalExceptionHandler : ExceptionHandler
{
    public override void Handle(ExceptionHandlerContext context)
    {
        var exception = context.Exception;

        var httpException = exception as HttpException;
        if (httpException != null)
        {
            context.Result = new CustomErrorResult(context.Request,
                (HttpStatusCode) httpException.GetHttpCode(), 
                 httpException.Message);
            return;
        }

        // Return HttpStatusCode for other types of exception.

        context.Result = new CustomErrorResult(context.Request, 
            HttpStatusCode.InternalServerError,
            exception.Message);
    }
}

CustomErrorResult

public class CustomErrorResult : IHttpActionResult
{
    private readonly string _errorMessage;
    private readonly HttpRequestMessage _requestMessage;
    private readonly HttpStatusCode _statusCode;

    public CustomErrorResult(HttpRequestMessage requestMessage, 
       HttpStatusCode statusCode, string errorMessage)
    {
        _requestMessage = requestMessage;
        _statusCode = statusCode;
        _errorMessage = errorMessage;
    }

    public Task<HttpResponseMessage> ExecuteAsync(
       CancellationToken cancellationToken)
    {
        return Task.FromResult(_requestMessage.CreateErrorResponse(
            _statusCode, _errorMessage));
    }
}

Credit to ASP.NET Web API 2: Building a REST Service from Start to Finish

TopTalkedBooks posted at August 20, 2017

1) How can we structure the application? Do we need to create different projects as layers?

If you ask 10 people, they will answer 10 different answers.

Look at open source project such as NopCommerce, Orchard or Umbraco. (They are all MVC; it is hard to find Web Form project these days).

Recently, I read ASP.NET Web API 2: Building a REST Service from Start to Finish. It explains how to create project structure step-by-step. (Although it is written for Web API, you will get the idea of how projects are layout.) Here is the source code.

2) When you use EF, do you have to dispose the DbContext? We have seen this example, is the correct?

You do not want to instantiate DbContext inside service classes. Instead, you want to inject dependencies using IoC container.

In addition, you do not want to call HttpContext object inside Database layer like you did in the method parameter.

FYI: you situation is very similar to Dependency Injection in .NET book Chapter 2. In the book, Mark Seemann explains the problem of tight coupling, and how to solve using dependency injection.

3) Also, if the entity Links (for example) has a lot of properties, do you always return all the properties even is you don't use them?

Some people like to return IQueryable from service classes instead of IList or custom collection. If IQueryable, you can retrieve only the properties that you want.

Other thoughts

If it is a new project, I would like to suggest to use ASP.Net MVC instead of Web Form. The reason is you cannot unit test Web Form, and it is hard to inject dependencies in Web Form.

Updated: 10/8/2014

we have been developing using WebForms and the application is quite complex

I see. However, layers of class libraries are basically same; only presentation layer is different. Main concept is you should be able to add Web Form, WPF, MVC, Web API to your existing solution without changing anything in class libraries. In order to do that you need to inject dependencies instead of tight coupling (you can still implement dependency injection in Web Form).

On thing I forget to mention is you want to access DbContext via Repository. In other words, Service layer should not need to know what kind of database you are using.

Look at those open source projects in answers 1.

TopTalkedBooks posted at August 20, 2017

There are four basic approaches to version the RESTful way -

  1. URI Path This approach takes the following form:

    http://api/v2/Tasks/{TaskId}

  2. URI Parameter This approach takes the following form:

    http://api/Tasks/{TaskId}?v=2

  3. Content Negotiation This is done in the HTTP header.

    Content Type: application/vnd.taskManagerApp.v2.param.json

  4. Request Header This is also done in the HTTP header.

    x-taskManagerApp-version: 2

I personally like 1st approach. You can read Mike Wasson's ASP.NET Web API: Using Namespaces to Version Web APIs.

Many people have modified Mike Wasson's Original Source. I like the one used in ASP.NET Web API 2 book by Jamie Kurtz, Brian Wortman.

Since it has too many moving pieces, I created a sample project at GitHub.

config.Routes.MapHttpRoute(
   name: "DefaultApi",
   routeTemplate: "api/{version}/{controller}",
   defaults: new { version = "v2" }
);

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

Then, you add ApiVersionConstraint

public class ApiVersionConstraint : IHttpRouteConstraint
{
    public ApiVersionConstraint(string allowedVersion)
    {
        AllowedVersion = allowedVersion.ToLowerInvariant();
    }

    public string AllowedVersion { get; private set; }

    public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName,
        IDictionary<string, object> values, HttpRouteDirection routeDirection)
    {
        object value;
        if (values.TryGetValue(parameterName, out value) && value != null)
        {
            return AllowedVersion.Equals(value.ToString().ToLowerInvariant());
        }
        return false;
    }
}

Usage

You just place RoutePrefix on a controller, and you are done.

[RoutePrefix("api/{apiVersion:apiVersionConstraint(v1)}/values")]
public class ValuesController : ApiController
{
    // GET api/v1/values
    [Route("")]
    public IEnumerable<string> Get()
    {
        return new string[] { "v1-value1", "v1-value2" };
    }

    // GET api/v1/values/5
    [Route("{id}")]
    public string Get(int id)
    {
        return "v1-value-" + id;
    }
}
TopTalkedBooks posted at March 18, 2018

I'm not sure why you said ASP.Net Core and Web API 2. They are mutually exclusive; we don't normally use both in same project.

Now my question is that is it possible to implement basic authentication in web API 2? If possible then can I achieve it with action filters and how? What is the best way to authentication in Web API 2? Should i use middle ware?

In Web API 2, you can easily implement Basic Authentication using DelegatingHandler.

Here is the sample code -

IBasicSecurityService

public interface IBasicSecurityService
{
    bool SetPrincipal(string username, string password);
}

BasicSecurityService

public class BasicSecurityService : IBasicSecurityService
{
    public bool SetPrincipal(string username, string password)
    {
        // Get user from database
        var user = GetUser(username);
        IPrincipal principal = null;
        if (user == null || (principal = GetPrincipal(user)) == null)
        {
            // System could not validate user
            return false;
        }

        Thread.CurrentPrincipal = principal;
        if (HttpContext.Current != null)
        {
            HttpContext.Current.User = principal;
        }

        return true;
    }

    public virtual IPrincipal GetPrincipal(User user)
    {
        var identity = new GenericIdentity(user.Username, Constants.SchemeTypes.Basic);

        identity.AddClaim(new Claim(ClaimTypes.GivenName, user.Firstname));
        identity.AddClaim(new Claim(ClaimTypes.Surname, user.Lastname));
        // Get authroized roles and add them as Role Claim.
        identity.AddClaim(new Claim(ClaimTypes.Role, "Manager"));

        return new ClaimsPrincipal(identity);
    }
}

BasicAuthenticationMessageHandler

public class BasicAuthenticationMessageHandler : DelegatingHandler
{
    public const char AuthorizationHeaderSeparator = ':';
    private const int UsernameIndex = 0;
    private const int PasswordIndex = 1;
    private const int ExpectedCredentialCount = 2;

    private readonly IBasicSecurityService _basicSecurityService;

    public BasicAuthenticationMessageHandler(IBasicSecurityService basicSecurityService)
    {
        _basicSecurityService = basicSecurityService;
    }

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        if (HttpContext.Current.User.Identity.IsAuthenticated)
        {
            // Already authenticated; passing on to next handler...
            return await base.SendAsync(request, cancellationToken);
        }

        if (!CanHandleAuthentication(request))
        {
            // Not a basic auth request; passing on to next handler...
            return await base.SendAsync(request, cancellationToken);
        }

        bool isAuthenticated;
        try
        {
            isAuthenticated = Authenticate(request);
        }
        catch (Exception e)
        {
            // Failure in auth processing
            return CreateUnauthorizedResponse();
        }

        if (isAuthenticated)
        {
            var response = await base.SendAsync(request, cancellationToken);
            return response;
        }

        return CreateUnauthorizedResponse();
    }

    public bool CanHandleAuthentication(HttpRequestMessage request)
    {
        return (request.Headers != null
                && request.Headers.Authorization != null
                && request.Headers.Authorization.Scheme.ToLowerInvariant() == Constants.SchemeTypes.Basic);
    }

    public bool Authenticate(HttpRequestMessage request)
    {
        // Attempting to authenticate...
        var authHeader = request.Headers.Authorization;
        if (authHeader == null)
        {
            return false;
        }

        var credentialParts = GetCredentialParts(authHeader);
        if (credentialParts.Length != ExpectedCredentialCount)
        {
            return false;
        }

        return _basicSecurityService.SetPrincipal(credentialParts[UsernameIndex], credentialParts[PasswordIndex]);
    }

    public string[] GetCredentialParts(AuthenticationHeaderValue authHeader)
    {
        var encodedCredentials = authHeader.Parameter;
        var credentialBytes = Convert.FromBase64String(encodedCredentials);
        var credentials = Encoding.ASCII.GetString(credentialBytes);
        var credentialParts = credentials.Split(AuthorizationHeaderSeparator);
        return credentialParts;
    }

    public HttpResponseMessage CreateUnauthorizedResponse()
    {
        var response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
        response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue(Constants.SchemeTypes.Basic));
        return response;
    }
}

Credit: Page 121 of ASP.NET Web API 2: Building a REST Service from Start to Finish.

Top Books
We collected top books from hacker news, stack overflow, Reddit, which are recommended by amazing people.