MVC 4 Controller and Web Api controller - asp.net

I am currently working on an MVC 4 project and i need to give acces to the database to the mobile apllication so i choosed to implement my Web services in Web Api to get a Json resulat .
The problem is i have many code redundancy ! the same code is existent in MVC controller and in the Web Api controller .
For exemple the get procedure :
1- web api controller :
public User GetUser(int id)
{
User user = db.Users.Find(id);
if (user == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return user;
}
2-MVC controller
public ActionResult Details(int id = 0)
{
User user = db.Users.Find(id);
if (user == null)
{
return HttpNotFound();
}
return View(user);
}
one is returning the User entity in JSon and the other return a view !
So how can use one Controller in the other to get rid of redandency ?

Looking at this question from the point of view of separation of concerns, both of the controllers have a different function (supplying JSON and markup respectively), but both need to make use of a common service: namely, data persistence.
For that reason, as #paul suggested in the comments, the repository pattern offers a good design to solve this problem. In your example, you may not seem to gain much, but as soon as your data retrieval and persistence logic becomes more complex, the repository will enforce consistency, reduce redundancy, and support more sophisticated patterns such as dependency injection.
Here's a simple repository:
interface IRepository
{
User GetUser(int id);
}
public class MyRepository: IRepository
{
public User GetUser(int id)
{
return db.Users.Find(id);
}
}
Api controller
public User GetUser(int id)
{
var repo = new MyRepository();
User user = repo.GetUser(id);
if (user == null)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
}
return user;
}
MVC controller:
public ActionResult Details(int id = 0)
{
var repo = new MyRepository();
User user = repo.GetUser(id);
if (user == null)
{
return HttpNotFound();
}
return View(user);
}

As paul said this is all about separation of concerns. Paul provided you an example with a "logical service layer" which is an independent class library in your solution that your other Web Applications or desktop applications etc. reference it. Another example may be a "physical service layer" which is another Web Api Project in your solution, that contains all the service methods of your application. From your MVC project whenever you want a call to have your users, you create a new WebClient to call your web api's GetUser end points.

You can just return JSON() from the MVC controller and avoid the usage of two frameworks. There is nothing wrong with that, and will keep your life simple.
public ActionResult GetUser(int id) // GetUser is the action name, or you can just use Index
{
User user = db.Users.Find(id);
if (user == null)
{
return HttpNotFound();
}
return Json(user);
}

Related

ASP.NET Core Authorization for owner only

I have a question similar to Owner based Authorization
Is it possible to use resource-based authorization or policy-based authorization to allow only the owner of a model to view/edit/delete it?
With something like
[Authorize(Policy = "OwnerAuthorization")]
public class EditModel : PageModel
Or do I have to add logic to every OnGet/OnPost on every page for handling the authorization?
do I have to add logic to every OnGet/OnPost on every page for handling the authorization?
You don't have to, but for a better performance, you should add logic for every OnGet/OnPost on every page.
To authorize the request in the way of resource-based authorization, we need firstly to know what the resource is, and only after that we can authorize the user against the resource and policy.
Typically, we need load the resource from the server and then we can know whether the resource belongs to the current user. As loading the resource from server is usually done within action method, we usually authorize the request within the action method. That's exactly what is described in the official document.
var authorizationResult = await _authorizationService
.AuthorizeAsync(User, Document, "EditPolicy");
if (authorizationResult.Succeeded)
{
return Page();
}
else if (User.Identity.IsAuthenticated)
{
return new ForbidResult();
}
else
{
return new ChallengeResult();
}
However, if we choose to decorate the pagemodel with [Authorize(Policy = "OwnerAuthorization")] only, and don't invoke the _authZService.AuthorizeAsync(User, resource, "OwnerAuthorization"); within the action method, we'll have to load the resource within the authorization handler( or a simple function). In other words, we'll query the database twice. Let's say an user wants to edit a Foo model , and then make a HTTP GET request to /Foo/Edit?id=1 to show the form. The OnGetAsync(int? id) method is :
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null){ return NotFound(); }
// load foo from database
Foo = await _context.Foos.FirstOrDefaultAsync(m => m.Id == id);
if (Foo == null){ return NotFound(); }
return Page();
}
Now the resource-based authorization will load the Foo entity from database and check the owner. If succeeds, the action method will then validate the model.id and load resource from the database again.

Mocking Services in ASP.NET Core

