Customize MapHttpAttributeRoutes for Web Api Versioning - c#

I am implementing Web API versioning as in Web API Versioning. My controllers are in 2 separate namespaces, and I've used a custom SelectController method to choose which version to use based on a a query parameter.
e.g.
http://myapi/api/values?version=1.0
This all works fine but some actions in the controllers use the Route attribute
[Route("api/values/getNames")]
public HttpResponseMessage Get() { ... }
Which are mapped to the correct controller by default using
config.MapHttpAttributeRoutes();
in WebApiConfig.cs
This will not work if I have multiple versions of the API with the same route. Am I able to provide a custom implementation for config.MapHttpAttributeRoutes() so I can select the correct version of the API to use, or is there a better way of doing this?

There is an example for this in the official WebApi 2.1 examplex on Codeplex.
It relies on a request header value to store the version.
I think it's a lot nicer, since it allows the routes to stay the same for all versions. Clients select the version simply by including an HTTP header in the request (in this case the version number).
This sample shows how to use Attribute Routing and Constraints in
ASP.NET Web API to dynamically filter controllers by an 'api-version'
HTTP header. When a route uses Constraints, each constraint has a
chance to prevent the route from matching a given request. In this
sample, a custom RouteFactoryAttribute (VersionedRoute) adds a
constraint to each attribute route.
...
The custom constraint implementation (VersionConstraint) is
implemented based on the value of 'api-version' matching an integer
value. The value of the allowed version for the constraint is provided
by the VersionedRoute attribute placed on each controller. When a
request comes in, the header value of 'api-version' is matched against
the expected version. This example uses a header but a constraint
implementation could use any criteria to decided if the request is
valid for the route.
Anyway, the end result would end up looking like this:
[VersionedRoute("api/Customer", 1)]
public class CustomerVersion1Controller : ApiController
{
// controller code goes here
}
[VersionedRoute("api/Customer", 2)]
public class CustomerVersion2Controller : ApiController
{
// controller code goes here
}

