I'm working on implementing the Nexmo API into my code and have run into some issues with the controller. I'm getting the error
AmbiguousMatchException: The request matched multiple endpoints. Matches:
gardenplanning.Controllers.SMSController.Send (gardenplanning)
gardenplanning.Controllers.SMSController.Send (gardenplanning)
Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.ReportAmbiguity(CandidateState[] candidateState)
I have a feeling that it has to do with the 2 "Send" methods however I'm out of ideas as to how to modify one of them to fix the routing. The code integrated into my project is below. I commented out the Index method because in my HomeController it already has method that maps to Index. Any help as to what's causing the issue would be appreciated, thanks.
enter code here
namespace gardenplanning.Controllers {
public class SMSController : Controller
{
/*
// GET: /<controller>/
public IActionResult Index()
{
return View();
}
*/
//Send Action Method
[System.Web.Mvc.HttpGet]
public ActionResult Send()
{
return View();
}
//Action method containing "to" and "text" params
[System.Web.Mvc.HttpPost]
public ActionResult Send(string to, string text)
{
var results = SMS.Send(new SMS.SMSRequest
{
from = Configuration.Instance.Settings["appsettings:NEXMO_FROM_NUMBER"],
to = to,
text = text
});
return View();
}
}
}
The guide for Nexmo API is below
public ActionResult Index()
{
return View();
}
[System.Web.Mvc.HttpGet]
public ActionResult Send()
{
return View();
}
[System.Web.Mvc.HttpPost]
public ActionResult Send(string to, string text)
{
var results = SMS.Send(new SMS.SMSRequest
{
from = Configuration.Instance.Settings["appsettings:NEXMO_FROM_NUMBER"],
to = to,
text = text
});
return View("Index");
}
AmbiguousMatchException: The request matched multiple endpoints. Matches: gardenplanning.Controllers.SMSController.Send (gardenplanning) gardenplanning.Controllers.SMSController.Send (gardenplanning) Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.ReportAmbiguity(CandidateState[] candidateState)
As exception message indicated, the request matched two or more endpoints, which cause the issue.
To resolve ambiguous actions you can try to apply different HTTP verb templates to these two "Send" action methods, which constrains matching to requests with specific HTTP method only. And in ASP.NET Core app, you can use [Microsoft.AspNetCore.Mvc.HttpGet] instead of [System.Web.Mvc.HttpGet].
[HttpGet]
public ActionResult Send()
{
return View();
}
//Action method containing "to" and "text" params
[HttpPost]
public ActionResult Send(string to, string text)
{
var results = SMS.Send(new SMS.SMSRequest
{
from = Configuration.Instance.Settings["appsettings:NEXMO_FROM_NUMBER"],
to = to,
text = text
});
return View();
}
Besides, if you'd like to conditionally enable or disable an action for a given request based on query string, you can try to implement a custom ActionMethodSelectorAttribute. For more information, please check this blog:
https://devblogs.microsoft.com/premier-developer/defining-asp-net-core-controller-action-constraint-to-match-the-correct-action/
Related
I have three actions that I have a value in the first action, I pass this value to the second action. I want to use this amount in the third action, is there a solution?
First Action:
public async Task<IActionResult> LoginUser(LoginViewModel viewModel)
{
string code = await _user.SendActiveCodeForUser(viewModel);
TempData["Mobile"] = viewModel.Mobile;
return Json(new { redirectToUrl = Url.Action("Activate") });
}
Second Action:
public IActionResult Activate()
{
ViewBag.Mobile = TempData["Mobile"].ToString();
return View();
}
Third Action:
public async Task<IActionResult> SendActiveCode()
{
string code = await _user.SendActiveCodeAgain(ViewData["Mobile"].ToString());
return Json(0);
}
I used the session according to the link address below, I think it is a good solution
How To Use Sessions In ASP.NET Core
I have no idea where to start with this. I asked a question previously, and someone suggested I look at attribute routing. I read up on it, and while it helped me to create the below code, I'm still not sure how to limit it like I want to.
public class ReviewController : ApiController
{
private Review db = new Review();
////This GET works. It was auto-generated by Visual Studio.
// GET: api/Review
public IQueryable<Review> GetReview()
{
return db.Review;
}
////This is the GET that I'm trying to write, but don't know what to do
// GET: api
[Route("api/review/site")]
[HttpGet]
public IEnumerable<Review> FindStoreBySite(int SiteID)
{
return db.Review
}
////This GET also works and was generated by VS.
// GET: api/Review/5
[ResponseType(typeof(Review))]
public IHttpActionResult GetReview(int id)
{
Review review = db.Review.Find(id);
if (review == null)
{
return NotFound();
}
return Ok(review);
}
Essentially what I'm aiming to do is to limit what's returned by the API to only the results where the SiteID is equal to whatever value is passed into the URL. I'm not even sure where to get started with this, and googling/searching stack overflow for "what to put in web api return" has been fruitless.
How do I tell the API what I want to have returned based off a parameter besides ReviewID?
Edit: I've updated the code per the suggestions in the answer below, but now I'm getting a new error.
Here's the current code:
private ReviewAPIModel db = new ReviewAPIModel();
// GET: api/Review
[Route("api/Review")]
[HttpGet]
public IQueryable<Review> GetReview()
{
return db.Review;
}
// GET: api
[Route("api/Review/site/{siteid}")]
[HttpGet]
public IEnumerable<Review> FindStoreBySite(int siteid)
{
return db.Review.Where(Review => Review.SiteID == siteid);
}
// GET: api/Review/5
[ResponseType(typeof(Review))]
public IHttpActionResult GetReview(int id)
{
Review review = db.Review.Find(id);
if (review == null)
{
return NotFound();
}
return Ok(review);
}
}
Here's the error that I get:
Multiple actions were found that match the request
When I google it, it takes me to this question: Multiple actions were found that match the request in Web Api
However, I've tried the answers there (I've confirmed that I'm using Web API V2, and my webapiconfig.cs file includes the config.MapHttpAttributeRoutes(); line.
In addition, as you can see in my code above, I've included the appropriate routing. However, I'm still getting an error telling me that it's returning two conflicting API calls.
To pass parameters to a WebApi controller you need to add a [Route()] attribute to that controller and mark the part of the link that's used as the attribute with this {}.
To return reviews that only match the passed in parameter you need to use LINQ to filter the data.
Here is an example:
[Route("api/getFoo/{id}")]
public IHttpActionResult GetFoo(int id)
{
return db.Foo.Where(x => x.Id == id);
}
The {id} part of the string represents the id that will be in the url in your browser: http://localhost:51361/api/getFoo/2. The "2" in the url IS the {id} property that you marked in your [Route("api/getFoo/{id}")] attribute.
I also modified your code:
public class ReviewController : ApiController
{
...
[Route("api/review/site/{siteId}")]
[HttpGet]
public IEnumerable<Review> FindStoreBySite(int SiteID)
{
return db.Review.Where(review => review.Id == SiteID);
}
...
Your request url should look somewhat like this: http://localhost:51361/api/review/site?SiteID=2
This can be difficult to wrap your head around at first but you'll get used to it eventually. It's how arguments are passed to Controller Action parameters.
if you want to get parameters for GET, it's like a simple overload, but if it's done, POST is with [fromBody], because the URL is in the tag [Route ("/abc/123/{id}")]
example
code
[Route ("/abc/123/{idSite}")]
[HttpGet]
public HttpResponseMessage ControllerIdSite(int IdSite){
//CODE . . .
return Request.CreateResponse<int>(HttpStatusCode.OK, IdSite);
}
call
/abc/123/17
return
17
OR
[Route ("/abc/123")]
[HttpGet]
public HttpResponseMessage ControllerIdSite(int IdSite){
//CODE . . .
return Request.CreateResponse<int>(HttpStatusCode.OK, IdSite);
}
call
/abc/123?IdSite=17
return
17
This question already has answers here:
MVC [HttpPost/HttpGet] for Action
(4 answers)
Closed 2 years ago.
Why is this incorrect?
{
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
Some Code--Some Code---Some Code
return View();
}
[HttpPost]
public ActionResult Index()
{
Some Code--Some Code---Some Code
return View();
}
}
How can I have a controlller thas answer one thing when is "getted" and one when is "posted"?
Since you cannot have two methods with the same name and signature you have to use the ActionName attribute:
[HttpGet]
public ActionResult Index()
{
// your code
return View();
}
[HttpPost]
[ActionName("Index")]
public ActionResult IndexPost()
{
// your code
return View();
}
Also see "How a Method Becomes An Action"
While ASP.NET MVC will allow you to have two actions with the same name, .NET won't allow you to have two methods with the same signature - i.e. the same name and parameters.
You will need to name the methods differently use the ActionName attribute to tell ASP.NET MVC that they're actually the same action.
That said, if you're talking about a GET and a POST, this problem will likely go away, as the POST action will take more parameters than the GET and therefore be distinguishable.
So, you need either:
[HttpGet]
public ActionResult ActionName() {...}
[HttpPost, ActionName("ActionName")]
public ActionResult ActionNamePost() {...}
Or,
[HttpGet]
public ActionResult ActionName() {...}
[HttpPost]
public ActionResult ActionName(string aParameter) {...}
I like to accept a form post for my POST actions, even if I don't need it. For me it just feels like the right thing to do as you're supposedly posting something.
public class HomeController : Controller
{
public ActionResult Index()
{
//Code...
return View();
}
[HttpPost]
public ActionResult Index(FormCollection form)
{
//Code...
return View();
}
}
To answer your specific question, you cannot have two methods with the same name and the same arguments in a single class; using the HttpGet and HttpPost attributes doesn't distinguish the methods.
To address this, I'd typically include the view model for the form you're posting:
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
Some Code--Some Code---Some Code
return View();
}
[HttpPost]
public ActionResult Index(formViewModel model)
{
do work on model --
return View();
}
}
You received the good answer to this question, but I want to add my two cents. You could use one method and process requests according to request type:
public ActionResult Index()
{
if("GET"==this.HttpContext.Request.RequestType)
{
Some Code--Some Code---Some Code for GET
}
else if("POST"==this.HttpContext.Request.RequestType)
{
Some Code--Some Code---Some Code for POST
}
else
{
//exception
}
return View();
}
Can not multi action same name and same parameter
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(int id)
{
return View();
}
althought int id is not used
You can't have multiple actions with the same name. You could add a parameter to one method and that would be valid. For example:
public ActionResult Index(int i)
{
Some Code--Some Code---Some Code
return View();
}
There are a few ways to do to have actions that differ only by request verb. My favorite and, I think, the easiest to implement is to use the AttributeRouting package. Once installed simply add an attribute to your method as follows:
[GET("Resources")]
public ActionResult Index()
{
return View();
}
[POST("Resources")]
public ActionResult Create()
{
return RedirectToAction("Index");
}
In the above example the methods have different names but the action name in both cases is "Resources". The only difference is the request verb.
The package can be installed using NuGet like this:
PM> Install-Package AttributeRouting
If you don't want the dependency on the AttributeRouting packages you could do this by writing a custom action selector attribute.
Today I was checking some resources about the same question and I got an example very interesting.
It is possible to call the same method by GET and POST protocol, but you need to overload the parameters like that:
#using (Ajax.BeginForm("Index", "MyController", ajaxOptions, new { #id = "form-consulta" }))
{
//code
}
The action:
[ActionName("Index")]
public async Task<ActionResult> IndexAsync(MyModel model)
{
//code
}
By default a method without explicit protocol is GET, but in that case there is a declared parameter which allows the method works like a POST.
When GET is executed the parameter does not matter, but when POST is executed the parameter is required on your request.
I was under the impression that every View in your application has it's own unique URL. For example:
Home/Index
Home/Test
Home/Error
Home/Help
In my Upload controller I call on the Error view. Yet the URL stays on what it was before, not changing to reflect the error url.
[HttpPost]
public ActionResult Index(HttpPostedFileBase excelFile)
{
if (excelFile != null)
{
*Snip for brevity, everything is peachy here.*
return View();
}
else
{
return View("Error");
}
}
Any suggestions why this is the case?
Shouldn't the URL be /Upload/Error? Thank you for your help. :)
URLs do not map to Views.
URLs map to Controller actions.
See this http://weblogs.asp.net/scottgu/archive/2007/12/03/asp-net-mvc-framework-part-2-url-routing.aspx
If you want a URL of /Upload/Error
You could make:
public ActionResult Error()
{
return View();
}
[HttpPost]
public ActionResult Index(HttpPostedFileBase excelFile)
{
if (excelFile != null)
{
*Snip for brevity, everything is peachy here.*
return View();
}
else
{
return RedirectToAction("Error","Upload");
}
}
You are returning the content of the View. If you want the URL to change, you need to RedirectToAction()
If you want the URL to change to /Upload/Error, here's what you would add to your UploadController:
public ActionResult Error()
{
return View();
}
Then, instead of returning the Error view, you would call: return RedirectToAction("Error","Upload");.
This basically shows the difference between controllers, actions, and views - controller actions can return any view they want (or other ActionResult) to a request, but only on one URL, unless they "reroute" the request to another action.
In ASP.NET MVC every URL maps to a controller/action. So you can return whatever view from your controller action, this doesn't change the URL.
If you want to redirect to an error Page, then either include a ErrorController in your project or an Error action in your FileUploadController and then do a Redirect to the appropriate action:
public class ErrorController : Controller
{
public ActionResult FileUploadError()
{
return View(); //returns view "FileUploadError"
}
}
public class FileUploadController : Controller // the controller you use to upload your files
{
public ActionResult Error()
{
return View(); //return view "Error"
}
public ActionResult Index(HttpPostedFileBase excelFile) // action from your post
{
//... do the upload stuff
else
{
return RedirectToAction("Error"); // if you want to use the Error action in this controller;
// or
return RedirectToAction("FileUploadError", "Error"); // if you want to use the action on the ErrorController
}
}
}
i konw that you can have a period in a querystring parameter, but you cant specify a period in variable names in .net.
The following code obviously does not work, but my external system uses the period in the names. Is there a way to do this?
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(string hub.mode)
{
return View();
}
You could read the value directly from the Request hash:
[HttpPost]
public ActionResult Index()
{
string hubMode = Request["hub.mode"];
return View();
}
or using an intermediary class:
public class Hub
{
public string Mode { get; set; }
}
[HttpPost]
public ActionResult Index(Hub hub)
{
string hubMode = hub.Mode;
return View();
}
As you will notice . has special meaning for the ASP.NET MVC default model binder.