I have a simple form to save and then use MailKit to provide email notification, with xUnit and Moq used for unit testing. I'm having difficulty setting up the unit test and associated services. I have a workaround ('if' statement in the action method) to only test the core repo saving functionality without also testing the email service. If I take out the if statement, the unit test does not have access to the appropriate methods, such as setting the web root path. The error is a null exception. If I default this value, there are other errors, such as "no database provider being configured for DbContext."
Is there a more appropriate way to set a unit test of this sort up? Or is it wrong to set up a unit test to test both the Create() and email functionality because it violates the one-function unit testing rule?
Unit test:
[Fact]
public void Can_Create_New_Lesson()
{
//Arrange
//create a mock repository
Mock<IHostingEnvironment> mockEnv = new Mock<IHostingEnvironment>();
Mock<ILessonRepository> mockRepo = new Mock<ILessonRepository>();
Mock<UserManager<AppUser>> mockUsrMgr = GetMockUserManager();
Mock<RoleManager<IdentityRole>> mockRoleMgr = GetMockRoleManager();
var opts = new DbContextOptions<AppIdentityDbContext>();
Mock <AppIdentityDbContext> mockCtx = new Mock<AppIdentityDbContext>(opts);
//create mock temporary data
Mock<ITempDataDictionary> tempData = new Mock<ITempDataDictionary>();
//create the controller
LessonController target = new LessonController(mockRepo.Object, mockEnv.Object, mockUsrMgr.Object, mockRoleMgr.Object, mockCtx.Object)
{
TempData = tempData.Object
};
//create a lesson
Lesson lesson = new Lesson { Title = "Unit Test", Domain= "Unit Test"};
//Act
//try to save the product using the Create method of the controller
IActionResult result = target.Create(lesson);
//Assert
//check that the repository was called
mockRepo.Verify(m => m.SaveLesson(lesson));
//check the result type is a redirection to the List action method of the controller
Assert.IsType<RedirectToActionResult>(result);
Assert.Equal("Success", (result as RedirectToActionResult).ActionName);
}
The Create() action method:
[HttpPost]
public IActionResult Create(Lesson lesson)
{
if (ModelState.IsValid)
{
repository.SaveLesson(lesson);
//This IF statement is a workaround for the unit test
//don't email users if the Title is "Unit Test"
if (lesson.Title != "Unit Test")
{
emailUsers(lesson);
}
TempData["message"] = $"{lesson.Title} has been saved";
//show the user that the update was made successfully
return RedirectToAction("Success");
}
else
{
//there is a problem with the data values
return View(lesson);
}
}
Email function:
public void emailUsers(Lesson lesson)
{
var webRoot = environment.WebRootPath;
var filePath = System.IO.Path.Combine(webRoot, "email\\NewLessonSubmitted.txt");
string message = System.IO.File.ReadAllText(filePath);
string domain = lesson.Domain;
IQueryable<AppUser> userList = GetUsersInRole(identityContext, domain);
//if there are users in that domain, send the email
if (userList != null)
{
foreach (AppUser user in userList)
{
sendEmail(domain, message, user.Email);
}
}
}
EDIT: I've instead implemented the email service as a class, as pointed out by MotoSV. However, I'm still getting an error for "No database provider has been configured for this DbContext" The stack trace for the exception points to the following method:
public static IQueryable<AppUser> GetUsersInRole(AppIdentityDbContext db, string roleName)
{
if (db != null && roleName != null)
{
var roles = db.Roles.Where(r => r.Name == roleName);
if (roles.Any())
{
var roleId = roles.First().Id;
return from user in db.Users
where user.Roles.Any(r => r.RoleId == roleId)
select user;
}
}
return null;
}
I have this constructor in my dbContext class:
public AppIdentityDbContext(DbContextOptions<AppIdentityDbContext> options)
: base(options) { }
EDIT: The solution (provided by MotoSV) was to:
1) Create an email service class with appropriate methods and
2) Install the appropriate Nuget package for Microsoft.EntityFrameworkCore.InMemory
3) mock the DbContext as:
var opts = new DbContextOptionsBuilder<AppIdentityDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
Mock<AppIdentityDbContext> mockCtx = new Mock<AppIdentityDbContext>(opts);
I would look at moving the code responsible for sending emails into it's own class. This class will implement an interface that can then be mocked in your test.
For example, create the interface and implementation:
public interface IEmailService
{
void SendEmail(string to, string from, string body);
}
public class EmailService : IEmailService
{
public void SendEmail(string to, string from string body)
{
...
}
}
The EmailService class will hold the functionality required to talk to MailKit. Then, register the IEmailService with .NET Core and add it to the constructor of your class:
public class LessonController : Controller
{
private readonly IEmailService _emailService;
public LessonController(IEmailService service, ...)
{
_emailService = emailService;
}
public void emailUsers(Lessong lesson)
{
...
if(userList != null)
{
foreach(...)
{
_emailService.Send(...);
}
}
...
}
}
In your test create a mock and pass that into your constructor.
First and foremost, you should never do stuff like putting in conditionals in your code for the purpose of unit testing. If for no other reason, you're violating the entire point of unit testing, as your test access different code paths than what your users actually experience; you learn nothing by doing this.
Testing that the repo actually saves is a job for a repo test not an action test. Likewise with your mail service: ensuring that an email is actually sent should be a test on your mail service, not your action method.
Long and short, your test here should simply ensure that the appropriate actions are taken (i.e. repo save is hit and email service send is hit). As such, you can drop in simple mocks that merely have those methods available to be hit. You don't need to (and shouldn't) be establishing full connections to the DB/SMTP server, as at that point you're integration testing, not unit testing.
Your applications send email class constructor should take an "email provider" object that is a generic email abstraction based on an IEmailProvider interface, and/or also take a IDataAccessProvider implementation.
Now you can mock both of these interfaces in the test and pass them to the send email class to test just your implementation.

