I want to make log off user functionality.
When user click this linq:
<a tabindex="-1" href="javascript:document.getElementById('logoutForm').submit()"><i class="glyphicon glyphicon-off"></i>LogOff </a>
This action is called:
[Authorize]
public class AccountController : BaseController
{
// POST: /Account/LogOff
[HttpPost]
//[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut();
return RedirectToAction("Index", "Home");
}
}
While I chenge the linq above to this:
#Ajax.ActionLink(#Resources.Resources.LogOff, "LogOff", "Account", new { id = "logoutForm" }, new AjaxOptions { HttpMethod = "POST" })
I get error:
Description: HTTP 404. The resource you are looking for (or one of its
dependencies) could have been removed, had its name changed, or is
temporarily unavailable. Please review the following URL and make
sure that it is spelled correctly. Requested URL:
/MySiteName/Account/LogOff/logoutForm
Any idea why the second attitude dosent work?Why I get logoutForm as postfix in my URL?
The route for /MySiteName/Account/LogOff/logoutForm simply does not exist. You are passing in a route parameter id with the value "logoutForm" to your route, yet the action method LogOff does not accept any parameters.
This is also excactly what the error tells you. You can verify this by looking at the signature of your action method:
public ActionResult LogOff() {}
As you can see it does not take any parameters, so the url you have to call is the same as it states in the comment just above it: // POST: /Account/LogOff
That in turn means, removing your route parameters, so just replace new { id = "logoutForm" } with null:
#Ajax.ActionLink(#Resources.Resources.LogOff, "LogOff", "Account", null, new AjaxOptions { HttpMethod = "POST" })
And you should be good to go.
I do not see a reason, why the Log out itself should be a post. You are not submitting any data to the server. You can do a simple GET, achieve the same and remove some unnessecary complexity along the way.
Related
I have two types of functionality for a action with a bool variable.
[HttpGet]
public ActionResult action(bool data = false)
{
if(data == false)
{
return View("view1");
}
else
{
return View("view2");
}
}
It is a [httpGet] method. some link has data bool value as true and some has false.
The url has the attribute like http://localhost:58241/action?data=False
I want to hide the ?data=False or ?data=Truefrom URL and should possess all the same functionality like before.
I want the URL like http://localhost:58241/action
Thanks in advance.
Routing has absolutely nothing at all to do with query string parameters. And in any case, you still need to transfer the data parameter to the server for the action method to receive it. There are 3 ways to do this:
Pass it in the URL using HTTP GET (as a route value or query string value)
Pass it in the form body using HTTP POST
Pass it in a model (using HTTP POST and model binding)
The simplest option is #1, however since you mentioned this isn't acceptable to pass the data through the URL, your only choice is to use HTTP post. So, the rest of this answer uses #2.
First, the default route does not cover your choice of URL (/action), so you need to insert a custom route for this.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// Add route to handle /action
routes.MapRoute(
name: "Action",
url: "action",
defaults: new { controller = "Data", action = "Action" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
Next, you need a controller to handle both the GET and POST from the browser.
public class DataController : Controller
{
public ActionResult Action()
{
return View();
}
[HttpPost]
public ActionResult Action(bool data = false)
{
if (data)
{
return View("view2");
}
return View("view1");
}
}
The data is sent back to the server in the POST, so it is not required to pass it in the URL.
Finally, you have the view (named Action.cshtml) that is returned from the Action action method. It has 2 different form tags that submit a different value for data depending on the button clicked.
#{
ViewBag.Title = "Action";
}
<h2>Choose an Option</h2>
#using (Html.BeginForm("action", "Data")) {
<input type="hidden" name="data" value="true" />
<input type="submit" value="With Data" />
}
#using (Html.BeginForm("action", "Data")) {
<input type="hidden" name="data" value="false" />
<input type="submit" value="Without Data" />
}
Note that you could do this step entirely in JavaScript (AJAX POST) if you please, which would enable you to use a hyperlink instead of a button or you could just style the button using CSS to look like a hyperlink.
You can achieve this functionality partially by making the parameter optional as suggested by #VishalSuthar. But for one condition you must use the parameter if you want to access it using a GET request.
Else, if you make the Action only accessible by POST requests, this part will be easier to implement. In that case only change you need is :
[HttpPost]
public ActionResult action(bool data = false)
{
if(data == false)
{
return View("view1");
}
else
{
return View("view2");
}
}
This way you can pass the parameter in form and hit the action with the URL showing: http://localhost:58241/action
Note: This way the action will not be accessible via GET requests.
I want to delete an item by getting confirmation from user & I want to delete it with POST method.
Here's my code snippet
#Html.ActionLink("Delete","Delete",new { id=item.UserId },new AjaxOptions {HttpMethod = "POST", Confirm = "Do you really want to delete this?"})
& my action method is:
[HttpPost]
public ActionResult Delete(int? id, LearningMVC.Models.User userDetails)
{
try
{
var dbContext = new MyDBDataContext();
var user = dbContext.Users.FirstOrDefault(userId => userId.UserId == id);
if (user != null)
{
dbContext.Users.DeleteOnSubmit(user);
dbContext.SubmitChanges();
}
return RedirectToAction("Index");
}
catch
{
return View();
}
}
If I remove the [HttpPost] attribute, It just deletes the record without any confirmation. It's giving 404 error with [HttpPost] attribute.
I have included jquery-1.5.1.min.js, unobtrusive-ajax.js, MicrosoftAjax.js, MicrosoftMvcAjax.js in the said order in layout page.
what I am missing here?
I believe that you wanted to write:
#Ajax.ActionLink("Delete", "Delete",
new { id = item.UserId },
new AjaxOptions { HttpMethod = "POST", Confirm = "Do you really want to delete this?" })
Just use AjaxHelper instead of HtmlHelper.
I setup a route like that:
routes.MapRoute(
name: "Pesquisar",
url: "Pesquisar/{aaa}/{bbb}/{id}",
defaults: new { controller = "Home", action = "Pesquisar",
aaa = UrlParameter.Optional,
bbb = UrlParameter.Optional,
id = UrlParameter.Optional
}
);
When I press Send button in a form (with GET method) the url is like that:
http://localhost:00000/Pesquisar?aaa=One&bbb=Two
But I was expecting for:
http://localhost:00000/Pesquisar/One/Two
When you map a rout, it adds it to the end of a list. When the router looks for the rule to match, it starts at the begining of the list and itterates through it. It will take the first rule that matches, not the most specific rule. Because it is natural to append code to the end, the default rule (which works for almost everything) will be at the start.
Try re-ordering your code to look like this:
///The specific rout which you want to use
routes.MapRoute(
name: "Pesquisar",
url: "{action}/{aaa}/{bbb}/{id}",
defaults: new { controller = "Home", action = "Pesquisar",
aaa = UrlParameter.Optional,
bbb = UrlParameter.Optional,
id = UrlParameter.Optional
}
);
///The generic catch all router
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
More information can be found in this question:
Setting up ASP.Net MVC 4 Routing with custom segment variables
When I press Send button in a form (with GET method) the url is like that:
http://mydomain.com/Pesquisar?aaa=One&bbb=Two
But I was expecting for:
http://mydomain.com/One/Two
This is because the browser is unaware of the fancy url you want, as the standard form Get method is to append form values in the querystring.
What you mostly likely have to do is something like Creating Canonical URLs including an id and title slug, except redirect to the url you want if it's not the url you want to display.
Or you can use jQuery to manually create the url you want on submit, but requires more client side work.
Not sure if you get a clean URL directly from a html form GET.
One suggestion would be to POST it to an action, do what you need to do with the data, then on completion, redirect to your clean URL.
e.g.
ViewModel:
public sealed class PesquisarModel
{
public string aaa { get; set; }
public string bbb { get; set; }
public string id { get; set; }
}
Controller Actions:
[HttpGet]
public ActionResult Pesquisar(PesquisarModel m)
{
return View();
}
[HttpPost]
[ActionName("Pesquisar")]
public ActionResult PesquisarPost(PesquisarModel m)
{
//do stuff
return RedirectPermanent("/pesquisar/" + m.aaa + "/" + m.bbb + "/" + m.id);
}
View:
#model MyApplication.Models.PesquisarModel
#using (Html.BeginForm())
{
#Html.TextBoxFor(m => m.aaa)
#Html.TextBoxFor(m => m.bbb)
#Html.TextBoxFor(m => m.id)
<button type="submit">Submit</button>
}
This is the browser behavior. When making a GET request browser appends all KeyValue pairs to querystring.
The mentioned route format will be available, when we use Html.ActionLink or Html.RouteUrl etc.
You could probably write few code in OnActionExecuting ( or you can use any handler) to reconstruct RouteData and redirect with appropriate url. Below code is not tested
var queries = this.Request.QueryString;
foreach(var query in queries)
{
// Add/Update to RequestContext.RouteData
}
var redirectUrl = Url.RouteUrl("Pesquisar",this.RequestContext.RouteData);
Response.Redirect(redirectUrl);
That's expected behavior.
The routing system is on server side. The browser knows nothing about routes, and what you're doing happens in the browser.
If you want to get that route you have to compose it on the client side with a custom script which uses the <form>'s action, an the values of the <input type="text"> values.
You cannot generate the Url on the server side (which could be done with some UrlHelper extension methods) because the changes on the text boxes wouldn't be updated.
This is not advisable because if you make changes on the routes, you could forget to update them in your browser scripts, breaking you application.
You could avoid this problem by creating the url in the server side using an UrlHelper extension method with special placeholders, which could be easily replaced on the client side. I.e. generate an url like this:
http://localhost/Pesquisar/$aaa$/$bbb$/$id$
by providing RouteValues like this: new {aaa="$aaa$, bbb="$bbb$, id="$id$"} to an UrlHelper method. This url can be stored in the value property of a hidden field.
Then, make a browser script for the click event of your button, recover the url with the placeholders from the hidden field, and replace the placeholders with the actual values of the textboxes. To execute the get run this: document.location = theUrl;
If you want to d this for many differnt instances you could create a Helper to geenrate the hidden field with the Url, and a javascript which makes the replacements.
The question is... is it worth the effort?
I am using the Ajax.BeginForm to create a form the will do an ajax postback to a certain controller action and then the response view is inserted into the UpdateTargetId.
using (Ajax.BeginForm("Save", null,
new { userId = Model.UserId },
new AjaxOptions { UpdateTargetId = "UserForm" },
new { name = "SaveForm", id = "SaveForm" }))
{
[HTML SAVE BUTTON]
}
Everything works great except when the Users session has timed out and then they are redirected back to the login page. The login page then gets returned from the Ajax call because of the Authorize attribute and the html gets loaded into the UpdateTargetId and therefore I end up with the login page html within the user profile page (at the Target Id). My controller action looks like this:
[Authorize]
public ActionResult Save(Int32 UserId)
{
//code to save user
return View("UserInfoControl", m);
}
How can I solve this problem?
UPDATE (2011-10-20):
Found this post from Phil Haack about this exact issue - http://haacked.com/archive/2011/10/04/prevent-forms-authentication-login-page-redirect-when-you-donrsquot-want.aspx. I have not gotten a chance to digest or implement his solution yet.
I think that you can handle the authorization issue from inside the action.
First remove [Authorize], the use this code:
public ActionResult Save(Int32 UserId)
{
if (!User.Identity.IsAuthenticated)
{
throw new Exception();
}
//code to save user
return View("UserInfoControl", m);
}
Then you can put a OnFailure condition in your AjaxOptions and redirect your page or something.
Suppose I want to use ajax action link to save one field with no change for view.
Here is my view(it is a strongly-typed view bound to type Model):
<%= Html.TextBox("myComment", Model.MyComment)%>
<%= Ajax.ActionLink("SAVE", "SaveComment", "Home",
Model, new AjaxOptions { HttpMethod = "POST"})
%>
Here is my Action for ajax actionlink in controller:
[AcceptVerbs(HttpVerbs.Post)]
public void SaveComment(Model model)
{
MyRepository repository = new MyRepository();
Model mod = repository.GetModel(model.ID);
mod.MyComment = model.MyComment;
try
{
repository.Save();
}
catch(Exception ex)
{
throw ex;
}
}
The problem is: I can't get the user input in textbox for Mycomment in action method. Whatever user input in browser for Mycomment, when click on Ajax ActionLink SAVE, then I check the value from param model for Mycomment, there is nothing changed for MyComment.
Confused is: Model should be bound to view in bi-way. but for this case, it seems not.
Then I changed the Ajax.ActionLink call to:
Ajax.ActionLink("SAVE", "SaveComment", "Home",
new {commentinput =Model.MyComment, new AjaxOptions { HttpMethod = "POST"})
then changed action method signature as:
public void SaveComment(string commentinput)
I still can't get the user input.
How to get the user input in controller action method?
AFAIK Ajax.ActionLink doesn't allow you to easily get form values and post them with AJAX. There are some workarounds which seem quite hackish (looking at the last paragraph Adapting the URL Dynamically makes me cry).
Here's the jQuery way (I prefer it if compared to MS Ajax):
<%= Html.TextBox("myComment", Model.MyComment) %>
<%= Html.ActionLink("SAVE", "SaveComment", "Home", new { id = "saveComment" }) %>
And then unobtrusively attach a click event handler to the anchor:
$(function() {
$('a#saveComment').click(function(evt) {
$.ajax({
url : this.href,
data: {
// Assuming your model has a property called MyComment
myComment: $('#myComment').val()
},
success: function(data, textStatus) {
// Comment saved successfully
}
});
evt.preventDefault();
});
});