ASP.Net WebApi: Invalid URL generated in BeginForm - asp.net

I'm beginning to use WebApi and I'm having an issue with a URL being incorrectly generated
I have an ApiController like this:
public class EntriesController : ApiController
{
public HttpResponseMessage Post(Entry entry)
{
...
}
}
And I was trying to create a standard controller (i.e. not webapi) in the same project to test this api (I already tested the api with fiddler and everything is ok there).
I assumed I could use the standard HTML helpers like this:
#using (Html.BeginForm("Post", "Entries"))
However this generates the following markup:
<form action="/Entries/Post" method="post">
and I expected it to generate
<form action="/api/Entries" method="post">
What is the correct way to generate the API url from a view?
I am using the default api and controller routes.
Thanks

#using (Html.BeginForm("Post", "Entries"))
You can not put WebAPI controller and method in MVC BeginForm like above. You need to pass MVC Controller and action to the BeginForm.
You can create a WebAPI EntriesController instance in your MVC controller, then use this instance to call the WebAPI method. See below:
//MVC Controller
public class EntriesController : Controller
{
[HttpGet]
public ActionResult Entries()
{
return View();
}
[HttpPost]
public ActionResult Entries(SomeModels model)
{
if (ModelState.IsValid)
{
var api = new EntriesController(); // Create WebAPI instance Here
api.Post(model.entry);
return RedirectToAction("Index", "Home");
}
return View();
}
}

This is technically possible by doing:
#using (Html.BeginForm("Post", "api/Entries"))
Don't forget, the "Post" value in the .BeginForm() extension method doesn't mean anything to an out-of-box Web Api route setup. Only the url and HTTP action matter (and any additional values on the URL for method overloading)

You would need to use BeginRouteForm as link generation to Web API routes always depends on the route name. Also make sure to supply the route value called httproute as below.
#using (Html.BeginRouteForm("DefaultApi", new { controller="Entries", httproute="true" }))

Related

HttpPost and Authorize leads to Error: Server Error in '/' Application. The resource cannot be found

In my ASP.Net (.net 4.7 + MVC5) web application I have an action Search() which leads to the an error after authorising the user. The error is as follows:
Server Error in '/' Application.
The resource cannot be found.
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: /Search
Version Information: Microsoft .NET Framework Version:4.0.30319;
ASP.NET Version:4.7.2106.0
The action is implemented as follows:
[Authorize]
[Route("Search")]
[HttpPost]
public ActionResult Search(SearchViewModels passedQuery)
{
//Action working code here
return View("~/Views/Home/Search.cshtml", passedQuery);
}
Form submission code as follows:
#using (Html.BeginForm("Search", "Home"))
{
<div class="form-group">
#Html.TextBoxFor(m => m.Content, new { #class = "form-control" })<br />
<button type="submit" class="btn btn-danger">SEARCH</button>
</div>
}
If I take the [HttpPost] out from the action then during debugging mode the application works perfectly even after authorising the user (No error thrown). But without using the [HttpPost] the code does nothing on the production server.
Can someone help with:
Why the error message is shown when [HttpPost] is used with
[Authroize]?
When and why to use [HttpPost]? (I have referred to few posts and
documents regarding the usage of HttpPost and HttpGet but still I
am finding it liitle hard to grasp the concept of their
appropriate usage [1], [2])
I am very new to ASP.Net and MVC development and any help is really appreciated. Thank you.
This line which returns a view makes no sense:
return View("~/Views/Home/Search.cshtml", passedQuery);
In proper usage, just use view name instead of its complete virtual path with extension and ensure that the Search method belongs to HomeController:
[Authorize]
public class HomeController : Controller
{
[HttpPost]
public ActionResult Search(SearchViewModels passedQuery)
{
return View("Search", passedQuery); // or just return View(passedQuery)
}
}
Note that AuthorizeAttribute requires authorized users who meet the specified requirements to access the method, so it's no use to set it on POST methods while submitting the form which already loaded. You need to specify AuthorizeAttribute on controller class when users requesting page they're wanted to view based from authentication roles, and set AllowAnonymousAttribute on methods which you're allow anonymous users to get through.
Hence, your Search action pair should constructed like this example:
[Authorize]
public class HomeController : Controller
{
// other stuff
[Route("Search")]
[HttpGet] // getting search page request
[AllowAnonymous] // allow anonymous users to load search page
public ActionResult Search()
{
return View();
}
[HttpPost] // posting content using inserted values on form
[AllowAnonymous] // allow anonymous users to load search page
public ActionResult Search(SearchViewModels passedQuery)
{
return View("Search", passedQuery); // or just return View(passedQuery)
}
// other stuff
}