MVC 4 and ASP.NET Membership

I am having issues understanding what I need to do with this and how to get them to interact.
I have created an MVC 4 Internet Application. From what I understand, the login mechanism uses the SimpleMembershipProvider which is not compatible with the SQL based ASP.NET Membership provider.
I have an existing site that uses ASP.NET Membership and we are going to be leveraging this. I only need a login controller. The User name is being passed in by the original application that is calls my new application. This is all on an intranet, and we are creating a simple SSO model.
From what I can tell I will need to add in the "DefaultMembershipProvider" entry into the web.config and create a connection string to my membership DB. I have that much.
But I am unclear as to what the code will need to look like for the controller.
Here is the code that I currently have for the controller for the simple provider:
[HttpPost]
[AllowAnonymous]
public ActionResult LoginAuto(string userid)
{
LoginModel model = new LoginModel();
if (!string.IsNullOrEmpty(userid))
{
model.UserName = userid;
model.RememberMe = true;
model.Password = "Dude!!!!1";
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
return RedirectToAction("Index", "Home");
}
}
else
{
model.UserName = "";
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View("Login", model);
}
Will I need to change this at all? Will the actual controller class be different from what comes with the template? I'm pretty new to the actual security thing and looking for some direction.
Thanks.

Where I can find documentation for ASP.NET Identity RC1?

After Microsoft updated ASP.NET Identity framework to version 1.0.0-rc1, I can't find any documentation or guide how to use it. There is 2 sample projects on github (one, two), but they not covering advanced things like tokens, password reset, roles, etc.
i believe the only real documentation is replies on this forum. The structure of several entities have changed since the beta as well.
I also could do with some more in depth information, particularly
linking to your own custom user table
having access to this custom table from the controller's User property
access to the user and roles from a authorise attribute regardless of the actual login method (local,gmail,twitter etc)
I have added a project called WebCustomUser to https://github.com/onybo/Asp.Net-Identity-RC1-sample-app/tree/master/WebApplication.
This project demonstrates use of the methods:
RequireTokenConfirmationForSignInAsync
ConfirmSignInTokenAsync
to implement token activation of user accounts.
Update:
The project now include a custom entity framework model with custom users which have an email address added (just as an example).
The modeling project that contains two diagrams that shows some of the new classes in ASP.NET identity is now in a separate solution so that the main solution can be opened in the profession SKU.
Heres the modified Register action and the Activate action.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
// Create a profile, password, and link the local login before signing in the user
User user = new User(model.UserName);
var result = await new UserManager(IdentityManager).CreateLocalUserAsync(user, model.Password);
if (result.Success)
{
var token = Guid.NewGuid();
var tokenResult = await AuthenticationManager.RequireTokenConfirmationForSignInAsync(token.ToString(), user.Id, DateTime.Now.AddDays(2));
if (tokenResult.Success)
{
return RedirectToAction("Registered", "Account", new { userId = user.Id.ToString(), token = token.ToString() });
}
else
AddModelError(tokenResult, "RequireTokenConfirmation failed");
}
else
{
AddModelError(result, "Failed to register user name: " + model.UserName);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
[AllowAnonymous]
public async Task<ActionResult> Activate(string userId, string token)
{
var tokenResult = await AuthenticationManager.ConfirmSignInTokenAsync(token);
return RedirectToAction("Login", new {returnUrl="/home"});
}
You can find samples to the nightly build of the identity library here.

mvc3 and entity - basic query dependant on role

I am very new to .net and mvc3.
In my app, I have two different roles, Admin and basic user. Admins can see everything, but users can only see items that are linked to them.
I am doing this in my controller:
private MembershipExtContext db = new MembershipExtContext();
[Authorize]
public ViewResult Index()
{
var thing1s = db.Thing1.Include(i => i.Thing2);
return View(thing1s.ToList());
}
I would like it so that the basic query (db.Thing1.Include(i => i.Thing2);) return only the items that the current user is allowed to see. Otherwise, I would need to do a separate query for each role.
Is this possible? If so, How?
If needed I am using mvc3 and entity4 code first.
One way to do this would be
if(Roles.IsUserInRole(User.Identity.Name, "Admin")
{
do stuff
return View();
}
else
{
//do non admin stuff
return View();
}
This assumes your admin user is called "Admin" in your roles and that you only have two role types.

Resources