Skip to content

Instantly share code, notes, and snippets.

@lnickers2004
Created January 18, 2014 07:59
Show Gist options
  • Save lnickers2004/8487619 to your computer and use it in GitHub Desktop.
Save lnickers2004/8487619 to your computer and use it in GitHub Desktop.
WebApi2: Added a Diary Controller which uses an Identity Service for authorization to return logged-in user's private daily diary. also added testing to ensure a diary is found and return the apropriate HttpReponse Codes-- either 200 OK or 404 not found. Also have special case code for returning a date as part of the URI (making use of string fo…
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace CountingKs.Services
{
public class CountingKsIdentityService : CountingKs.Services.ICountingKsIdentityService
{
public CountingKsIdentityService()
{
}
public string CurrentUser
{
get
{
//hard-coded username for now since it is in the database
return "shawnwildermuth";
}
}
}
}
using CountingKs.Data;
using CountingKs.Data.Entities;
using CountingKs.Models;
using CountingKs.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Web.Http;
namespace CountingKs.Controllers
{
public class DiariesController : BaseApiController
{
private ICountingKsIdentityService _identityService;
//We have opted to create and use a user Identity service and we'll inject it
//into the controller. We didn't inject it into the BaseApiController because
//the Food and Measures don't require identity checking. Only the personal
//part of the api e.g. Diaries require Authentication) SOLID Principle
//Interface Segregation Principle (Don't force other classed to take dependencies
// that they do not need!!)
//If we wish, we could create another BaseApiController with Identity
//e.g. BaseWithIdentityApiController, then we could inject the Indentity Service into that
//base api controller and derive form it instead. But for simplicity, we won't don that now
public DiariesController( ICountingKsRepository repo,
ICountingKsIdentityService identityService )
: base(repo)
{
_identityService = identityService;
}
public IEnumerable<DiaryModel> Get()
{
//using Thread.CurrentPrincipal to get the user is not very testable
//var username = Thread.CurrentPrincipal.Identity.Name;
var username = _identityService.CurrentUser;
var results = TheRepository.GetDiaries(username)
.OrderByDescending(d => d.CurrentDate)
.Take(10)
.ToList()
.Select(d => TheModelFactory.Create(d));
return results;
}
//here is a special case: if the diary doesn't exist we
// want to return a result of not found
public HttpResponseMessage Get( DateTime diaryId )
{
var username = _identityService.CurrentUser;
//NOTE: GetDiary method expects a DateTime
var result = TheRepository.GetDiary(username, diaryId);
if(result == null)
{
//Always return results or not found. never return empty results. that is not useful
return Request.CreateResponse(HttpStatusCode.NotFound);
}
else
{
//the result diary will be serialized into the body of the reponse and the
// status code will be set to ok
return Request.CreateResponse(HttpStatusCode.OK, TheModelFactory.Create(result));
}
}
}
}
using CountingKs.Data.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CountingKs.Models
{
public class DiaryModel
{
public string Url { get; set; }
public DateTime CurrentDate;
//public IEnumerable<DiaryEntryModel> Entries { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using CountingKs.Data.Entities;
using System.Web.Http.Routing;
using System.Net.Http;
namespace CountingKs.Models
{
//using a Model Factory, we have only one place to handling Mapping from
//Database entities to DTO's
public class ModelFactory
{
private UrlHelper _urlHelper;
//LEN NOTE: THE REASON WE DID NOT USE AUTOMAPPER IS BECAUSE WE WANTED TO USE THE REQUEST
//AND THE URL HELPER TO AID US IN CREATING OUR HYPERLINKS
public ModelFactory( HttpRequestMessage request )
{
//Remember to use the Url helper for WebAPI
//which is in System.Web.Http.Routing
_urlHelper = new UrlHelper(request); //this will help us to build
//the URLs for developer API discovery
}
public FoodModel Create( Food food )
{
return new FoodModel()
{
//get an url for the Food roud and supply the id of the food that we're returning
Url = _urlHelper.Link("Food", new { foodid = food.Id }),
Description = food.Description,
Measures = food.Measures.Select(m => Create(m))
};
}
public MeasureModel Create( Measure measure )
{
return new MeasureModel()
{
Url = _urlHelper.Link("Measures", new { foodid = measure.Food.Id, id = measure.Id }),
Description = measure.Description,
Calories = Math.Round(measure.Calories)
};
}
public DiaryModel Create( Diary d )
{
return new DiaryModel()
{
//NOTE: IMPORTANT, we are not using the Id property for the diary ID
//we are using the created date, or current date. There will be one diary per day per user.
//We need to insure that only one diary exists per user for any given date!!! We will do that
//elsewhere. We will assume that the database has unique constraint set on this field also
Url = _urlHelper.Link("Diaries", new { diaryid = d.CurrentDate.ToString("yyyy-MM-dd")}),
CurrentDate = d.CurrentDate
//Entries = TheFactory.Create( d.Entries
};
}
public ICollection<DiaryEntry> Entries { get; set; }
}
}
using System;
namespace CountingKs.Services
{
public interface ICountingKsIdentityService
{
string CurrentUser { get; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Formatting;
using System.Web.Http;
using Newtonsoft.Json.Serialization;
namespace CountingKs
{
public static class WebApiConfig
{
public static void Register( HttpConfiguration config )
{
config.Routes.MapHttpRoute(
name: "Food",
//routeTemplate: "api/foods/{foodid}",
routeTemplate: "api/v1/nutrition/foods/{foodid}",
defaults: new { controller = "foods", foodid = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "Measures",
routeTemplate: "api/v1/nutrition/foods/{foodid}/measures/{id}",
defaults: new { controller = "measures", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "FoodOrig",
routeTemplate: "api/nutrition/foodsOrig/{foodid}",
defaults: new { controller = "foodsOrig", foodid = RouteParameter.Optional }
);
//setting up a route to return diaries for the current logged in user
//LEN Note: we will secure the api and use that info to get the logged user
//we did not have to pass the user's id since we're dealing with logger in user
config.Routes.MapHttpRoute(
name: "Diaries",
routeTemplate: "api/user/diaries/{diaryid}",
defaults: new { controller = "diaries", diaryid = RouteParameter.Optional }
);
// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
//LEN NOTE:
//config.EnableQuerySupport();//optional support to expose an iqueryable endpoint for
//ODATA feeds for line of business that is Microsoft centric
//so we don't do this we don't support querying over the wire
//we will instead build a standard restful api that returns
//standard json or xml
//add camel casing
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().FirstOrDefault();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment