ASP.NET complex validation in business / service layer - asp.net

I am asking this because after long time searching I haven't found a good answer on this yet...
Here is what I want:
Example: I have a domain model "JobPosting" which a user should be able to change state to published, if it is still a draft. Before publishing I must not only validate the model properties I must also validate many different requirements regarding the user account, it's registered company etc. All this validation logic is put into a service layer. So far so good...
This is how my service layer looks like:
public IValidationResult ValidatePublish(JobPosting jobPosting){
...
}
public void Publish(JobPosting jobPosting){
jobPosting.State = JobPostingState.Published;
...
}
Any my controller:
public ActionResult Publish(PublishViewModel model){
...
var validationResult = _jobService.ValidatePublish(jobPosting);
if(validationResult.Success){
_jobService.Publish(jobPosting);
...
}
...
}
And here now my questions:
I want to be able to call the ValidatePublish from the controller to show validation errors in the view. However I must never be able to publish a job when validation fails.
So to have my code more robust I added a second validation check in my Publish method in service layer:
public void Publish(JobPosting jobPosting){
if(ValidatePublish(jobPosting).Success){
jobPosting.State = JobPostingState.Published;
...
}
}
but I have not such a good feeling with this approach because now I am calling the validation twice when validation is OK during each controller publish request.
What do you think. Is the second call to much? Is there a better approach?
I am asking because my whole application looks like that and if I would ever forget a validation call in controller I might end up with an not allowed domain model state in database. That's why I added the second validation check in each service method.
Thanks in advance for your thoughts on this!!!

One quick solution might be to have the Publisher class require the JobPosting and IValidationResult objects as arguments.
public void Publish(JobPosting jobPosting, IValidationResult validation)
{
if (validation.IsValid)
{
jobPosting.State = JobPostingState.Published;
// other work here...
}
}
Your Controller can then call the Validator, receive an IValidationResult and pass that back to the presentation layer if needed. Otherwise pass on to Publisher
public ActionResult Publish(PublishViewModel model)
{
var validationResult = _jobService.ValidatePublish(jobPosting);
if(validationResult.Success) _jobService.Publish(jobPosting, validationResult);
else return View("error", validationResult);
}
Edit:
A cleaner solution may be to have the Publisher class return a PublishAttempt result.
public class PublishAttempt : IValidationResult
{
public enum AttemptOutcome {get; set;}
}
public ActionResult Publish(PublishViewModel model)
{
var attempt = _jobService.Publish(jobPosting);
if (attempt.Success) return View("success");
else return View("error", attempt.ValidationResults);
}

The following just came into my mind... what do you think:
I change my service method to:
public IValidationResult Publish(JobPosting jobPosting, bool validateOnly = false){
var validationResult = ValidatePublish(jobPosting);
if(validateOnly) return validationResult;
jobPosting.State = JobPostingState.Published;
...
return validationResult;
}
And then in controller I always call only the Publish method and not the extra ValidatePublish anymore:
public ActionResult Publish(PublishViewModel model)
{
var validationResult = _jobService.Publish(jobPosting);
if(!validationResult.Success) return View("error", validationResult);
}
And when I need only simple validation I do
var validationResult = _jobService.Publish(jobPosting, true);
Is this okey to do it like that?
Or is it not good looking if a normal service call returns IValidationResult?

Related

How to require parameters in asp.net actions

