error in attribute routing MVC 5 - asp.net

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.

Related

Asp.net Core Web Api Multiple Controller Method For Get All

I have a controller which gets data from a database but i want to have multiple methods [Actions] for the same GetAll Method based on different key.
Example
consider this DB
can i have a controller with different GetAll method based on CountryID, AddressID, DepartmentID like this
[ApiController]
[Route("api/Users")]
public class UsersController : ControllerBase
{
//Without a key
[HttpGet]
public IEnumerable<User> GetAll()
{
return repository.GetAll();
}
[HttpGet("{addressId}")]
public async IEnumerable<User> GetAll([FromRoute]string addressId)
{
User user= repository.GetAll(addressId);
}
[HttpGet("{CountryID}")]
public async IEnumerable<User> GetAll([FromRoute]string CountryID)
{
User user= repository.GetAll(CountryID);
}
[HttpGet("{DepartmentID }")]
public async IEnumerable<User> GetAll([FromRoute]string DepartmentID )
{
User user= repository.GetAll(DepartmentID );
}
}
and if i can how can i call this different method from the http request.
thanks in advance
You can define routing rules like below:
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
// Without a key
[HttpGet]
// GET /api/Users
public IEnumerable<User> Get()
{
return repository.GetAll();
}
[HttpGet("AddressId/{id}")]
// GET /api/Users/AddressId/123
public IEnumerable<User> GetByAddressIdAll([FromRoute] int id)
{
....
}
[HttpGet("CountryId/{id:int}")]
// GET /api/Users/CountryId/456
public IEnumerable<User> GetByCountryIdAll([FromRoute] int id)
{
....
}
[HttpGet("DepartmentID/{id:int}")]
// GET /api/Users/DepartmentId/678
public IEnumerable<User> GetByDepartmentIdAll([FromRoute] int id)
{
....
}
}
See the documentation: Attribute routing with Http verb attributes

Error 405 not allowed on HttpPost in Asp.net core 3.1 web api

When i try to post the raw text with postman the server answer 405 not allowed. I try to add
app.UseCors(options => options.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
services.AddCors();
without any solution.
Whis is the code of the application:
[Route("api/[controller]")]
[ApiController]
public class VideoWallController : ControllerBase
{
// GET: api/<ValuesController>
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<ValuesController>/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
// POST api/<ValuesController>
[HttpPost]
public string prova([FromBody] VideoCallBack test)
{
return "true;";
}
[HttpPost]
public void teststring(string test)
{
}
// PUT api/<ValuesController>/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/<ValuesController>/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
If you want to reach that endpoint using the /teststring suffix, you need to put that in the endpoint template:
[HttpPost("teststring")]
public void teststring(string test)
{
}
Or, as #maghazade said, you can just reach the endpoint using the controller URL, without a suffix: https://localhost:44336/api/videowall
Also, the CORS settings are not needed to access the API using Postman. CORS is used only to configure the API access through a web application running on a web browser.
Firstly,as maghazade said,there are two endpoints for the post method.You can try to use [HttpPost("teststring")] as ferhrosa said.You can also add [action] to [Route("api/[controller]")],so that routes of actions will include their action names.If you add other actions with post method,the routes will not conflict anymore.
[Route("api/[controller]/[action]")]
[ApiController]
public class VideoWallController : ControllerBase
{
// GET: api/<ValuesController>/Get
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<ValuesController>/Get/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
// POST api/<ValuesController>/prova
[HttpPost]
public string prova()
{
return "true;";
}
// POST api/<ValuesController>/teststring
[HttpPost]
public void teststring([FromBody]string test)
{
}
// PUT api/<ValuesController>/Put/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/<ValuesController>/Delete/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
And if you want to pass string test from Body,you need to add [FromBody] and select JSON in postman.
result:

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 });
}
}

Asp.Net Core TempData attribute - value is always null