Here is a solution that will let you use the Web API 2 way of versioned routes (headers), in addition to query parameter support (i.e. use a header called 'api-version' or a querystring parameter named '?api-version=XXX'.
The HTTP Route constraint does the work:
/// <summary>
/// Add a route constraint to detect version header or by query string
/// </summary>
public class RouteVersionHttpConstraint : IHttpRouteConstraint
{
public const string VersionHeaderName = "api-version";
private const int DefaultVersion = 1;
/// <summary>
/// Add a route constraint to detect version header or by query string
/// </summary>
/// <param name="allowedVersion"></param>
public RouteVersionHttpConstraint(int allowedVersion)
{
AllowedVersion = allowedVersion;
}
public int AllowedVersion
{
get;
private set;
}
/// <summary>
/// Perform the controller match
/// </summary>
/// <param name="request"></param>
/// <param name="route"></param>
/// <param name="parameterName"></param>
/// <param name="values"></param>
/// <param name="routeDirection"></param>
/// <returns></returns>
public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
{
if (routeDirection == HttpRouteDirection.UriResolution)
{
int version = GetVersionHeaderOrQuery(request) ?? DefaultVersion;
if (version == AllowedVersion)
{
return true;
}
}
return false;
}
/// <summary>
/// Check the request header, and the query string to determine if a version number has been provided
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private int? GetVersionHeaderOrQuery(HttpRequestMessage request)
{
string versionAsString;
IEnumerable<string> headerValues;
if (request.Headers.TryGetValues(VersionHeaderName, out headerValues) && headerValues.Count() == 1)
{
versionAsString = headerValues.First();
int version;
if (versionAsString != null && Int32.TryParse(versionAsString, out version))
{
return version;
}
}
else
{
var query = System.Web.HttpUtility.ParseQueryString(request.RequestUri.Query);
string versionStr = query[VersionHeaderName];
int version = 0;
int.TryParse(versionStr, out version);
if (version > 0)
return version;
}
return null;
}
}
And the route factory:
/// <summary>
/// Versioning support for the WebAPI controllers
/// </summary>
public class RouteVersionAttribute : RouteFactoryAttribute
{
public int Version { get; private set; }
public RouteVersionAttribute() : this(null, 1)
{
}
/// <summary>
/// Specify a version for the WebAPI controller
/// </summary>
/// <param name="version"></param>
public RouteVersionAttribute(int version) : this(null, version)
{
}
public RouteVersionAttribute(string template, int version)
: base(template)
{
Version = version;
}
public override IDictionary<string, object> Constraints
{
get
{
var constraints = new HttpRouteValueDictionary();
constraints.Add("version", new RouteVersionHttpConstraint(Version));
return constraints;
}
}
public override IDictionary<string, object> Defaults
{
get
{
var defaults = new HttpRouteValueDictionary();
defaults.Add("version", 1);
return defaults;
}
}
}
Usage:
[RouteVersion("api/versiontest", 1)]
public class Version1TestController : BaseApiController
{
// get: api/versiontest
[HttpGet]
public HttpResponseMessage get()
{
return Request.CreateResponse(HttpStatusCode.OK, new { Version = "API Version 1 selected" });
}
}
[RouteVersion("api/versiontest", 2)]
public class Version2TestController : ApiController
{
// get: api/versiontest
[HttpGet]
public HttpResponseMessage get()
{
return Request.CreateResponse(HttpStatusCode.OK, new { Version = "API Version 2 selected" });
}
}

I extended Michael Brown's answer to allow setting a default version:
Only now I'm thinking how to make it work with Swashbuckle swagger.
RouteVersionAttribute:
using System.Collections.Generic;
using System.Web.Http.Routing;
namespace YourNameSpace.Filters
{
/// <summary>
/// Here is a solution that will let you use the Web API 2 way of versioned routes (headers),
/// in addition to query parameter support (i.e.use a header called 'api-version' or
/// a querystring parameter named '?api-version=XXX'.
/// <para>https://stackoverflow.com/a/28934352/3187389</para>
/// <para>https://stackoverflow.com/questions/25299889/customize-maphttpattributeroutes-for-web-api-versioning</para>
/// </summary>
public class RouteVersionAttribute : RouteFactoryAttribute
{
public int Version { get; private set; }
public int VersionDefault { get; private set; }
public RouteVersionAttribute() : this(null, 1, true)
{
}
/// <summary>
/// Specify a version for the WebAPI controller or an action method
/// for example: [RouteVersion("Test", 1)] or [RouteVersion("Test", 1, true)]
/// </summary>
/// <param name="version"></param>
/// <param name="isDefault"></param>
public RouteVersionAttribute(int version, bool isDefault = false) : this(null, version, isDefault)
{
}
/// <summary>
/// Specify a version for the WebAPI controller or an action method
/// for example: [RouteVersion("Test", 1)] or [RouteVersion("Test", 1, true)]
/// </summary>
/// <param name="template"></param>
/// <param name="version"></param>
/// <param name="isDefault"></param>
public RouteVersionAttribute(string template, int version, bool isDefault = false)
: base(template)
{
Version = version;
if (isDefault)
VersionDefault = version;
}
public override IDictionary<string, object> Constraints
{
get
{
var constraints = new HttpRouteValueDictionary();
constraints.Add("version", new RouteVersionHttpConstraint(Version, VersionDefault));
return constraints;
}
}
public override IDictionary<string, object> Defaults
{
get
{
var defaults = new HttpRouteValueDictionary();
defaults.Add("version", VersionDefault);
return defaults;
}
}
}
}
RouteVersionHttpConstraint:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http.Routing;
namespace Boyd.Core.Filters
{
/// <summary>
/// Here is a solution that will let you use the Web API 2 way of versioned routes (headers),
/// in addition to query parameter support (i.e.use a header called 'api-version' or
/// a querystring parameter named '?api-version=XXX'.
/// <para>https://stackoverflow.com/a/28934352/3187389</para>
/// <para>https://stackoverflow.com/questions/25299889/customize-maphttpattributeroutes-for-web-api-versioning</para>
/// </summary>
public class RouteVersionHttpConstraint : IHttpRouteConstraint
{
public const string VersionHeaderName = "api-version";
private readonly int VersionDefault = 1;
/// <summary>
/// Add a route constraint to detect version header or by query string
/// </summary>
/// <param name="allowedVersion"></param>
public RouteVersionHttpConstraint(int allowedVersion, int versionDefault)
{
AllowedVersion = allowedVersion;
VersionDefault = versionDefault;
}
public int AllowedVersion
{
get;
private set;
}
/// <summary>
/// Perform the controller match
/// </summary>
/// <param name="request"></param>
/// <param name="route"></param>
/// <param name="parameterName"></param>
/// <param name="values"></param>
/// <param name="routeDirection"></param>
/// <returns></returns>
public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
{
if (routeDirection == HttpRouteDirection.UriResolution)
{
int version = GetVersionHeaderOrQuery(request) ?? VersionDefault;
if (version == AllowedVersion)
{
return true;
}
}
return false;
}
/// <summary>
/// Check the request header, and the query string to determine if a version number has been provided
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private int? GetVersionHeaderOrQuery(HttpRequestMessage request)
{
string versionAsString;
if (request.Headers.TryGetValues(VersionHeaderName, out IEnumerable<string> headerValues)
&& headerValues.Count() == 1)
{
versionAsString = headerValues.First();
if (versionAsString != null && Int32.TryParse(versionAsString, out int version))
{
return version;
}
}
else
{
var query = System.Web.HttpUtility.ParseQueryString(request.RequestUri.Query);
string versionStr = query[VersionHeaderName];
int.TryParse(versionStr, out int version);
if (version > 0)
return version;
}
return null;
}
}
}
Usage (can be used on the controller or action methods):
#region Temporary Tests
// {{BaseUrl}}Test?api-version=1
[HttpGet]
[RouteVersion("Test", 1)]
public async Task<IHttpActionResult> Test1([FromBody]GetCustomerW2GsForPropertyRequest request)
{
return await Task.FromResult(Ok("API Version 1 selected"));
}
[HttpGet]
[RouteVersion("Test", 2)]
[RouteVersion("Test", 3)]
[RouteVersion("Test", 4)]
public async Task<IHttpActionResult> Test4([FromBody]GetCustomerW2GsForPropertyRequest request)
{
return await Task.FromResult(Ok("API Version 2, 3 or 4 selected"));
}
[HttpGet]
[RouteVersion("Test", 5, true)]
public async Task<IHttpActionResult> Test5([FromBody]GetCustomerW2GsForPropertyRequest request)
{
return await Task.FromResult(Ok("API Version 5 selected"));
}
#endregion Temporary Tests

Related

c# - Can I refactor functions that have similar inputs but call different services into one generic function?

I am looking for a way to combine x amount of very similar CRUD functions into one without having to use x amount of if else statements to check the type of a generic.
I have Web API controllers that I want to make calls from like this:
Service.Get<FooModel>(number, type, part, version);
This is to prevent having to have an extremely similar function for 40+ API endpoints. The issue is when I receive this in my service, I have to check the type of the generic given and compare with those 40+ object types in the one function. All of the models currently inherit from a base inherited model.
Current generic function
(Create, Update, Delete functions are similar):
public T Get<T>(string documentNr, string type, string part, string version) where T : InheritedModel, new()
{
try
{
T model = new T();
if (typeof(T) == typeof(InheritedModel))
{
using (var repo = new InheritedModelConsumer(ref _helper))
{
model = (T)repo.Get(documentNr, type, part, version);
}
}
else if (typeof(T) == typeof(FooModel))
{
using (var repo = new FooModelConsumer(ref _helper))
{
model = (T)(object)repo.Get(documentNr, type, part, version);
}
}
else if (typeof(T) == typeof(ComponentModel))
{
using (var repo = new ComponentModelConsumer(ref _helper))
{
model = (T)(object)repo.Get(documentNr, type, part, version);
}
}
else if (typeof(T) == typeof(BarModel))
{
using (var repo = new BarModelConsumer(ref _helper))
{
model = (T)(object)repo.Get(documentNr, type, part, version);
}
}
... and so on
... and so on
...
else
throw new Exception("Type T structure not defined");
return model;
}
catch (Exception)
{
throw;
}
finally
{
_helper.Dispose();
}
}
This does work, but if it is possible I am looking for something where I can say at run time, "oh I have this object of Type T, and well since I know the functions all have the same inputs I'm going to instantiate this consumer of Type TConsumer, call consumer.Get(inputs), and then return an object of T to whatever API controller called me."
Edit
Example of a simple consumer class in use
internal sealed class FooConsumer : RepositoryConsumer<Foo, FooRepository, FooFilter>
{
public FooConsumer(ref SqlHelper helper) : base(ref helper) { }
public List<Foo> GetAll(string token)
{
return _repo.Get().Where(x => Extensions.StringContainsToken(x.AccountName, token)).ToList();
}
}
Repository Consumer that all consumers inherit from .
T is the model, K is the Repository (custom ORM class), and O is Filter for the WHERE clause the ORM executes.
public abstract class RepositoryConsumer<T, K, O> : IDisposable, IRepositoryConsumer<T> where T : class, new() where K : Repository<T, O>, new() where O : QueryFilter, new()
{
/// <summary>
/// Repository instance
/// </summary>
protected K _repo;
/// <summary>
/// Only constructor avaialble. MUst pass SqlHelper instance for transaction support
/// </summary>
/// <param name="sql"></param>
public RepositoryConsumer(ref SqlHelper sql)
{
_repo = Activator.CreateInstance(typeof(K), new object[] { sql }) as K;
}
/// <summary>
/// Allow consumer initializations in using statements
/// </summary>
public void Dispose()
{
}
/// <summary>
/// Create instance of T
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public virtual int Create(T data)
{
return _repo.Create(data);
}
/// <summary>
/// Bulk create instances of T
/// </summary>
/// <param name="contract"></param>
/// <returns></returns>
public virtual int Create(BaseBulkable<T> contract)
{
return _repo.BulkCreate(contract);
}
/// <summary>
/// Get an instance of T based on a single PK field id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public virtual T Get(long id)
{
return _repo.Get(id);
}
/// <summary>
/// Gets all instances of T
/// </summary>
/// <returns></returns>
public virtual List<T> GetAll()
{
return _repo.Get();
}
/// <summary>
/// Updates an instance of T
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public virtual int Update(T data)
{
return _repo.Update(data);
}
/// <summary>
/// Updates an instance of T based on a single PK field id
/// </summary>
/// <param name="id"></param>
/// <param name="data"></param>
/// <returns></returns>
public virtual int Update(long id, T data)
{
return _repo.Update(id, data);
}
/// <summary>
/// Deletes an instance of T
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public virtual int Delete(T data)
{
return _repo.Delete(data);
}
/// <summary>
/// Deletes an instance of T based on a single PK field id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public virtual int Delete(long id)
{
return _repo.Delete(id);
}
}

ASP.Net Web API - How to unregister custom action filter?

I have developed a custom action filter-LogActionWebApiFilter for Web API 2:
namespace Utility.Logger.WebApi.RequestResponse.Attributes
{
/// <summary>
/// Log action web API action filter
/// </summary>
internal class LogActionWebApiFilter : ActionFilterAttribute
{
#region Private Fields
/// <summary>
/// The current identifier
/// </summary>
private static Guid currentId;
/// <summary>
/// The request start time
/// </summary>
private static DateTime requestStartTime;
/// <summary>
/// The logger
/// </summary>
private IEventLogger logger;
#endregion Private Fields
#region Public Constructors
/// <summary>
/// Initializes a new instance of the <see cref="LogActionWebApiFilter"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
public LogActionWebApiFilter(IEventLogger logger)
{
this.logger = logger;
}
#endregion Public Constructors
#region Public overrided Methods
/// <summary>
/// Occurs after the action method is invoked.
/// </summary>
/// <param name="actionExecutedContext">The action executed context.</param>
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext.ActionContext.ActionDescriptor.GetCustomAttributes<NoLogWebApiFilter>().Any())
{
return;
}
// Some business logic
this.logger.DebugFormat("API Call of {0}()", actionExecutedContext.ActionContext.ActionDescriptor.ActionName);
}
/// <summary>
/// Occurs before the action method is invoked.
/// </summary>
/// <param name="actionContext">The action context.</param>
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ActionDescriptor.GetCustomAttributes<NoLogWebApiFilter>().Any())
{
return;
}
// Some business logic
}
#endregion Public overrided Methods
}
}
Now, I want to register/unregister this custom action filter run-time later after application started, based on a flag.
Here is my filter registration code:
public void StartLogging()
{
var existingFilter
= System.Web.Http.GlobalConfiguration.Configuration.Filters.FirstOrDefault(
f =>
f.Instance.GetType().ToString().Equals(typeof(LogActionWebApiFilter).ToString()
));
if (existingFilter == null)
{
System.Web.Http.GlobalConfiguration.Configuration.Filters.Add(new LogActionWebApiFilter(this.logger));
}
}
Here is my filter unregistration code:
public void StopLogging()
{
var existingFilter
= System.Web.Http.GlobalConfiguration.Configuration.Filters.FirstOrDefault(
f =>
f.Instance.GetType().ToString().Equals(typeof(LogActionWebApiFilter).ToString()
));
if (existingFilter != null)
{
System.Web.Http.GlobalConfiguration.Configuration.Filters.Remove(existingFilter.Instance);
existingFilter = null;
}
}
Problem :-
On each Application_BeginRequest()-event, I check the flag (has some business logic), and based on value of the flag I call respective method out of above 2 methods.
However, any of the above method doesn't work.
Even filter is added into System.Web.Http.GlobalConfiguration.Configuration.Filters, but OnActionExecuting() / OnActionExecuted() any of them are not invoked.
Please guide me, what's I'm doing wrong here.
Thanks in advance,
Balaguru.
It's not possible to control the action filter in Application_BeginRequest() event. Action filters are global Filters which can only register at Application_Start().
I would suggest , you can add or remove filter through configuration file.
<configSections>
<section name="filters" type="ConfigurableFilters.FiltersSettings, AssemblyName "/>
</configSections>
<filters>
<add type="ConfigurableFilters.BarFilter, AssemblyName" />
<add type="ConfigurableFilters.FooFilter, AssemblyName" />
</filters>
In that case you'll need a ConfigurationElement.
Public class FilterAction : ConfigurationElement
{
[ConfigurationProperty("type", IsRequired = true, IsKey = true)]
public string Type
{
get { return base["type"] as string; }
set { base["type"] = value; }
}
}
And a ConfigurationElementCollection.
public class FilterActionCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{ return new FilterAction();}
protected override object GetElementKey(ConfigurationElement element)
{ return ((FilterAction) element).Type;}
}
And a ConfigurationSection.
public class FiltersSettings : ConfigurationSection
{
public static FiltersSettings Settings
{
get {
var section = ConfigurationManager.GetSection("filters")
as FiltersSettings;
return section ?? new FiltersSettings();
}
}
[ConfigurationProperty("", IsDefaultCollection = true)]
public FilterActionCollection Filters
{
get { return base[_filtersProperty] as FilterActionCollection; }
set { base[_filtersProperty] = value; }
}
private readonly ConfigurationProperty _filtersProperty =
new ConfigurationProperty(
null, typeof (FilterActionCollection), null,
ConfigurationPropertyOptions.IsDefaultCollection);
}
One way to apply the configured filters is to use the following code during application startup:
var filters = FiltersSettings.Settings.Filters;
foreach (var filter in filters.Cast<FilterAction>())
{
var filterType = Type.GetType(filter.Type);
GlobalFilters.Filters.Add(Activator.CreateInstance(filterType));
}
The simplest answer here would be to add a toggle value to Request.Properties and interrogate this in your logging code.
public void StartLogging()
{
// Check the value does not already exist etc. and either add or modify.
if (actionContext.Request.Properties.ContainsKey("ShouldLog")) {
actionContext.Request.Properties["ShouldLog"] = true;
}
else {
actionContext.Request.Properties.Add("ShouldLog", true);
}
}
In your Logger:
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.Request.Properties.ContainsKey("ShouldLog"))
{
// Parse and check the value of ShouldLog before returning when false.
return;
}
// Some business logic
}

