asp-route-id always pass id as query parmeter - asp.net

I'm trying pass id as route parameter by asp-route-id always pass as a query parameter
and I don't want to use href
<a asp-area="Employe" asp-controller="PersonManager" asp-action="UserDetails" asp-route-id="#Model.Provider.Id">details</a>
the route is like this
https://localhost:5002/Employe/PersonManager/UserDetails?id=b4065ff6-c7bd-4244-a6b6-9bc6b7b4c7a8
but I want be like this
https://localhost:5002/Employe/PersonManager/UserDetails/b4065ff6-c7bd-4244-a6b6-9bc6b7b4c7a8
and this is my action
[HttpGet]
public async Task<IActionResult> UserDetails([FromRoute] Guid id)
{
var user = await _service.GetOneUser(id);
return View(user);
}
and I don't want change fromRoute to fromQuery it must be FromRoute

To use the Id as part of the route, you need to define it as part of the HttpGet attribute.
[HttpGet("UserDetails/{id}")]
public async Task<IActionResult> UserDetails([FromRoute] Guid id)
{
var user = await _service.GetOneUser(id);
return View(user);
}

Define the route for HttpGet:
[HttpGet("UserDetails/{id}")]
public async Task<IActionResult> UserDetails([FromRoute] Guid id)
{
var user = await _service.GetOneUser(id);
return View(user);
}

Related

Asp.net core attribute route issue

I have this code:
[Route("Users")]
public class UserRegistrationController : Controller
{
[HttpGet("details/{userId}")]
public async Task<IActionResult> UserDetails(Guid userId)
{
// .....
}
[HttpPost("Save")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SaveUser(UserVM userVM)
{
// ......
return RedirectToAction("details", "Users", new { userId = userVM.UserId });
}
}
If I save the user redirectToAction generate userId as query string, this create an issue.
I mean instead of
https://localhost:5001/Users/Details/5de304c7-4c69-4819-c879-08d90306b555
redirect to action creates the URL as
https://localhost:5001/Users/Details?userId=5de304c7-4c69-4819-c879-08d90306b555
which causes a 404 error.
How do I solve this issue? I want to pass userId in route as below
https://localhost:5001/Users/Details/5de304c7-4c69-4819-c879-08d90306b555
Thanks in advance.
The issue was, the action method UserDetails need to add route [Route("details")] This will solve the issue.
[Route("Users")]
public class UserRegistrationController : Controller
{
[HttpGet("details/{userId}")]
[Route("details")]
public async Task<IActionResult> UserDetails(Guid userId)
{
// .....
}
[HttpPost("Save")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SaveUser(UserVM userVM)
{
// ......
return RedirectToAction("details", "Users", new { userId = userVM.UserId });
}
}

ViewModel not passing value from Register Action to Async Register Action in MVC 5

I'm currently customizing the registration page to pass in a companyID during registration. I'm fairly new to MVC best practices so if this isn't the most ideal approach please let me know. I have already modified the IdentityModel to accommodate the CompanyID property.
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
public Company Company { get; set; }
public int CompanyId { get; set; }
}
Currently I'm modifying the default registration page as a test.
Observed Behavior: The correct CompanyID is being grabbed correctly via the lambda expression. It fails to pass the viewModel to the async RegisterController.
Since it fails to pass the viewModel from the other Register action it fails to assign the CompanyID and throws a foreign key error.
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
{
var viewModel = new RegisterViewModel
{
CompanyID = _context.Companies.First(c => c.CompanyName == "Company2").Id
};
return View("Register", viewModel);
}
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email, CompanyId = model.CompanyID };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
// For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=320771
// Send an email with this link
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking here");
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
Any recommendations is appreciated
You should add a field for the company id in the view.
In Register.cshtml, add:
<input type="hidden" name="CompanyId" value="#Model.CompanyId" />
Or, using the built-in HTML helper:
#Html.HiddenFor(m => m.CompanyId)

Asp.Net Core 2.0 RedirectToAction not working after post

