I am trying to receive a HTTP POST from Mirth Connect in my ASP.NET MVC solution but I don´t know how this data comes to my web application. I create a controller to receive it:
public class IntegrationController : Controller
{
public ActionResult Index()
{
var c = HttpContext.CurrentHandler;
var v = c.ToString();
Console.Write("The value is" + v);
return View();
}
}
What should I receive in Index()? A dictionary? And once I receive it, how to how it in the viewer?
Thank you.
I use the [FromBody] Annotation, then I can treat the incoming like any other parameter.
[Route("api/gateways")]
[Route("api/connectedDevices")]
[Route("api/v0/gateways")]
[Route("api/v0/connectedDevices")]
[HttpPost]
public HttpResponseMessage Create([FromBody] IGatewayNewOrUpdate device,
I do have a case when I read the content directly from the Request;
[HttpPost]
public HttpResponseMessage AddImageToScene(long id, string type)
{
SceneHandler handler = null;
try
{
var content = Request.Content;
I think it may be a little different for different versions, but I hope that will give you a starting point.
Try something like this. This will work, if v is the name of a field that will be posted from a form.
public class DataModel
{
public string v {get; set;}
}
public class IntegrationController : Controller
{
[HttpPost]
public ActionResult Index(DataModel model)
{
Console.Write("The value is" + model.v);
return View();
}
}
If you receive Json then a can add [FromBody]
public class IntegrationController : Controller
{
[HttpPost]
public ActionResult Index([FromBody] DataModel model)
{
Console.Write("The value is" + model.v);
return View();
}
}
Related
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/
I have two projects, one is SPA and the other one is ASP.NET aspx project.
Normally those two projects communicate with each other via web API.
There is a running method like this (from SPA to ASP.NET)
[HttpPost]
[Route("DenyInvoice/{approvalId:long}")]
public IHttpActionResult DenyInvoice(long approvalId)
{
string exMsg = "";
if (DenyInvoice(approvalId, this.UserId, this.Email, out exMsg))
return Ok();
return BadRequest(exMsg);
}
This method works as I wanted, but I want to write a method that accepts a DTO variable like;
[HttpPost]
[Route("InvoiceDetailUpdate/{invoices : invoiceDetailDtoModel}")]
public IHttpActionResult InvoiceDetailUpdate(invoices : invoiceDetailDtoModel)
{
....
}
Is it possible? How should I do this?
Can you help?
Thank you..
Use [FromBody] to pass in the POST parameter. See https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-web-api#implement-the-other-crud-operations for more details.
[HttpPost]
[Route("InvoiceDetailUpdate")]
public IHttpActionResult InvoiceDetailUpdate([FromBody]InvoiceDetailDtoModel invoices)
{
....
}
You want to retrieve parameter via post so there is no required for get parameter routing.
[HttpPost]
[Route("InvoiceDetailUpdate")]
public IHttpActionResult InvoiceDetailUpdate(InvoiceDetailDtoModel invoices)
{
....
}
Also, be careful with the name convention of a class.
In the following code, you can use like this :
public class BooksController : ApiController
{
[Route("api/books")]
public IEnumerable<Book> GetBooks() { ... }
[Route("api/books/{id:int}")]
public Book GetBook(int id) { ... }
[Route("api/books")]
[HttpPost]
public HttpResponseMessage CreateBook(Book book) { ... }
}
Now , you can see this link attribute-routing-in-web-api-2
and change your code to above figure
[Route("api/YourController/InvoiceDetails")]
public IHttpActionResult InvoiceDetailUpdate(InvoiceDetails invoiceDetailsDto)
I have the following action methods:
[HttpGet]
public ActionResult DBLookupIndex(DBLookupDTO dto)
{
dto.Areas = _ph.GetProfiles();
return View(dto);
}
[HttpGet]
public ActionResult Search(DBLookupDTO dto)
{
dto.Orders = _oh.GetOrders(dto.OrderNumber, dto.ProductNumber, dto.DateRange, dto.SelectDeleted, dto.AreaId);
return RedirectToAction("DBLookupIndex", dto);
}
The user simple enters valid information into one or more of the textboxes on the webpage, and submits it to the controller by pressing submit. This then calls the Search-action.
By debugging, I have determined, that the function works. It does find what it should, but it is not passed on, when it redirects back to the DBLookupIndex-action.
My question is; What am I doing wrong? I have seen code examples similar to the one above provided as solutions for similar issues, but for some reason it does not work for me.
EDIT:
I realised after the first answer came, that I was missing some information. Whenever the page is loaded, it has to update a dropdown that is rendered in the view, in case new profiles/areas have been added. I do that with razor:
<select name="AreaId" asp-for="AreaId" class="form-control">
<option disabled selected value=""> -- Vælg et område -- </option>
#foreach (var a in Model.Areas)
{
<option value="#a.ProfileId">#a.Name</option>
}
That is why I have to used RedirectToAction, instead of having a new action render the same view. Unless there is a better way to do it? :)
Thank in advance for any help!
In addition to Peter B's answer, another option is to store it in the TempData object that exists on your base controller.
[HttpGet]
public ActionResult Search(DBLookupDTO dto)
{
var orders = new List<Order>();
TempData["orders"] = orders;
return RedirectToAction("DBLookupIndex", dto);
}
You can then retrieve the data on the following request like so:
[HttpGet]
public ActionResult DBLookupIndex(DBLookupDTO dto)
{
var yourData = (List<Order>)TempData["orders"];
...
return View(dto);
}
The TempData object exists for a single request and is then cleared up. You can read more about it here.
The object parameter in RedirectToAction is meant to set querystring values for the URL that is generated and then sent to the browser as a "Redirect to other page" result. It is supposed to be an object similar to new { id = 7, otherParam = 5 }, or a Dictionary, but certainly not a recordset or any other kind of business data.
It seems like you want to show the View that belongs to the DBLookupIndex action. That can be done in a very simple way, like this:
[HttpGet]
public ActionResult Search(DBLookupDTO dto)
{
dto.Orders = _oh.GetOrders(dto.OrderNumber, dto.ProductNumber, dto.DateRange, dto.SelectDeleted, dto.AreaId);
return View("DBLookupIndex", dto); // Render the "DBLookupIndex" view and pass it the dto object
}
Update: if you need dto.Areas to be always set, you can create a method that just does that.
Like this (1):
[HttpGet]
public ActionResult DBLookupIndex(DBLookupDTO dto)
{
SetAreas(dto);
return View(dto);
}
[HttpGet]
public ActionResult Search(DBLookupDTO dto)
{
dto.Orders = _oh.GetOrders(dto.OrderNumber, dto.ProductNumber, dto.DateRange, dto.SelectDeleted, dto.AreaId);
SetAreas(dto);
return View("DBLookupIndex", dto);
}
private void SetAreas(DBLookupDTO dto)
{
dto.Areas = _ph.GetProfiles();
}
Or like this (2):
[HttpGet]
public ActionResult DBLookupIndex(DBLookupDTO dto)
{
return SetAreasAndView(dto);
}
[HttpGet]
public ActionResult Search(DBLookupDTO dto)
{
dto.Orders = _oh.GetOrders(dto.OrderNumber, dto.ProductNumber, dto.DateRange, dto.SelectDeleted, dto.AreaId);
return SetAreasAndView(dto);
}
private ActionResult SetAreasAndView(DBLookupDTO dto)
{
dto.Areas = _ph.GetProfiles();
return View("DBLookupIndex", dto);
}
I am separating my query and command on service side like this:
public class ProductCommandService{
void AddProduct(Product product);
}
public interface ProductQueryService{
Product GetProduct(Guid id);
Product[] GetAllProducts();
}
Command Query Separation accepts that a method should change state or return a result. There is no problem.
public class ProductController: ApiController{
private interface ProductCommandService commandService;
private interface ProductQueryService queryService;
[HttpPost]
public ActionResult Create(Product product){
commandService.AddProduct(product);
return ???
}
[HttpGet]
public Product GetProduct(Guid id){
return commandService.GetProduct(id);
}
[HttpGet]
public Product[] GetAllProducts(){
return queryService.GetAllProducts();
}
}
I am applying command query separation on service side but not applying in controller class. Because user may want to see created product result. But commandService works in Create Controller Action metod and does not return created product.
What will we return to user? AllProducts? Will CQS applly about application lifecycle?
In such scenario I usually go with generating new entity Ids on the client.
Like this:
public class ProductController: Controller{
private IProductCommandService commandService;
private IProductQueryService queryService;
private IIdGenerationService idGenerator;
[HttpPost]
public ActionResult Create(Product product){
var newProductId = idGenerator.NewId();
product.Id = newProductId;
commandService.AddProduct(product);
//TODO: add url parameter or TempData key to show "product created" message if needed
return RedirectToAction("GetProduct", new {id = newProductId});
}
[HttpGet]
public ActionResult GetProduct(Guid id){
return queryService.GetProduct(id);
}
}
This way you are also following POST-REDIRECT-GET rule which you should do even when not using CQRS.
EDITED:
Sorry, did not notice that you are building an API, not MVC application.
In this case I would return a URL to newly created Product:
public class ProductController: ApiController{
private IProductCommandService commandService;
private IProductQueryService queryService;
private IIdGenerationService idGenerator;
[HttpPost]
public ActionResult Create(Product product){
var newProductId = idGenerator.NewId();
product.Id = newProductId;
commandService.AddProduct(product);
return this.Url.Link("Default", new { Controller = "Product", Action = "GetProduct", id = newProductId });
}
[HttpGet]
public ActionResult GetProduct(Guid id){
return queryService.GetProduct(id);
}
}
Command methods does not return anything, only changes state but command events can return arguments that you need.
commandService.OnProductAdd += (args)=>{
var id = args.Product.Id;
}
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.