Can I have multiple POST actions for an ApiController? - ajax

The scenario:
a User class has several groups of properties: password, address, preference, roles.
We need different Ajax calls to update the (1) user password, (2) user profile, (3) roles a user is in.
All the tutorials and examples only shows one POST action to update the whole User class. My question is how we can update only part of the class.
For example, when updating the user password, we will:
Display a text box to collect new password from user input.
Make an Ajax call that only POST the new password together with the userId (like: {id=3, newPassword=xxxxx}) to the WebAPI POST action.
That action will only update the password for the user.
One solution: (the easiest to think of)
Call the GET action with the userId to retrieve all the data for a user
Update the password in the user data with the values obtained from the web user input
Call the POST action with the updated data, which contains all properties in the User class.
That POST action will update the whole data without knowing only the password is changed.
The benefit: only one POST action is needed for the ApiController.
The shortcoming: we have to Ajax twice.
So, is it possible that we can have multiple POST actions in one ApiController? For example, PostPassword(userId, password), PostProfile(userId, profile) and PostRoles(userId, roles).
In this way, we will only call PostPassword to send the password to ApiController. In client side, there will be only one Ajax call. It is on the server where we will do the update. The benefit is of course the reduced data transferred over Internet.
If it is possible, what is the correct way to direct all different POST calls to their corresponding actions in the ApiController?
Please help us. Thank you all.

Most of cases, needless to have muptile post actions, I think. The typical case is consumer needs to edit user. So, s/he needs to load user data first to show on the edit form. After editing, consumer can click Save button to submit data and call POST action on api controller.
If your case is different, you should have nullable property for value type, and then the logic which should be checked in controller is if any property is null, it should not update this property into database.

You can only have one post action per controller action name. That is, you cannot do
// NOT VALID:
public ActionResult UpdateUser(string newPassword) { }
public ActionResult UpdateUser(List<string> newRoles) { }
However, parameters of the action can certainly be nullable. If a given property is not supplied in a given HTTP request, the value of the property in the controller would be null.
// VALID:
public ActionResult UpdateUser(string newPassword, List<string> newRoles)
{
if (newPassword != null) { } // It must have been supplied
if (newRoles != null) { } // It must have been supplied
}
Alternatively, you can have related controller actions that each handle one of your use cases, e.g. UpdatePassword(...), UpdateAddress(...), ...

Related

Restful application with update entity

Given a controller which will create/update/delete/query for User:
Get /users query the list
Post /users add a new user
Get /users/1 query a single record
Delete /users/1 delete a single record
Put /users/1 update a single record
Note the last Put method to action /users/1 which means the user of identify 1 should be updated with the data in the request.
However suppose the user with identify 1 have the following properties(partially):
{username:uname,location:sg}
Now given the following requests:
PUT /user/1
username=hg
PUT /user/1
username=hg&location=
We should set the username to hg, but how do we handle the location? Should it be set null or left as it is in the database?
Generally we may use the Data binding like spring mvc in the controller:
#RequestMapping(value="/{userId}",method="PUT")
public String update(#PathVariable String userId, User user){
//merge the model, suppose the `user` contains all the properties of the user
user = entityManager.merge(user);
entityManager.persist(user);
return "user/show"
}
In this case, once the two example requests are executed, the location will set to null in the database, which may or not what the client want.
Normally, we should use the Patch to update the partial properties of the resource, however not all the framework support that method.
And what's more, even the Patch method is supported like this:
#RequestMapping(value="/{userId}",method="PATCH")
public String updatePartial(#PathVariable String userId, User user){
//just set the properties no null
User userInDB=entityManager.find(userId);
//iterator all the properties, set the property which is not null or empty to the userInDB
entityManager.persist(userInDB);
return "user/show"
}
As shown, we have to check the properties of the model, it would be tedious once the model have some deep nested beans.
What's your general practice when handling this kind of situation?
The best practice is to use mentioned PATCH method if partial (in the meaning of fields) request is being sent. Then, all the fields that are present in the request should be modified - set to null value e.g.
When it comes to PUT you should not accept partial requests - since this is not compatible with the standards. When a request is syntactically correct you should modify all the fields unless DB constraints prevents you to do so. So if a particular user can (in terms of the system) set location to null value, he/she should be allowed to do so. If it's not possible you should raise bad request exception and return 400 status code along with the message.

Spring MVC - Keeping object state between ajax requests

