It seems IE doesn’t print canvas elements which are absolutely positioned in the correct place. What a surprise.
This problem came about when i tried to print some of my jqPlot graphs, as it uses absolutely positioned canvas tags for the data and axes.
There’s probably a much easier solution to this, but i couldn’t find it.
Heres some mega-hax that makes things work:
Update: This requires jQuery 1.4.2+
(function($) {
$.fn.CanvasHack = function() {
var canvases = this.find('canvas').filter(function() {
return $(this).css('position') == 'absolute';
});
canvases.wrap(function() {
var canvas = $(this);
var div = $('<div />').css({
position: 'absolute',
top: canvas.css('top'),
left: canvas.css('left')
});
canvas.css({
top: '0',
left: '0'
});
return div;
});
return this;
};
})(jQuery);
Call it after your graph setup code, like this:
$('body').CanvasHack();
Quick Link: Download the Example Code.
OK so here’s my problem,
My client wants most of the actions in their system to work slightly differently based on the authorization role of the logged in user. For example, Moderators can edit a user, but only some fields, Administrators can edit all fields for a user.
I don’t want to have different URIs for each action, I don’t want to have to faff about with routes, I want to do this transparently, with as little pain as possible.
One approach would be to have a switch inside the method (on the current users role) and delegate the action to the private method specific to that role. However this suffers several problems:
The main action method that gets called by the controller can only have a single Resource type, which means if you want different resource types per role, youre in a mess. You therefore have to accept FormCollection, then do binding and validation manually etc. etc. urgh. You also end up with 4 methods for each action. This quickly bloats your controller
So, what can we do about this? We’ll in this post I’ll show you how you can leverage the power of you IoC container to swap out the controller instance with one specific to the current user’s role.
Here’s what you do:
Create an IXXXController for each of your controllers you want to swap based on role, like this:
public interface IUserController : IController
{
ActionResult Edit(int id);
}
Then, create your multiple implementations of this controller:
public class UserController_ForAdministrator : Controller, IUserController
{
[AcceptVerbs(HttpVerbs.Get)]
public override ActionResult Edit(int id)
{
// this would really come from a repos
var resource = new EditUser_AdministratorResource{ EmailAddress = "me@here.com"};
return View("Edit_Administrator", resource);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, EditUser_AdministratorResource resource)
{
// do admin stuff here
return RedirectToAction("Index");
}
}
public class UserController_ForModerator : HomeController
{
[AcceptVerbs(HttpVerbs.Get)]
public override ActionResult Edit(int id)
{
// this would really come from a repos
var resource = new EditUser_ModeratorResource{ EmailAddress = "me@here.com"};
return View("Edit_Moderator", resource);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, EditUser_ModeratorResource resource)
{
// save moderator stuff here
return RedirectToAction("Index");
}
}
Next, you need a custom IControllerFactory. Note in the example below I have forgone any error checking or optimisation for brevity, I have also omitted any Namespace checking.
public class CustomControllerFactory : IControllerFactory
{
private readonly IEnumerable<Type> controllerTypes;
public CustomControllerFactory()
{
this.controllerTypes = Assembly.GetExecutingAssembly().GetTypes().Where(t => (t.IsInterface || !t.IsAbstract) && t.Name.EndsWith("Controller"));
}
public IController CreateController(RequestContext requestContext, string controllerName)
{
var currentUser = ObjectFactory.GetInstance<User>();
var controllerInterface = this.controllerTypes.Where(t => t.IsInterface && t.Name.Equals("I" + controllerName + "Controller", StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
if (controllerInterface != null)
{
return ObjectFactory.GetNamedInstance(controllerInterface, currentUser.Role) as IController;
}
var controllerClass = this.controllerTypes.Where(
t => t.IsClass &&
t.GetInterfaces().Contains(typeof(IController)) &&
t.Name.Equals(controllerName + "Controller", StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
if(controllerClass != null)
{
return ObjectFactory.GetInstance(controllerClass) as IController;
}
return null;
}
}
Then simply wire things up in your Application_Start:
protected void Application_Start()
{
ObjectFactory.Initialize(a =>
{
a.For<IUserController>()
.Use<UserController_ForModerator>()
.Named("Moderator");
a.For<IUserController>()
.Use<UserController_ForAdministrator>()
.Named("Administrator");
// this is obviously a hack for the purposes of this post
// here you would really be loading your real user
a.For<User>()
.Use(c => new User { Role = "Moderator" });
});
ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());
}
The controller wiring can easily be done by convention in StructureMap (and other containers) so you don’t have to list each one manually.
Download the Example Code here, aren’t I nice to you?
For those of you still running IIS6 who would like to have nice SEO friendly URLs, one option is to use ISAPI Rewrite.
First, add a “.mvc” handler mapping to the asp.net dll as per Phil Haacks post.
Obviously you’ll need ISAPI Rewrite installed, then you can create the following .htaccess file:
RewriteEngine on
AllowOverride All
# Ignore Assets folder
RewriteRule ^assets/(.*?)$ /assets/$1 [NC,L]
# Rewrite everything else to have .mvc on the end of the controller name
RewriteRule ^([^/]*)(/(?:.*?)*)?$ /$1.mvc$2 [NC,L]
I keep all my CSS, images and javascript under the /assets folder. If you have these elsewhere, you probably want to modify the first rewrite rule for your specific location.
N.B. you can probably ignore these folders in a much more elegant way, perhaps with a RewriteCond on the 2nd Rule. I’m not a master of this syntax yet so this will do for the time being, it works!
Some additional rules you may also want are the following:
# Rewrite favicon
RewriteRule ^favicon.ico(.*?)$ /assets/images/icons/favicon.ico [NC,L]
# Rewrite appleicon
RewriteRule ^apple-touch-icon.png(.*?)$ /assets/images/icons/apple-touch-icon.png [NC,L]
# Ignore robots.txt
RewriteRule ^robots.txt(.*?)$ /robots.txt [NC,L]
Adding the following to your global.asax:
protected void Application_BeginRequest()
{
if (this.Request.AppRelativeCurrentExecutionFilePath.Contains(".mvc"))
this.Context.RewritePath(this.Request.Url.PathAndQuery.Replace(".mvc", string.Empty));
}
means you don’t need to have the .mvc in your route configurations, which keeps things nice and tidy!
jqPlot is an awesome javascript graphing library, supporting loads of cool features. Except clickable bar charts.
Here is a plugin which allows you to capture click events for bar charts.
Use it like this:
$.jqplot('Graph', data, {
barClickable: {
onClick: function(i, j, data){
alert("Clicked series: " + i + ", data point: " + j + ", data: " + data);
}
}
});
This won’t work on horizontal or stacked graphs without modification, but it would be a fairly trivial change.
The code is available on github.
The simplest way I’ve found to encrypt data with NHibernate is to use a custom UserType which encrypts/decrypts the data on read/write.
As this post on stackoverflow demonstrates, it’s rather easy to make such a UserType. However, what I didn’t want to have to do is make a different UserType for each different property type that I want to encrypt.
Enter Encrypted<T>:
public class Encrypted<T> : PrimitiveType
{
private readonly IEncryptionService encrypter;
private readonly IBinaryConverter converter;
public Encrypted() : base(new BinarySqlType())
{
this.encrypter = ServiceLocator.Current.GetInstance<IEncryptionService>();
this.converter = ServiceLocator.Current.GetInstance<IBinaryConverter>();
}
public override string Name
{
get { return typeof (T).Name; }
}
public override Type ReturnedClass
{
get { return typeof (T); }
}
public override Type PrimitiveClass
{
get { return typeof (T); }
}
public override object DefaultValue
{
get { return default(T); }
}
public override void Set(IDbCommand cmd, object value, int index)
{
var serialized = this.converter.Serialize(value);
var encrypted = this.encrypter.Encrypt(serialized);
((IDataParameter) cmd.Parameters[index]).Value = encrypted;
}
public override object Get(IDataReader rs, int index)
{
if (rs.IsDBNull(index))
return null;
var encrypted = rs[index] as byte[];
var decrypted = this.encrypter.Decrypt(encrypted);
var deserialized = this.converter.Deserialize(decrypted);
return deserialized;
}
public override object Get(IDataReader rs, string name)
{
return this.Get(rs, rs.GetOrdinal(name));
}
public override object FromStringValue(string xml)
{
if (xml == null)
return null;
if (xml.Length % 2 != 0)
throw new ArgumentException("The string is not a valid xml representation of a binary content.", "xml");
var bytes = new byte[xml.Length / 2];
for (var i = 0; i < bytes.Length; i++)
{
var hexStr = xml.Substring(i * 2, (i + 1) * 2);
bytes[i] = (byte) (byte.MinValue + byte.Parse(hexStr, NumberStyles.HexNumber, CultureInfo.InvariantCulture));
}
var decrypted = this.encrypter.Decrypt(bytes);
var deserialized = this.converter.Deserialize(decrypted);
return deserialized;
}
public override string ObjectToSQLString(object value, Dialect dialect)
{
var bytes = value as byte[];
if (bytes == null)
return "NULL";
var builder = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
string hexStr = (bytes[i] - byte.MinValue).ToString("x", CultureInfo.InvariantCulture);
if (hexStr.Length == 1)
builder.Append('0');
builder.Append(hexStr);
}
return builder.ToString();
}
}
The implementations of IBinaryConverter and IEncryptionService aren’t important for the purposes of this post, here are their signatures so you can implement then how you like:
public interface IBinaryConverter
{
byte[] Serialize(object obj);
object Deserialize(byte[] bytes);
}
public interface IEncryptionService
{
byte[] Encrypt(byte[] plain);
byte[] Decrypt(byte[] cipher);
}
I try to use natural IDs in my URIs wherever possible, like:
http://www.mydomain.com/some/resource/description
However sometimes this is not always practical, and for many of the non-public facing applications I work on it is simply unnecessary. In these cases I tend to use the GUID ID of the requested resource, like:
http://www.mydomain.com/resources/95801FAD-DA29-434F-B4EA-175C76266BB7
These 36 character GUIDs are rather ugly, here is a solution to shorten them down to 22 chars, which looks like:
http://www.mydomain.com/resources/DV0Ft9JPqkGV2Xne0Q64XA
not perfect, but much better.
N.B. My next step to shorten them further is to use a custom GUID algorithm with no machine-specific part to it, which should remove a significant number of bits. In the meantime, however…
First, we’ll introduce a ShortGuid struct based on the class described here.
public struct ShortGuid
{
private readonly Guid guid;
public ShortGuid(Guid guid)
{
this.guid = guid;
}
public static bool TryParse(string guid, out ShortGuid shortGuid)
{
Guid parsed;
try
{
parsed = new Guid(Convert.FromBase64String(guid.Replace("_", "/").Replace("-", "+") + "=="));
}
catch
{
try
{
parsed = new Guid(guid);
}
catch
{
shortGuid = new ShortGuid();
return false;
}
}
shortGuid = new ShortGuid(parsed);
return true;
}
public override string ToString()
{
return Convert.ToBase64String(guid.ToByteArray())
.Substring(0, 22)
.Replace("/", "_")
.Replace("+", "-");
}
public Guid ToGuid()
{
return this.guid;
}
public static implicit operator string(ShortGuid guid)
{
return guid.ToString();
}
public static implicit operator Guid(ShortGuid shortGuid)
{
return shortGuid.guid;
}
}
All pretty easy stuff. The difficulty comes when trying to get MVC to use and recognise it.
The easiest solution is to use the ShortGuid class on your resources, however the whole “short guid” concept is purely for HTTP. It certainly has no place in your domain, and has questionable presence in your resources (view models).
The next easiest solution is to use a custom route which replaces any GUID route values just before rendering, here is the code:
public class ShortGuidReplacingRoute : Route
{
public ShortGuidReplacingRoute(string url, IRouteHandler routeHandler) : base(url, routeHandler)
{
}
public ShortGuidReplacingRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler) : base(url, defaults, routeHandler)
{
}
public ShortGuidReplacingRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler) : base(url, defaults, constraints, routeHandler)
{
}
public ShortGuidReplacingRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler) : base(url, defaults, constraints, dataTokens, routeHandler)
{
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
var dictionary = new RouteValueDictionary();
foreach(var kvp in values)
{
if (kvp.Value.GetType() == typeof (Guid))
dictionary.Add(kvp.Key, new ShortGuid((Guid)kvp.Value));
else
dictionary.Add(kvp.Key, kvp.Value);
}
return base.GetVirtualPath(requestContext, dictionary);
}
}
You then need to use this class when registering your routes, I made this convenient extension method to make life easier:
public static class RouteExtensions
{
public static Route MapGuidReplacingRoute(this RouteCollection routes, string name, string url, object defaults)
{
Route route = new ShortGuidReplacingRoute(url, new MvcRouteHandler());
route.Defaults = new RouteValueDictionary(defaults);
route.Constraints = new RouteValueDictionary();
routes.Add(name, route);
return route;
}
}
which you can use almost as normal, like so:
routes.MapGuidReplacingRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index" }
);
Then all you need is a ShortGuid model binder:
public class ShortGuidModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if(bindingContext.ValueProvider.ContainsKey(bindingContext.ModelName))
{
ShortGuid shortGuid;
var valid = ShortGuid.TryParse(bindingContext.ValueProvider[bindingContext.ModelName].AttemptedValue, out shortGuid);
if (valid)
{
if(bindingContext.ModelType == typeof(Guid))
return shortGuid.ToGuid();
if (bindingContext.ModelType == typeof(ShortGuid))
return shortGuid;
}
}
return Guid.Empty;
}
}
Which you register in the normal way:
ModelBinders.Binders[typeof (Guid)] = new ShortGuidModelBinder();
Job done
There are plenty of discussions on why you should do this, which I’m not going to cover here.
What I am interested in discussing is a solution to the endless overriding of Equals and GetHashCode in each of your entities. Not only does this pollute the object with noise, but its a nightmare to maintain.
I use GUIDs for my Id columns, which is very convenient as you don’t have to goto the database for clues as to what the next unique ID may be.
Normally, when overriding GetHashCode you have to compare all your entity’s properties and when comparing for equality, you have to do the same because transient objects don’t yet have an ID.
This got me thinking, why even bother letting NH assign the ID? If you create the ID in your entities’ constructor GetHashCode and Equals become very easily overridden and can be abstracted to a base class, like so:
public abstract class Entity
{
public virtual Guid Id { get; protected set; }
protected Entity()
{
this.Id = Guid.NewGuid();
}
public override int GetHashCode()
{
return this.Id.GetHashCode();
}
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj))
return true;
var entity = obj as Entity;
if (ReferenceEquals(null, entity))
return false;
return entity.Id.Equals(this.Id);
}
}
For this to work, you must set your NHibernate ID Generator mapping to “Assigned”, like so:
<id name="Id" type="System.Guid">
<column name="Id" />
<generator class="assigned" />
</id>
For all you optimizers out there, you can still use alternate GUID algorithms like Comb
Thoughts?
I’m fed up of writing:
<%= Html.Encode(bla) %>
throughout my views. Not only is it messy, but ASP.NET’s default behaviour of “be as insecure as possible” means you have to remember to do this everywhere. In addition to this, it simply uses:
System.Web.HttpUtility.HtmlEncode()
underneath, which isn’t particularly good at preventing XSS.
OpenRasta (a brilliant alternative to MVC which you should be using) has an excellent solution to this problem, by using a custom CSharCodeProvider to help with view compilation.
Below is a simplified version of the code OpenRasta uses, demonstrating how you can get automatic HTML encoding of all code expressions in your views. This also works for WebForms.
It uses an IoC service locator to request an arbitrary IHtmlEncoder. This allows you to use whatever encoding library you like, such as Microsoft AntiXss.
public class AutoHtmlEncodingCSharpCodeProvider : CSharpCodeProvider
{
public AutoHtmlEncodingCSharpCodeProvider()
{
}
public AutoHtmlEncodingCSharpCodeProvider(IDictionary<string, string> providerOptions) : base(providerOptions)
{
}
public override void GenerateCodeFromStatement(CodeStatement statement, TextWriter writer, CodeGeneratorOptions options)
{
var codeExpressionStatement = statement as CodeExpressionStatement;
if (codeExpressionStatement != null)
{
var methodInvokeExpression = codeExpressionStatement.Expression as CodeMethodInvokeExpression;
if (methodInvokeExpression != null)
{
if (methodInvokeExpression.Method.MethodName == "Write" && methodInvokeExpression.Parameters.Count == 1)
{
var parameter = methodInvokeExpression.Parameters[0] as CodeSnippetExpression;
if ((parameter != null) && (!string.IsNullOrEmpty(parameter.Value)))
parameter.Value = "global::" + GetType().FullName + ".PreProcessObject(this, " + parameter.Value + ")";
}
}
}
base.GenerateCodeFromStatement(statement, writer, options);
}
public static string PreProcessObject(object source, object value)
{
if(value is Raw)
return ((Raw)value).Value;
var encoder = ServiceLocator.Current.TryGetInstance<IHtmlEncoder>();
if (encoder != null)
return encoder.HtmlAttributeEncode(value.ToString());
return HttpUtility.HtmlAttributeEncode(value.ToString());
}
}
public class Raw
{
public string Value { get; set; }
public static explicit operator Raw(string text)
{
return new Raw { Value = text };
}
public static implicit operator string(Raw output)
{
return output.Value;
}
}
You need to register this in your Web.config like so:
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs" warningLevel="4" type="MyAssembly.AutoHtmlEncodingCSharpCodeProvider, MyAssembly">
<providerOption name="CompilerVersion" value="v3.5" />
<providerOption name="WarnAsError" value="false" />
</compiler>
</compilers>
</system.codedom>
Then in your pages, you can do this:
<p><%= "<script>alert('i'm encoded, so i wont popup');</script>" %></p>
<p><%= (Raw)"<strong>i'm bold because im escaped with (Raw)!</strong>" %></p>
Which will be rendered as:
<script>alert(‘i’m encoded, so i wont popup’);</script>
i’m bold because im escaped with (Raw)!
There you go!
Because this code has been inspired by/copied from/a modification of code from OpenRasta, according to its license I must reproduce the copyright notice. If you also wish to use this code, you must do the same.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
In order to use the UrlHelper class in your tests, you need to construct it with a ControllerContext, which consists of an HttpRequest and an HttpResponse. Unfortunately these are quite difficult to construct, as you’ll know if you’ve ever tried. Luckily, the UrlHelper doesn’t use much of these two classes, only a few properties, making them fairly easy to mock.
The first thing you need to do is get your routes, usually declared in your Global.asax MvcApplication
var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
Then you need to mock an HttpRequest
var request = MockRepository.GenerateStub<HttpRequestBase>();
request.Stub(x => x.ApplicationPath).Return("/");
request.Stub(x => x.Url).Return(new Uri("http://localhost/a", UriKind.Absolute));
request.Stub(x => x.ServerVariables).Return(new System.Collections.Specialized.NameValueCollection());
Then you need to mock the HttpResponse
var response = MockRepository.GenerateStub<HttpResponseBase>();
response.Stub(x => x.ApplyAppPathModifier(Arg<string>.Is.Anything))
.Return(null)
.WhenCalled(x => x.ReturnValue = x.Arguments[0]);
Then you can simply stick these two mocks onto a mock context, and use that to create your UrlHelper instance and set them on the controller you are testing:
var context = MockRepository.GenerateStub<HttpContextBase>();
context.Stub(x => x.Request).Return(request);
context.Stub(x => x.Response).Return(response);
var subjectUnderTest = new MyController(); // this is the controller you are testing
subjectUnderTest.ControllerContext = new ControllerContext(context, new RouteData(), subjectUnderTest);
subjectUnderTest.Url = new UrlHelper(new RequestContext(context, new RouteData()), routes);
Job done.
Now go and use OpenRasta and not have any of these problems.
In this post I’m going to discuss one method of implementing validation into your OpenRasta handlers using an OperationInterceptor.
OpenRasta deliberately doesn’t ship with a validation framework, there are plenty out there for you to use. For the purposes of this post, I’m not using any particular validation framework, simply demonstrating the point with a generic IValidator, something like:
public interface IValidator<TResource>
{
ValidationReport Validate(object resource);
}
First, lets see a way you could do validation in an MVC sort of way:
public class EditUserHandler
{
private readonly IValidator<EditUserResource> validator;
public EditUserHandler(IValidator<EditUserResource> validator)
{
this.validator = validator;
}
public OperationResult Post(EditUserResource resource)
{
var validationReport = validator.Validate(resource);
if (!validationReport.IsValid)
return new OperationResult.BadRequest {ResponseResource = resource};
// save user details here
return new OperationResult.SeeOther {RedirectLocation = "~/ "};
}
This is probably a very familiar pattern and something you could expect to see inside all hander operations which require validation. Meh. This duplication is not only wasteful, but its unnecessary noise cluttering up your Operation and we can get rid of it.
OperationInterceptors wrap the execution of an operation, giving you three places to interact, BeforeExecute, RewriteOperation and AfterExecute. In this instance, we are only concerned with BeforeExecution.
This method is called just before the Operation in invoked, this is where we want to examine the input parameters, and check if they are valid.
For the purposes of brevity, the below code assumes there is only one parameter to your operation method; hence the FirstOrDefault();
public class ValidationOperationInterceptor : OperationInterceptor
{
private readonly IDependencyResolver resolver;
private readonly ICommunicationContext context;
public ValidationOperationInterceptor(IDependencyResolver resolver, ICommunicationContext context)
{
this.resolver = resolver;
this.context = context;
}
public override bool BeforeExecute(OpenRasta.OperationModel.IOperation operation)
{
var input = operation.Inputs.FirstOrDefault();
if(input == null)
return true;
var parameter = input.Binder.BuildObject();
var validatorType = typeof(IValidator<>).MakeGenericType(parameter.Instance.GetType());
if (!resolver.HasDependency(validatorType))
return true;
var validator = this.resolver.Resolve(validatorType) as IValidator;
var validationReport = validator.Validate(parameter.Instance);
if (validationReport.IsValid)
return true;
// add validation errors here
context.OperationResult = new OperationResult.BadRequest { ResponseResource = parameter.Instance };
return false;
}
}
We register this OperationInterceptor as follows:
ResourceSpace.Uses.CustomDependency<IOperationInterceptor, ValidationOperationInterceptor>(DependencyLifetime.Transient);
The above code assumes that the parameter binding succeeded, I’ll demonstrate a way to deal with binding failures in a future post (its very similar to the above code).
So what effect does this code have? Well, it can make your handlers super clean:
public class EditUserHandler
{
public OperationResult Post(EditUserResource resource)
{
// save user details here
return new OperationResult.SeeOther {RedirectLocation = "~/ "};
}
Your Operations are now only called when the resource is valid, excellent!
You may be seeing one obvious drawback with this approach, that being what if your resources need extra data setting on them before they get sent to the codec in the case of invalidity? When editing a user, perhaps the resource needs to also contain a list of possible authorization roles, one of which will be selected for the user. Having a single mechanism for handling validation for all types doesn’t allow you anywhere to set this extra information on the resource. Well, there is a solution I’ll be proposing in my next post.
So stay tuned