I am new to MVC. Here is the gist of what I am doing on my View:
Display Identity User with their assigned User Roles.
Clone User Roles portion of page:
Have a SelectList where the end-user can select some other Identity User.
Display that selected user's current User Roles - I have this working in a Partial View.
Have a Clone User button and onclick remove all User Roles for the currently displayed user and then add them to all the Roles that the selected user from the
SelectList is assigned to.
I have the View working the way I want it to with one exception - after changing the user's Identity Roles (via clicking the Clone User link) the portion of my View does not refresh to show the new Roles. In my ViewModel that would be UserRoles. If I press CTRL+F5 to refresh then I see the newest Roles.
Here is my ViewModel:
public ApplicationUser AppUser { get; set; }
public TblEmployee Employee { get; set; }
[Display(Name = "User Roles")]
public List<Tuple<string, string>> UserRoles { get; set; }
[Display(Name = "Available Roles")]
public List<Tuple<string, string>> AllRoles { get; set; }
[Display(Name = "Select Employee")]
public List<SelectListItem> EmployeeList { get; set; }
I have a link on my page:
<a id="uxCloneUserButton" class="hidden">
<i class="glyphicon glyphicon-duplicate"></i> Clone User
</a>
And then in my script in my View I have the following:
$('#uxCloneUserButton').click(function () {
var selectedId = $('#uxEmployeeSelect').val();
var appUserId = $('#uxHiddenAppUserId').val();
var url = '#Url.Action("CloneUser", "ApplicationUser")';
//post id and username to controller action
$.post(url, { id: appUserId, cloneUserId: selectedId }, function (data) {
});
});
I get to the Action on my Controller with the proper parameters and it is working fine. In my Controller I have this:
[HttpPost]
public async Task<IActionResult> CloneUser(string id, string cloneUserId)
{
ApplicationUser currentUser = await _userManager.FindByIdAsync(id);
ApplicationUser cloneUser = await _userManager.FindByIdAsync(cloneUserId);
//Remove currentUser from all current roles
var currentUserRoles = await _userManager.GetRolesAsync(currentUser);
if (currentUserRoles.Count > 0)
await _userManager.RemoveFromRolesAsync(currentUser, currentUserRoles.ToArray());
var cloneUserRoles = await _userManager.GetRolesAsync(cloneUser);
if (cloneUserRoles.Count > 0)
await _userManager.AddToRolesAsync(currentUser, cloneUserRoles.ToArray());
return RedirectToAction(nameof(Details), new { id = id });
}
Your help is greatly appreciated. I just need this portion of the page to refresh after the click and I am all set.
UPDATE
I was able to get it working, but my solution doesn't feel like a good one.
I added a string to my ViewModel:
public string JSRedirectLocation { get; set; }
I set the property in my Details Action where I assign all of my VM properties:
//Redirect location
model.JSRedirectLocation = "/ApplicationUser/Details/" + model.AppUser.Id;
I changed my JavaScript to redirect after post:
//post id and username to controller action
$.post(url, { id: appUserId, cloneUserId: selectedId }, function (data) {
window.location = '#Model.JSRedirectLocation';
});
Can somebody tell me if I should be doing this some cleaner way, please? This solution seems to me to be redundant and feels like a workaround. Thanks.
Your request is ajax call, for this you must return json and then handle response redirect to target.So in action use :
[HttpPost]
public async Task<IActionResult> CloneUser(string id, string cloneUserId)
{
//.. your code
var redirectUrl = Url.Action(nameof(Details), new { id = id });
return Json(new {
redirectUrl = redirectUrl
});
}
and in ajax call use following code:
$.post(url, { id: appUserId, cloneUserId: selectedId }, function (data) {
window.location = data.redirectUrl;
});

WebApi: Get api/Record/id/Tags

In my ASP.NET MVC (6) web Web Api I have
[Produces("application/json")]
[Route("api/Record")]
public class RecordController : Controller
{
// ...
// GET: api/Record/5
[HttpGet("{id}", Name = "GetRecord")]
public async Task<Record> Get(string id)
{
var record = await repository.GetAsync<Record>(id);
return record;
}
Now, my Record contains some Tags.
I would like to return these tags via api/Record/5/Tags
How should I write my action in that case?
Assuming it is a property on the record create a route template to match the desired route and return the tags when called
[HttpGet("{id}/Tags")] // GET: api/Record/5/Tags
public async Task<IActionResult> GetTags(string id) {
var record = await repository.GetAsync<Record>(id);
if(record == null) return NotFound();
return Ok(record.Tags);
}

error in attribute routing MVC 5

I'm trying to route action with multiple optional parameters but it is not working. I'm sharing my code please guide me.
[HandleError]
[RouteArea("Admin", AreaPrefix = "sp-admin")]
[RoutePrefix("abc-system")]
[Route("{action}")]
public class AbcController : Controller
{
[Route("list/{id:int?}/{PersonID?}/{ref?}")]
public async Task<ActionResult> Index(int? id, int? PersonID, string #ref)
{
return view();
}
}
This will not work like this
http://anylocallink.com/sp-admin/abc-system/list/2/details
but work like this
http://anylocallink.com/sp-admin/abc-system/list/2/3/details
i want it to work if link has any of the optional parameter.
Please guide me
This won't work because the route won't know where to put the int parameter. You could do something like this though
[Route("list/{type}/{id}/{ref?}")]
public async Task<ActionResult> Index(string type, int id, string #ref)
{
if(type == "Person"){ ... }
else { ... }
return View();
}
Then you can do this for a route
list/Person/1/Details
list/ID/2/Details
You can specify 'alpha' as the constraint for the #ref action parameter and have two actions like following:
[Route("list/{id:int?}/{ref:alpha?}")]
public async Task<ActionResult> Index(int? id, string #ref)
{
return await Index(id, null, #ref);
}
[Route("list/{id:int?}/{personId:int?}/{ref:alpha?}")]
public async Task<ActionResult> Index(int? id, int? personId, string #ref)
{
return View();
}
This does work for both the scenarios. I prefer this because I do not have to
modify my routes again and again.

Resources