How to require/validate parameters for actions. Right now I have lot of actions that looks like this (which is horrible):
public ActionResult DoSomething(string paramA, string paramB, string paramC)
{
if (string.IsNullOrWhiteSpace(paramA))
{
return JsonResult(false, "paramA is missing");
}
if (string.IsNullOrWhiteSpace(paramB))
{
return JsonResult(false, "paramB is missing");
}
if (string.IsNullOrWhiteSpace(paramC))
{
return JsonResult(false, "paramC is missing");
}
//Actual Code
}
How to encapsulte this (potentially "globally")? I know that its possible to wrap parameters to model and use ModelState.IsValid like in this post: https://stackoverflow.com/a/39538103/766304
That is maybe one step forward on same places but generally I don't that it's realistic to wrap all parameters to models everywhere (~1 class definition per 1 action method... how nice is that?).
Also this is again per action ceremony which should be handled somewhere centralized:
if (ModelState.IsValid == false)
{
return BadRequest(ModelState);
}
The easiest way to do it would be to create a model class and use [Required] attributes like this:
public class FooModel
{
[Required]
public string ParamA {get;set;}
[Required]
public string ParamB {get;set;}
[Required]
public string ParamC {get;set;}
}
And then use it in your controller like this:
public ActionResult DoSomething(FooModel model)
{
if (!ModelState.IsValid)
{
// return some errors based on ModelState
}
//Actual Code
}
If you are looking for more global approach, then i believe you could look into Action Filters and use OnActionExecuting filter and handle the validation there (haven't used that myself tho).
Here is how to do it:
How can I centralize modelstate validation in asp.net mvc using action filters?
That way your method would never be called if any of the parameters were missing.
The model annotations with [Required] [Length] and all these attributes is one of the most common ways to validate your model, specially it integrates with the Razor View engine and generates JavaScript validation as well, the same will happen if you are using EntityFramework for your back end, so this way you will have validation at the level of the UI, Controller and Data access.
You can also use Code Contracts which allows you to put pre and post conditions for your method in a nice way https://msdn.microsoft.com/en-us/library/dd264808(v=vs.110).aspx
If none of the above is still not enough, then you can add some checks in either your controller action or in your business domain service to make some business validation and return an error code if any errors found

ValueProvider never being called

I'm working with MVC 4 Web API and I have this dummy ValueProvider:
DummyValueProvider.cs
class DummyValueProvider : IValueProvider
{
public DummyValueProvider()
{
}
public bool ContainsPrefix(string prefix)
{
return true;
}
public ValueProviderResult GetValue(string key)
{
return new ValueProviderResult("testing", "testing", System.Globalization.CultureInfo.InvariantCulture);
}
}
class DummyValueProviderFactory : System.Web.Http.ValueProviders.ValueProviderFactory
{
public override IValueProvider GetValueProvider(System.Web.Http.Controllers.HttpActionContext actionContext)
{
return new DummyValueProvider();
}
}
This ValueProvider should return true for any key asked, so it will always supply a value to the model binder when it needs. The ValueProvider is registered in the WebApiConfig like this:
WebApiConfig.cs
config.Services.Add(typeof(ValueProviderFactory), new DummyValueProviderFactory());
The code compiles and runs fine.
I also have this action in the Account API controller:
AccountController.cs
public HttpResponseMessage Register(string foo) { ... }
The action gets called fine when I call it like below:
/register?foo=bar
And foo is filled with bar as expected; but if I call:
/register
The server returns 404 with the message No HTTP resource was found that matches the request URI 'http://localhost:14459/register'.
Also, I put breakpoints inside methods ContainsPrefix() and GetValue(), but they never get triggered.
What am I doing wrong? Shouldn't DummyValueProvider be providing the value testing to parameter foo?
Try this
public HttpResponseMessage Get([ValueProvider(typeof(DummyValueProviderFactory))] string foo) {... }
I higly suggest you to read this recent article to customize Web Api Binding.
Update:
After reading the article the OP was able to discover the solution. It was that using the parameter attribute [ModelBinder] was required for it to work. This was because unless the parameter is annotated, [FromUri] is assumed. Once annotated with [ModelBinder] the registered handlers are executed.

Using object Helper Methods to implement authorization rules

I have the following:-
I am working on an asset management system using Asp.net MVC4 with windows authentication enabled.
The system allow to specify what actions a group of users can do(for example certain group can have the authority to add new physical asset , while they can only read certain logical asset, and so on).
So I found that using the build-in Asp.net role management, will not allow me to have the level of flexibility I want. So I decided to do the following:-
I have created a table named “group” representing the user groups. Where users are stored in active directory.
I have created a table named ”Security Role” which indicate what are the permission levels each group have on each asset type(edit, add, delete or view)per asset type.
Then on each action methods , I will use Helper methods to implement and check if certain users are within the related group that have the required permission ,, something such as
On the Car model object I will create a new helper method
Public bool HaveReadPermison(string userName) {
//check if this user is within a group than have Read permission on CARS, //OR is within a GROUP THAT HAVE HIGHER PERMISON SUCH AS EDIT OR ADD OR //DELETE.
}
Next, On the Action method, I will check if the user has the Read permission or not by calling the action method:-
public ActionResult ViewDetails(int id) { // to view transportation asset type
Car car = repository.GetCar(id);
if (!car.HaveReadPermision(User.Identity.Name)) {
if (car == null)
return View("NotFound");
else
return View(car);
}
else
return view (“Not Authorized”);
So can anyone advice if my approach will be valid or it will cause problem I am unaware about.
Regards
In my opinion, once you have decided to use the ASP membership and role providers you can keep leveraging them also for authorization, simply using the Authorize attribute. This will also allow to restrict access by user names and roles.
What the attribute won't do is Action-based authorization. In that case there are a few options but in my opinion this could be brilliantly resolved by a Custom Action Filter based loosely on the following code:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class CheckUserPermissionsAttribute : ActionFilterAttribute
{
public string Model { get; set; }
public string Action { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var user = filterContext.HttpContext.User.Identity.Name; // or get from DB
if (!Can(user, Action, Model)) // implement this method based on your tables and logic
{
filterContext.Result = new HttpUnauthorizedResult("You cannot access this page");
}
base.OnActionExecuting(filterContext);
}
}
Yes, it is vaguely inspired to CanCan, which is a nice Ruby gem for this kind of things.
Returning Unauthorized (401) will also instruct your server to redirect to the login page if one is specified. You may want to work on that logic if you want to redirect somewhere else. In that case you should do:
filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary { { "Controller", "Home" }, { "Action", "Index" } });
and choose the appropriate controller/action pair.
You can use the attribute like this:
[CheckUserPermissions(Action = "edit", Model = "car")]
public ActionResult Edit(int id = 0)
{
//..
}
Let me know if that works nicely for you.
The approach you took looks reasonable, but I would add few changes:
What if you forgot to call HaveReadPermision method? And checking authotization from Actions is not the cleanest solution either, that is not an Action reponsibility.
It is better to keep authorization logic separately. For instance you can create a decorator over you repository which will check the permissions of the current User:
public class AuthorizationDecorator: IRepository
{
public AuthorizationDecorator(IRepository realRepository, IUserProvider userProvider)
{
this.realRepository = realRepository;
this.userProvider = userProvider;
}
public Car GetCar(int id)
{
if(this.UserHaveReadPermission(this.userProvider.GetUserName(), Id))
{
return this.realRepository.GetCar(id);
}
else
{
throw new UserIsNotAuthorizedException();
}
}
private bool UserHaveReadPermission(string username, int id)
{
//do your authorization logic here
}
}
IUserProvider will return curent user name from httpRequest.
After doing the change you don't need to warry about authorization when writing Actions

MVC2 and two different models using same controller method? Possible?

I don't know if this is the right way of doing this or not, but I am using Jquery and MVC2. I am using a the $.ajax method to make a call back to a controller to do some business logic on a .blur of a textbox.
I have two views that basically do the same thing with the common data, but are using different models. They both use the same controller. It might be easier to explain with code:
So here are the two models:
public class RecordModel {
public string RecordID { get; set; }
public string OtherProperties { get; set; }
}
public class SecondaryModel {
public string RecordID { get; set; }
public string OtherPropertiesDifferentThanOtherModel { get; set; }
}
There are two views that are strongly typed to these models. One is RecordModel, the other SecondaryModel.
Now on these views is a input="text" that is created via:
<%= Html.TextBoxFor(model => model.RecordID) %>
There is jQuery javascript that binds the .blur method to a call:
<script>
$('#RecordID').blur(function() {
var data = new Object();
data.RecordID = $('#RecordID').val();
// Any other stuff needed
$.ajax({
url: '/Controller/ValidateRecordID',
type: 'post',
dataType: 'json',
data: data,
success: function(result) {
alert('success: ' + result);
},
error: function(result) {
alert('failed');
}
});
}
</script>
The controller looks like:
[HttpPost]
public ActionResult ValidateRecordID(RecordModel model) {
// TODO: Do some verification code here
return this.Json("Validated.");
}
Now this works fine if I explicitly name the RecordModel in the controller for the View that uses the RecordModel. However, the SecondaryModel view also tries to call this function, and it fails because it's expecting the RecordModel and not the SecondaryModel.
So my question is this. How can two different strongly typed views use the same Action in a controller and still adhering to the modeling pattern? I've tried abstract classes and interfaces (and changing the view pages to use the Interface/abstract class) and it still fails.
Any help? And sorry for the robustness of the post...
Thanks.
You could define an interface for those classes.
interface IRecord
{
string RecordID { get; set; }
string OtherProperties { get; set; }
}
and make the method receive the model by using that:
[HttpPost]
public ActionResult ValidateRecordID(IRecord model)
{
// TODO: Do some verification code here
return this.Json("Validated.");
}
If you only need the RecordID, you can just have the controller method take int RecordID and it will pull that out of the form post data instead of building the view model back up and providing that to your action method.
[HttpPost]
public ActionResult ValidateRecordID(int RecordID) {
// TODO: Do some verification code here
return this.Json("Validated.");
}
There is no direct way of binding data to a interface/abstract class. The DefaultModelBinder will try to instantiate that type, which is (by definition) impossible.
So, IMHO, you should not use that option. And if you still want to share the same controller action between the two views, the usual way of doing that would be using a ViewModel.
Make your strongly-typed views reference that viewmodel. Make the single shared action receive an instance of it. Inside the action, you will decide which "real" model should be used...
If you need some parameter in order to distinguish where the post came from (view 1 or 2), just add that parameter to the ajax call URL.
Of course, another way is keeping what you have already tried (interface/abstract class), but you'll need a custom Model Binder in that case... Sounds like overcoding to me, but it's your choice.
Edit After my dear SO fellow #Charles Boyung made a gracious (and wrong) comment below, I've come to the conclusion that my answer was not exactly accurate. So I have fixed some of the terminology that I've used here - hope it is clearer now.
In the case above your action could accept two strings instead of a concrete type.
Another possibility is having two actions. Each action taking one of your types. I'm assuming that functionality each type is basically the same. Once the values have been extracted hand them off to a method. In your case method will probably be the same for each action.
public ActionResult Method1(Record record)
{
ProcessAction(record.id, record.Property);
}
public ActionResult Action2(OtherRecord record)
{
ProcessAction(record.id, record.OtherProperty);
}
private void ProcessAction(string id, string otherproperity)
{
//make happen
}

Mocking HttpContext doesn't work

I am trying to mock out HttpContext so that I can unit test my controller's Request.IsAuthenicated call. I am using the code that I found at Scott Hanselman's blog to simulate HttpContext using rhino.mocks.
so i have this unit test piece:
PostsController postsController = new PostsController(postDL);
mocks.SetFakeControllerContext(postsController);
Expect.Call(postsController.Request.IsAuthenticated).Return(true);
In my controller action, I have something like
if(Request.IsAuthenticated)....
when I try to run the unit test, the test fails throwing a null exception, and when I try to debug the unit test, I see that the HttpContext is never assigned to the controller.
any ideas?
This should work:
PostsController postsController = new PostsController(postDL);
var context = mocks.Stub<HttpContextBase>();
var request = mocks.Stub<HttpRequestBase>();
SetupResult.For(request.IsAuthenticated).Return(true);
SetupResult.For(context.Request).Return(request);
postsController.ControllerContext = new ControllerContext(context, new RouteData(), postsController);
This may be of some use to you, worked for me in a similar scenario:
http://haacked.com/archive/2007/06/19/unit-tests-web-code-without-a-web-server-using-httpsimulator.aspx
You may find the post I wrote on this to be helpful in some way
http://santoshbenjamin.wordpress.com/2008/08/04/mock-httpcontext-and-session-state/
cheers
benjy
Now, for disclosure, I have yet to get my hands dirty with most of the stuff you are working with, however:
If you want to mock the IsAuthenticated, why not just create a static class to return a bool that can the be manipulated by your test code?
This is a bit rough round the edges, but hopefully you get the idea:
interface IAuthenticationChecker
{
bool IsAuthenticated { get; }
}
public class MockAuthenticationChecker : IAuthenticationChecker
{
static bool _authenticated = false;
public static void SetAuthenticated(bool value)
{
_authenticated = value;
}
#region IAuthenticationChecker Members
public bool IsAuthenticated
{
get { return _authenticated; }
}
#endregion
}
public class RequestAuthenticationChecker : IAuthenticationChecker
{
#region IAuthenticationChecker Members
public bool IsAuthenticated
{
get {
if (HttpContext.Current == null)
throw new ApplicationException(
"Unable to Retrieve IsAuthenticated for Request becuse there is no current HttpContext.");
return HttpContext.Current.Request.IsAuthenticated;
}
}
#endregion
}
You can then use a reference to either at app level, yeah it means you have to add a reference at app level, and you need to use a different ref rather than Request, but you also get complete control over the authentication for testing :)
FYI - this is totally open to being blown apart, I threw it together in about a minute :)
Here is one simple way to fake the context, found it from Jeff's blog :
TextWriter tw = new StringWriter();
HttpWorkerRequest wr = new SimpleWorkerRequest("/webapp", "c:\\inetpub\\wwwroot\\webapp\\", "default.aspx", "", tw);
HttpContext.Current = new HttpContext(wr);
Here's a class that may be useful. It handles ajax requests, user authentication, request parameters and more: https://gist.github.com/3004119

Resources