ASP.NET Core over writing my URL

I'm having a problem where the action in my controller called GiftCards is over-writing the actual URL/View page name.
public IActionResult SomeStupidName()
{
return ("Checkout");
}
my url turns into
www.mywebsite.com/GiftCards/SomeStupidName
while I want it to be
www.mywebsite.com/GiftCards/Checkout
Some further detail would be that this is execute with an form-submit where the form has an
asp-action="SomeStupidName"
You have to understand that www.mywebsite.com/GiftCards/SomeStupidName means SomeStupidName is an action of controller GiftCards so www.mywebsite.com/GiftCards/Checkout means Checkout is an action of controller GiftCards but in your code you are returning View from GiftCards action not redirecting to other action. Lets suppose I have action name Checkout.
public IActionResult Checkout()
{
return View("Checkout");
}
It will return the view Checkout when I enter www.mywebsite.com/GiftCards/Checkout so now I can redirect to action from any other action like:
public IActionResult SomeStupidName()
{
return RedirectToAction("Checkout");
}
You can use "Attribute Routing" in Asp.Net Core. Return the proper view in your controller.Try this code !!
[Route("GiftCards/Checkout")]
public IActionResult SomeStupidName()
{
return View("Checkout");
}
Attribute routing is set of attributes to map actions directly to route templates. We can mention rewrite url inside the "()" in "[Route("")]" Attribute.
More details about attribute routing in Asp.net core : click here

Ajax with ViewComponent

How to use AJAX for ViewComponent in Asp.Net Core? That is calling the method #Component.Invoke ("ComponentName", SomeData); ViewComponent will involve various views depending on SomeData without rebooting the main view.
Update
My solution is:
$(function () {
$(Container).load('ControllerName/ControllerAction', {
ArgumentName: ArgumentValue });
});
Controller :
public IActionResult ControllerAction(string value)
{
return ViewComponent("ViewComponent", value);
}
Is there a way to directly use a ViewComponent as AjaxHelpers in previous versions?
Your solution is correct. From the docs:
[ViewComponents are] not reachable directly as an HTTP endpoint, they're invoked from your code (usually in a view). A view component never handles a request.
While view components don't define endpoints like controllers, you can easily implement a controller action that returns the content of a ViewComponentResult.
So as you suggested, a lightweight controller can act as an AJAX proxy to our ViewComponent.
public class MyViewComponentController : Controller
{
[HttpGet]
public IActionResult Get(int id)
{
return ViewComponent("MyViewComponent", new { id });
}
}

How to create url for MVC action from ApiController?

Is there a way to create an url that liks to an MVC action from an ApiController? I see examples of doing this the other way around, to reach ApiController from with MVC using MVC's UrlHelper (http://blogs.msdn.com/b/roncain/archive/2012/07/17/using-the-asp-net-web-api-urlhelper.aspx).
Any help would be greatly appreciated. Thanks.
You can use the Url property on the API controller to find a route to a web api or an MVC controller. Here is an example of creating a link to the default project template MVC method AccountController.Login(string returnURL).
public class SOExampleController : ApiController
{
public SOExample GetSOExample()
{
var url = Url.Route("Default", new {controller = "Account", action = "Login", returnUrl = "hello"});
return new SOExample{URL = url};
}
}
public class SOExample
{
public string URL {get;set;}
}
When ran you get the url value of the "/Account/Login?returnUrl=hello". It looks like you may need to prepend the domain, which should be trivial to find.

Using Html.Action with a controller in a subfolder

I am building a webapge using asp.net mvc4. For organization, I would like to put some of the controllers inside a subfolder in the Controllers folder. For example:
Controllers
AccountController
BlahController
Dashboard (Folder)
ChickenController
BeefController
To use BeefController (which returns a partial view), it seems as though I should use:
#Html.Action("Index", "Dashboard/BeefDashboard")
However this gets me the following error:
The controller for path '/' was not found or does not implement IController.
How would I be able to use BeefController?
There no physical sub folder concepts in the ASP.NET MVC world. What you should do is to have an action method in Dashboard controller, which accepts a parameter and then return specific views according to that.
public class DashBoardController: Controller
{
public ActionMethod Index(string id)
{
if(id=="chicken")
{
return PartialView("Chicken");
}
else if(id=="beef")
{
return PartialView("beef");
}
return View("NotFound");
}
}
Now you can access those like
Dashboard/beef
Dashboard/chicken

Resources