Spring MVC 4.1
Hi,
I have a situation where, on a single page, there are several input fields. As the users enters numbers into these fields, a bunch of calculations will occur and update various other fields on the page.
I want this whole calculation model to take place in Java on the server-side. I really want to avoid replicating this logic in Javascript on the client.
What I envision is...
User opens the page, the object that does the calculations (let's call it Calculator) is created and its initial state is set (many of its fields are pre-populated with values).
This Calculator instance is stored and available for the duration of the user's time on that page.
Whenever the user changes a value in an input field, that new value is sent to the server via ajax and plugged into our Calculator object. The Calculator, re-calculates the other fields based on the new state and returns the results to the page.
The other fields on the page are updated accordingly.
The key here is that I'm not sending the state of all fields with each ajax request. I'm only sending the current value that was updated. Essentially, I'm trying to ensure that the form state and the Calculator state on the back-end are always synchronized.
I have looked into #SessionAttributes and #ModelAttribute.
The problem with #ModelAttribute, as I understand it, is that it will be re-created with each ajax request.
The problem with #SessionAttributes is that it is a session variable. What if the user has two of these windows open? And how do I ensure the object is removed from the session when they leave the page? etc...
Maybe there's no magic Spring bullet and I just have to figure out the session variable thing. But any pointers on dealing with this would be much appreciated.
Thanks!
You have a couple of options:
.1. Like you have said using the #SessionAttributes, however yes it suffers from the issue that you have mentioned, multiple instances of the same session will see the same variable.
.2. Store state somewhere else and re-hydrate the state using #ModelAttribute annotated method. I would personally prefer this approach, essentially when you create the form, create it with a identifier for the current state:
#RequestMapping(params = "form")
public String createForm(Model uiModel) {
uiModel.addAttribute("calculationId", UUID.randomUUID().toString());
return "calculationpage/create";
}
Then for subsequent ajax requests, ensure your previous calculationId is sent across:
#ModelAttribute("calculationState")
public CalculationState rehydrateState(String calculationId) {
//retrieve current state of calculation from some persistent store..
}
#RequestMapping("/calculate")
public String handleCalculation(#ModelAttribute("calculationState") CalculationState c) {
//you will get a calculationstate with the delta's at this point..
}
.3. Another potential approach may be to use session but disambiguate different instances within the session with a custom id:
public String handleCalculation(HttpSession session, #RequestParam("calcId") String calcId) {
CalculationState calcState = (CalculationState) session.getAttribute("calculation" + calcId);
}
You need any sort of persistent store outside session to store and retrieve the state of your calculator model. Like Biju said, I will go for solutions like No 2.

MVC3 - Call logon method from GET request

I need to be able to call the built-in [HttpPost] Logon method (in the Account Controller) from a GET request. Basically, a new account would be assigned a temporary password, delivered via email. The user clicks on the link in the email (containing a userid and temp pw in the URL), which would be handled in the account controller in the MVC app. I'm not sure how to handle this. Is it possible to redirect from a GET action method on the controller, to the POST logon action method? How would this be done while maintaining the HttpContext object? It seems that this wouldn't be doable outside of just creating a new GET logon method with the user and pw strings params and recreating the logic (using MembershipServer and FormsService objects) that's in the POST method.
EDIT: For anyone following this post, the solution I wound up implementing is:
Using #Shyju's recommendation in a comment, I created a new GET action method to respond to the email link, that returns a "Register New User" view containing hidden inputs for the passed user\pw and a submit button.
The view posts back to the normal logon method. If the user or pw has not been altered, the user will be authenticated
I added a global action filter that tests for the user object property "MustChangePassword". If true, it redirects to the change password action method. This will happen for any method of any controller, except the account controller (so you can actually hit the logon, chg pw, and other methods)
Then in the "ChangePassword" POST method, a successful pw change will reset the "MustChangePassword" property to false.
You can create a unique string (use Guid) for each user and send that as the query string for the link in the email. In the GET action validate that against the user table & Temporary Password table and get the user record and consider that user as logged in user.

ASP.Net MVC 3 Querystring Parameter

I am developing an ASP.Net MVC 3 Web Application. Within some of my Views I display tabular data to the user, and beside each record in the table there is an Edit link. When the user clicks this link it takes them to an edit page where they can edit and update the record.
My issue is that once the user clicks the edit link, the URL becomes something like this
http://www.mytestsite.com/myData/edit/3
The '3' is the ID of the record to be updated, however, there is nothing stopping the user from changing the '3' to another digit, and this then means they can edit potentially a record which does not belong to them.
Does anyone have a solution on how I can prevent this from happening?
Thanks for you help.
You need to introduce Authentication and Authorisation into your application. Here is one article of many out there on how to get started with this. You will additionally need to work out how to store logged on user identity and then how to attach this to the record when it was created in the first place. You must then validate, on the server, that the subsequent edit request is being made by the user who created the record in the first place (or by a user who has a role on your system which allows them to do this, such as an Administrator).
Even if the ID wasn't being displayed on the URL a malicious user could still manipulate the HTTP Request to pass an ID of their choice. In any secure system you should always, always, always validate that the currently logged on user genuinely has permission to carry out the requested action. You should never rely on what comes back from the browser to determine this (aside from the authentication context which is managed securely by the MVC framework. Usually).
I believe you should have the information about who have the edit permission on this purticular resource, in your tables. Ex : in your table you might have the "CreatedById" column where you store the ID of the user who created this record. Now in your edit action method, you check the "CreatedById" of the current Item is same as of the "UserId" of the Current user (you maye get this from the session, if you stored it there). Something like this.
public ActionResult Edit(int id)
{
int currentUserID=1; // TO DO : get this value from session or somewhere
ProductVieWModel product=myRepo.GetProduct(id);
if(product!=null)
{
if(product.CreatedById==currentUserID)
{
return View(product);
}
else
{
return View("NotAutherized");
}
}
return View("ProdcutNotFound");
}
You should try using the GUID data type as it helps in these kind of situations, and the user cannot easily guess the next value

MVC 3 Page navigation security rules

I have a MVC3 application which follows PRG pattern, I am looking for a best way to define navigation rules to my application. For example I have application with pages A, B, C and D. Lets say A is a logon page. After user logon successfully, user will be redirected to page B. Now I do not want let the user to type url of page C in the address bar and get access to page C (Page C should be accessible only after POST ing page B or from Page D back button) I have to setup similar rules for all other pages as well (lets say when user is in page D, should not allow them to get into Page B)
currently I have one option where I can check the #Request.UrlRefferer property to get the source of the every request and decide which page to redirect to. I am not sure this is a best solution.
Appreciate your feedbacks!!
Do not base your security on this. Use the [Authorize] attribute to define security. The UrlReferrer can easily be forged as well.
Why are you trying to limit this? If you have a business reason the user most go through a particular flow, then consider either a cookie, session, or database entry to note their current 'completion' status - IE some somewhat persistent method to determine this. You could also form a token based on say - a session id - that gets passed into each page. If the token exists and matches the user's current session , then load the page for them. Of course this could be forged if the user understands this - but if you are simply trying to ensure the proper flow then this is a way as well. The user would not get a link with the current session id in it until they hit the prior step.
If you don't want a particular page to be accessible via the URL, one option available is to make sure there's no way to access the page via a URL. To access the page, make a POST action that will return a view rather than a redirect. This would mean the view your POST action returns will be shown on a page with the URL of the previous page. For exameple:
Page A URL is /login and after logging in, the user is redirected to Page B. The URL is now /home. Page B sends a POST request and the contents of the page becomes Page C but the URL still remains as /home. The only way to view the contents of Page C would be to visit Page B and send a POST request.
This breaks the PRG pattern but that's one option.
There is one more alternative, store the current permissions of the user indicating which page they're allowed to enter and check if the user is authorized to view a page before executing the action. You could place the code in an ActionAttribute which you can apply to your action methods or entire controllers. If you'd like a more detailed explanation of this technique, leave me a comment and I'll write up another answer describing this technique in more detail.
Here's a quick proof-of-concept of the technique described above:
public class PermissionsNeeded : ActionFilterAttribute
{
string expectedPermission;
public PermissionsNeeded(string permission)
{
expectedPermission = permission;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var currentPermissions = filterContext.HttpContext.Session["CurrentPermissions"] as IEnumerable<string> ?? new List<string>();
// If user does NOT have permission to access the action method
if(!currentPermissions.Contains(expectedPermission)
{
throw new HttpException(403, "User is not authorized to view this page");
}
}
}
class YourController : Controller
{
[HttpPost]
public ActionResult PageB()
{
var currentPermissions = Session["CurrentPermissions"] ?? new List<string>();
currentPermissions.Add("PostedFromPageB");
Session["CurrentPermissions"] = currentPermissions;
return RedirectToAction("PageC");
}
[PermissionsNeeded("PostedFromPageB")
public ActionResult PageC()
{
return View();
}
}
Currently, the custom attribute will only accept one permission at a time which is a simply limitation to rectify. You'd be responsible for removing the permissions stored in the Session when you feel the user shouldn't have certain permissions anymore. I threw an HttpException that return a 403 status code (unauthorized access) but if you'd instead like to return an ActionResult such as a RedirectToRoute or a View, you could set a value to the filterContext.Result property.
I end up doing this task as below:
On successful completion of every page, save the page Name in database
When user request for a new page, simply check the page they have completed last from database and decide what to do.
I chooses this approach simply because it will definitely help during Problem Solving/Troubleshooting once application is in Production, which is huge for me.
Thanks everyone for your responses!

Resources