ASP.NET Identity - Custom role validation not being called

I am trying to the create a custom RoleValidator object to validate my custom IdentityRole. I have created an ApplicaitonRoleValidator class that inherits from RoleValidator and set this as the RoleValidator in my ApplicationRoleManager class. But when I create new role the validation function ValidateAsync is never called.
I have tried looking at similar questions implementing UserValidator like How can customize Asp.net Identity 2 username already taken validation message?
and this one ASP.NET Identity - setting UserValidator does nothing but cannot get it to work.
/// <summary>
/// Custom role validator, used to validate new instances of ApplicationRole that are added to the system.
/// </summary>
/// <typeparam name="TRole">The type of the role.</typeparam>
public class ApplicationRoleValidator<TRole> : RoleValidator<TRole> where TRole : ApplicationRole
{
private RoleManager<TRole, string> Manager { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ApplicationRoleValidator" /> class.
/// </summary>
/// <param name="manager">The manager.</param>
public ApplicationRoleValidator(RoleManager<TRole, string> manager) : base(manager)
{
Manager = manager;
}
/// <summary>
/// Validates a role before saving.
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
/// <exception cref="System.ArgumentNullException">item</exception>
public override async Task<IdentityResult> ValidateAsync(TRole item)
{
if (item == null)//<= break point here never reached.
{
throw new ArgumentNullException(nameof(item));
}
var rslt = base.ValidateAsync(item);
if (rslt.Result.Errors.Any())
{//return error if found
return IdentityResult.Failed(rslt.Result.Errors.ToArray());
}
var errors = new List<string>();
//validate the min num of members
if (role.MinimumNumberOfMembers < 0)
{
errors.Add(string.Format(CultureInfo.CurrentCulture, "最小数は0以上でなければなりません。"));
}
return errors.Count > 0 ? IdentityResult.Failed(errors.ToArray()) : IdentityResult.Success;
}
}
ApplicationRoleManager where the custom RoleValidator is set during create. I can break on that line so I know it is being called.
public class ApplicationRoleManager : RoleManager<ApplicationRole, string>
{
/// <summary>
/// Initializes a new instance of the <see cref="ApplicationRoleManager"/> class.
/// </summary>
/// <param name="store"></param>
public ApplicationRoleManager(IRoleStore<ApplicationRole, string> store)
: base(store)
{
}
/// <summary>
/// Creates the specified options.
/// </summary>
/// <param name="options">The options.</param>
/// <param name="context">The context.</param>
/// <returns></returns>
public static ApplicationRoleManager Create(IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context)
{
var manager = new ApplicationRoleManager(new ApplicationRoleStore(context.Get<MyContext>()));
manager.RoleValidator = new ApplicationRoleValidator<ApplicationRole>(manager);
return manager;
}
}
public class ApplicationRole : IdentityRole<string, ApplicationUserRole>
{
public bool IsSystemGroup { get; set; }
public string Description { get; set; } = "";
public int MinimumNumberOfMembers { get; set; }
}
public class ApplicationRoleStore : RoleStore<ApplicationRole, string, ApplicationUserRole>
{
public ApplicationRoleStore(MyContext context)
: base(context)
{
}
}
The role is being created by call to Create on the ApplicationRoleManager
var store = new ApplicationRoleStore(new MyContext());
var manager = new ApplicationRoleManager(store);
manager.Create(group);
You are setting the ApplicationRoleValidator as RoleValidator of ApplicationRoleManager in Create method of ApplicationRoleManager. In the 3 last lines of code you posted, you are newing an instance of ApplicationRoleManager. This instance of ApplicationRoleManager gets the default RoleValidator.
If you want to new an instance of ApplicationRoleManager you have to put that logic inside constructor
public ApplicationRoleManager(IRoleStore<ApplicationRole, string> store) : base(store)
{
RoleValidator = new ApplicationRoleValidator<ApplicationRole>(this);
}

MVC web API & jsonp

I'm trying to find a way to wrap a a json response with a callback for jsonp. The problem I am running into though is I don't want to use the 'JsonResult' class to construct the response as it wraps it with its own object where as if I return the model directly it is properly serialized to json.
So far I have tried using the a 'ActionFilter' but couldn't find out how I could wrap the result after the action executed.
Any direction at all would be appreciated
I've been down this road, and can offer the following code which encapsulates JsonP calls into an ActionResult, adds a method to your Controller which allows you to prioritize the type of ActionResult you want, and a couple of extension methods to glue it all together. The only requirement is to consistently name your callback parameter, so it can be reliably culled from the Request.
First, the derived ActionResult:
using System;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Script.Serialization;
namespace CL.Enterprise.Components.Web.Mvc
{
/// <summary>
/// Extension of JsonResult to handle JsonP requests
/// </summary>
public class JsonPResult : ActionResult
{
private JavaScriptSerializer _jser = new JavaScriptSerializer();
public Encoding ContentEncoding { get; set; }
public string ContentType { get; set; }
public object Data { get; set; }
public string JsonCallback { get; set; }
public JsonPResult() { }
/// <summary>
/// Package and return the result
/// </summary>
public override void ExecuteResult(ControllerContext Context)
{
//Context.IsChildAction
if (Context == null)
{
throw new ArgumentNullException("Context");
}
JsonCallback = Context.HttpContext.Request["callback"];
if (string.IsNullOrEmpty(JsonCallback))
{
JsonCallback = Context.HttpContext.Request["jsoncallback"];
}
if (string.IsNullOrEmpty(JsonCallback))
{
throw new ArgumentNullException("JsonP callback parameter required for JsonP response.");
}
HttpResponseBase CurrentResponse = Context.HttpContext.Response;
if (!String.IsNullOrEmpty(ContentType))
{
CurrentResponse.ContentType = ContentType;
}
else
{
CurrentResponse.ContentType = "application/json";
}
if (ContentEncoding != null)
{
CurrentResponse.ContentEncoding = ContentEncoding;
}
if (Data != null)
{
CurrentResponse.Write(string.Format("{0}({1});", JsonCallback, _jser.Serialize(Data)));
}
}
}
}
Next, the Controller extension methods:
using System.IO;
using System.Web.Mvc;
namespace CL.Enterprise.Components.Web.Mvc
{
/// <summary>
/// Extension methods for System.Web.Mvc.Controller
/// </summary>
public static class ContollerExtensions
{
/// <summary>
/// Method to return a JsonPResult
/// </summary>
public static JsonPResult JsonP(this Controller controller, object data)
{
JsonPResult result = new JsonPResult();
result.Data = data;
//result.ExecuteResult(controller.ControllerContext);
return result;
}
/// <summary>
/// Get the currently named jsonp QS parameter value
/// </summary>
public static string GetJsonPCallback(this Controller controller)
{
return
controller.ControllerContext.RequestContext.HttpContext.Request.QueryString["callback"] ??
controller.ControllerContext.RequestContext.HttpContext.Request.QueryString["jsoncallback"] ??
string.Empty;
}
}
}
Finally, add this method to your Controller:
/// <summary>
/// Gets an ActionResult, either as a jsonified string, or rendered as normally
/// </summary>
/// <typeparam name="TModel">Type of the Model class</typeparam>
/// <param name="UsingJson">Do you want a JsonResult?</param>
/// <param name="UsingJsonP">Do you want a JsonPResult? (takes priority over UsingJson)</param>
/// <param name="Model">The model class instance</param>
/// <param name="RelativePathToView">Where in this webapp is the view being requested?</param>
/// <returns>An ActionResult</returns>
public ActionResult GetActionResult<T>(T Model, bool UsingJsonP, bool UsingJson, string RelativePathToView)
{
string ViewAsString =
this.RenderView<T>(
RelativePathToView,
Model
);
if (UsingJsonP) //takes priority
{
string Callback = this.GetJsonPCallback();
JsonPResult Result = this.JsonP(ViewAsString.Trim());
return Result;
}
if (UsingJson)//secondary
{
return Json(ViewAsString.Trim(), JsonRequestBehavior.AllowGet);
}
return View(Model); //tertiary
}

How can I make method signature caching?

I'm building an app in .NET and C#, and I'd like to cache some of the results by using attributes/annotations instead of explicit code in the method.
I'd like a method signature that looks a bit like this:
[Cache, timeToLive=60]
String getName(string id, string location)
It should make a hash based on the inputs, and use that as the key for the result.
Naturally, there'd be some config file telling it how to actually put in memcached, local dictionary or something.
Do you know of such a framework?
I'd even be interested in one for Java as well
With CacheHandler in Microsoft Enterprise Library you can easily achieve this.
For instance:
[CacheHandler(0, 30, 0)]
public Object GetData(Object input)
{
}
would make all calls to that method cached for 30 minutes. All invocations gets a unique cache-key based on the input data and method name so if you call the method twice with different input it doesn't get cached but if you call it >1 times within the timout interval with the same input then the method only gets executed once.
I've added some extra features to Microsoft's code:
My modified version looks like this:
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Web;
using System.Web.Caching;
using System.Web.UI;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.Unity.InterceptionExtension;
namespace Middleware.Cache
{
/// <summary>
/// An <see cref="ICallHandler"/> that implements caching of the return values of
/// methods. This handler stores the return value in the ASP.NET cache or the Items object of the current request.
/// </summary>
[ConfigurationElementType(typeof (CacheHandler)), Synchronization]
public class CacheHandler : ICallHandler
{
/// <summary>
/// The default expiration time for the cached entries: 5 minutes
/// </summary>
public static readonly TimeSpan DefaultExpirationTime = new TimeSpan(0, 5, 0);
private readonly object cachedData;
private readonly DefaultCacheKeyGenerator keyGenerator;
private readonly bool storeOnlyForThisRequest = true;
private TimeSpan expirationTime;
private GetNextHandlerDelegate getNext;
private IMethodInvocation input;
public CacheHandler(TimeSpan expirationTime, bool storeOnlyForThisRequest)
{
keyGenerator = new DefaultCacheKeyGenerator();
this.expirationTime = expirationTime;
this.storeOnlyForThisRequest = storeOnlyForThisRequest;
}
/// <summary>
/// This constructor is used when we wrap cached data in a CacheHandler so that
/// we can reload the object after it has been removed from the cache.
/// </summary>
/// <param name="expirationTime"></param>
/// <param name="storeOnlyForThisRequest"></param>
/// <param name="input"></param>
/// <param name="getNext"></param>
/// <param name="cachedData"></param>
public CacheHandler(TimeSpan expirationTime, bool storeOnlyForThisRequest,
IMethodInvocation input, GetNextHandlerDelegate getNext,
object cachedData)
: this(expirationTime, storeOnlyForThisRequest)
{
this.input = input;
this.getNext = getNext;
this.cachedData = cachedData;
}
/// <summary>
/// Gets or sets the expiration time for cache data.
/// </summary>
/// <value>The expiration time.</value>
public TimeSpan ExpirationTime
{
get { return expirationTime; }
set { expirationTime = value; }
}
#region ICallHandler Members
/// <summary>
/// Implements the caching behavior of this handler.
/// </summary>
/// <param name="input"><see cref="IMethodInvocation"/> object describing the current call.</param>
/// <param name="getNext">delegate used to get the next handler in the current pipeline.</param>
/// <returns>Return value from target method, or cached result if previous inputs have been seen.</returns>
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
lock (input.MethodBase)
{
this.input = input;
this.getNext = getNext;
return loadUsingCache();
}
}
public int Order
{
get { return 0; }
set { }
}
#endregion
private IMethodReturn loadUsingCache()
{
//We need to synchronize calls to the CacheHandler on method level
//to prevent duplicate calls to methods that could be cached.
lock (input.MethodBase)
{
if (TargetMethodReturnsVoid(input) || HttpContext.Current == null)
{
return getNext()(input, getNext);
}
var inputs = new object[input.Inputs.Count];
for (int i = 0; i < inputs.Length; ++i)
{
inputs[i] = input.Inputs[i];
}
string cacheKey = keyGenerator.CreateCacheKey(input.MethodBase, inputs);
object cachedResult = getCachedResult(cacheKey);
if (cachedResult == null)
{
var stopWatch = Stopwatch.StartNew();
var realReturn = getNext()(input, getNext);
stopWatch.Stop();
if (realReturn.Exception == null && realReturn.ReturnValue != null)
{
AddToCache(cacheKey, realReturn.ReturnValue);
}
return realReturn;
}
var cachedReturn = input.CreateMethodReturn(cachedResult, input.Arguments);
return cachedReturn;
}
}
private object getCachedResult(string cacheKey)
{
//When the method uses input that is not serializable
//we cannot create a cache key and can therefore not
//cache the data.
if (cacheKey == null)
{
return null;
}
object cachedValue = !storeOnlyForThisRequest ? HttpRuntime.Cache.Get(cacheKey) : HttpContext.Current.Items[cacheKey];
var cachedValueCast = cachedValue as CacheHandler;
if (cachedValueCast != null)
{
//This is an object that is reloaded when it is being removed.
//It is therefore wrapped in a CacheHandler-object and we must
//unwrap it before returning it.
return cachedValueCast.cachedData;
}
return cachedValue;
}
private static bool TargetMethodReturnsVoid(IMethodInvocation input)
{
var targetMethod = input.MethodBase as MethodInfo;
return targetMethod != null && targetMethod.ReturnType == typeof (void);
}
private void AddToCache(string key, object valueToCache)
{
if (key == null)
{
//When the method uses input that is not serializable
//we cannot create a cache key and can therefore not
//cache the data.
return;
}
if (!storeOnlyForThisRequest)
{
HttpRuntime.Cache.Insert(
key,
valueToCache,
null,
System.Web.Caching.Cache.NoAbsoluteExpiration,
expirationTime,
CacheItemPriority.Normal, null);
}
else
{
HttpContext.Current.Items[key] = valueToCache;
}
}
}
/// <summary>
/// This interface describes classes that can be used to generate cache key strings
/// for the <see cref="CacheHandler"/>.
/// </summary>
public interface ICacheKeyGenerator
{
/// <summary>
/// Creates a cache key for the given method and set of input arguments.
/// </summary>
/// <param name="method">Method being called.</param>
/// <param name="inputs">Input arguments.</param>
/// <returns>A (hopefully) unique string to be used as a cache key.</returns>
string CreateCacheKey(MethodBase method, object[] inputs);
}
/// <summary>
/// The default <see cref="ICacheKeyGenerator"/> used by the <see cref="CacheHandler"/>.
/// </summary>
public class DefaultCacheKeyGenerator : ICacheKeyGenerator
{
private readonly LosFormatter serializer = new LosFormatter(false, "");
#region ICacheKeyGenerator Members
/// <summary>
/// Create a cache key for the given method and set of input arguments.
/// </summary>
/// <param name="method">Method being called.</param>
/// <param name="inputs">Input arguments.</param>
/// <returns>A (hopefully) unique string to be used as a cache key.</returns>
public string CreateCacheKey(MethodBase method, params object[] inputs)
{
try
{
var sb = new StringBuilder();
if (method.DeclaringType != null)
{
sb.Append(method.DeclaringType.FullName);
}
sb.Append(':');
sb.Append(method.Name);
TextWriter writer = new StringWriter(sb);
if (inputs != null)
{
foreach (var input in inputs)
{
sb.Append(':');
if (input != null)
{
//Diffrerent instances of DateTime which represents the same value
//sometimes serialize differently due to some internal variables which are different.
//We therefore serialize it using Ticks instead. instead.
var inputDateTime = input as DateTime?;
if (inputDateTime.HasValue)
{
sb.Append(inputDateTime.Value.Ticks);
}
else
{
//Serialize the input and write it to the key StringBuilder.
serializer.Serialize(writer, input);
}
}
}
}
return sb.ToString();
}
catch
{
//Something went wrong when generating the key (probably an input-value was not serializble.
//Return a null key.
return null;
}
}
#endregion
}
}
Microsoft deserves most credit for this code. We've only added stuff like caching at request level instead of across requests (more useful than you might think) and fixed some bugs (e.g. equal DateTime-objects serializing to different values).
To do exactly what you are describing, i.e. writing
public class MyClass {
[Cache, timeToLive=60]
string getName(string id, string location){
return ExpensiveCall(id, location);
}
}
// ...
MyClass c = new MyClass();
string name = c.getName("id", "location");
string name_again = c.getName("id", "location");
and having only one invocation of the expensive call and without needing to wrap the class with some other code (f.x. CacheHandler<MyClass> c = new CacheHandler<MyClass>(new MyClass());) you need to look into an Aspect Oriented Programming framework. Those usually work by rewriting the byte-code, so you need to add another step to your compilation process - but you gain a lot of power in the process. There are many AOP-frameworks, but PostSharp for .NET and AspectJ are among the most popular. You can easily Google how to use those to add the caching-aspect you want.

Categories

Resources