I'm using the [TempData] attribute on a property of my Controller class, e.g:
public class FooController : Controller
{
[TempData]
public string ReturnUrl { get; set; }
....
}
And setting this value in an Index action handling a GET request:
public IActionResult Index(string returnUrl = null)
{
this.ReturnUrl = returnUrl;
// Do stuff...
return View();
}
I have a second action handling a POST request which is raised from the first (Index) action, it's here that I need to read the TempData value back:
[HttpPost]
public IActionResult HandlePost(int id)
{
// Do post things...
// Read value from [TempData] backed property
string returnUrl = this.ReturnUrl;
return this.Redirect(returnUrl);
}
However I'm finding that the value of the ReturnUrl property is always null.
But if I use TempData directly like this:
// Set return url
TempData["returnUrl"] = returnUrl;
And
// Get return url
string returnUrl = TempData["returnUrl"] as string;
It works fine - am I using the attribute incorrectly? Is it possible to use [TempData] in this way?
I can get returnUrl with this.ReturnUrl,here is a demo:
Controller:
public class Test1Controller : Controller
{
[TempData]
public string ReturnUrl { get; set; }
public IActionResult Index(string returnUrl)
{
this.ReturnUrl = returnUrl;
return View();
}
[HttpPost]
public IActionResult HandlePost()
{
string returnUrl = this.ReturnUrl;
return Ok();
}
}
Index.cshtml:
<form method="post" asp-action="HandlePost">
<input type="submit" value="submit"/>
</form>
url:
https://localhost:44355/Test1?returnUrl=ssss
result:

Is there a variable to inform Edit Action if I come from Create in mvc?

Using mvc3, is there some variable to inform if I came from a Create to Edit action?
If the routing is used as well in a GET I want a certain behavior, otherwise if the CreateAction has been fired I want to open a different context Edit.
Failed first solution: Creating a extra Action Edit, fires: The current request for action 'Edit' on controller type 'XController' is ambiguous between the two action methods.
More important, is there a misinterpretation of MVC? Does this solution sound weird to anyone? I'm facing nice doubts with mvc. :)
You can either add an optional parameter to your Edit action that flags whether or not you came from the Add action or you could create an entirely new action (with a unique name from Edit).
The former would look something like:
public ActionResult Edit(int id, bool? fromCreate = false)
{
if(fromCreate)
{
// do your special processing
}
}
And the latter, obviously, would be:
public ActionResult Edit(int id)
{
}
public ActionResult EditNewlyCreated(int id)
{
}
In your create view or create controller action (no razor markup if its in the action):
#{ TempData["FromCreate"] = true; }
And then in your edit get action:
public ActionResult Edit()
{
if( TempData.ContainsKey("FromCreate") )
{
var fromCreate = (bool)TempData["FromCreate"];
TempData.Remove("FromCreate");
if( fromCreate )
{
//insert logic
}
}
}
If you have something linke this in MyView view:
#model ModelClass
#Html.BeginForm()
{
if(Model.Id > 0)
{
#Html.HiddenFor(m => m.Id);
}
...
}
And in controller:
public ActionResult Edit(int id)
{
var model = modelRepository.GetById(id);
Return View("MyView", model);
}
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
var model = modelRepository.GetById(id);
TryUpdateModel(model)
{
modelRepository.Save(model);
}
}
public ActionResult Create()
{
var model = new ModelClass();
Return View("MyView", model);
}
[HttpPost]
public ActionResult Create(ModelClass model)
{
...
}
In view, if Model.Id > 0 it means that we entered view using Edit action and when form posts, if there will be Id field in post parameters (hidden for id) then Edit(with HttpPost) will be called, else if there will not be Id parameter then Create action will be called
If you have 2 Edit methods, they must have different method inputs to differentiate them.
public ActionResult Edit(int id)
{
return View(db.GetWidget(id));
}
public ActionResult Edit(int id, string username)
{
ViewBag.Username = username;
return View(db.GetWidget(id));
}
Or, just make the method into one with an optional parameter
public ActionResult Edit(int id, string username = "")
{
ViewBag.Username = username;
return View(db.GetWidget(id));
}
I would also recommend painting a method attribute such as [HttpGet], [HttpPost], etc.

Resources