In my web api I am primarily using POST methods so the client can send function parameters using JSON. However, I noticed that having a model (dto) for these params on both the client and server doesn't really make sense. Or does it?
Since I am using POST is creating a model object for binding required? Sometimes I am actually dealing with an entity like Customer and it makes complete sense, but when I am dealing with 3 random parameters do I really have to create a model (dto) so I can retrieve the data into the POST function?
Here is my POST function in my WEB API:
[HttpPost, Route("GetAccountInformation")]
public IActionResult RetrieveAccountInformation(GetAccountInformationParamsObj myParams)
{
var retVal = _repository.GetAccountInformation(myParams.StartDate, myParams.EndDate, myParams.Count)
return Ok(retVal);
}
Here is an example of what the client will send in the POST body:
{
"StartDate":"10-25-2015",
"EndDate":"11-25-2015",
"Count":20
}
It would be great if the following would work but the param values end up being empty when making the request:
[HttpPost, Route("GetAccountInformation")]
public IActionResult RetrieveAccountInformation([FromBody] DateTime startDate, DateTime endDate, int count = 0)
{
}
You dont need to do a POST, what you trying to do doesn't justify a POST, just try to get the data from uri using the Attr [FromUri] like this:
[HttpGet, Route("GetAccountInformation")]
public IActionResult Get([FromUri]GetAccountInformationParamsObj #params)
{
var retVal = _repository.GetAccountInformation(#params.StartDate, #params.EndDate, #params.Count)
return Ok(retVal);
}
and make the request like this:
/GetAccountInformation?StartDate=10-25-2015&EndDate=11-25-2015&Count=20
Related
I'm using ASP.NET MVC 4. I am trying to pass data from one controller to another controller. I'm not getting this right. I'm not sure if this is possible?
Here is my source action method where I want to pass the data from:
public class ServerController : Controller
{
[HttpPost]
public ActionResult ApplicationPoolsUpdate(ServiceViewModel viewModel)
{
XDocument updatedResultsDocument = myService.UpdateApplicationPools();
// Redirect to ApplicationPool controller and pass
// updatedResultsDocument to be used in UpdateConfirmation action method
}
}
I need to pass it to this action method in this controller:
public class ApplicationPoolController : Controller
{
public ActionResult UpdateConfirmation(XDocument xDocument)
{
// Will add implementation code
return View();
}
}
I have tried the following in the ApplicationPoolsUpdate action method but it doesn't work:
return RedirectToAction("UpdateConfirmation", "ApplicationPool", new { xDocument = updatedResultsDocument });
return RedirectToAction("UpdateConfirmation", new { controller = "ApplicationPool", xDocument = updatedResultsDocument });
How would I achieve this?
HTTP and redirects
Let's first recap how ASP.NET MVC works:
When an HTTP request comes in, it is matched against a set of routes. If a route matches the request, the controller action corresponding to the route will be invoked.
Before invoking the action method, ASP.NET MVC performs model binding. Model binding is the process of mapping the content of the HTTP request, which is basically just text, to the strongly typed arguments of your action method
Let's also remind ourselves what a redirect is:
An HTTP redirect is a response that the webserver can send to the client, telling the client to look for the requested content under a different URL. The new URL is contained in a Location header that the webserver returns to the client. In ASP.NET MVC, you do an HTTP redirect by returning a RedirectResult from an action.
Passing data
If you were just passing simple values like strings and/or integers, you could pass them as query parameters in the URL in the Location header. This is what would happen if you used something like
return RedirectToAction("ActionName", "Controller", new { arg = updatedResultsDocument });
as others have suggested
The reason that this will not work is that the XDocument is a potentially very complex object. There is no straightforward way for the ASP.NET MVC framework to serialize the document into something that will fit in a URL and then model bind from the URL value back to your XDocument action parameter.
In general, passing the document to the client in order for the client to pass it back to the server on the next request, is a very brittle procedure: it would require all sorts of serialisation and deserialisation and all sorts of things could go wrong. If the document is large, it might also be a substantial waste of bandwidth and might severely impact the performance of your application.
Instead, what you want to do is keep the document around on the server and pass an identifier back to the client. The client then passes the identifier along with the next request and the server retrieves the document using this identifier.
Storing data for retrieval on the next request
So, the question now becomes, where does the server store the document in the meantime? Well, that is for you to decide and the best choice will depend upon your particular scenario. If this document needs to be available in the long run, you may want to store it on disk or in a database. If it contains only transient information, keeping it in the webserver's memory, in the ASP.NET cache or the Session (or TempData, which is more or less the same as the Session in the end) may be the right solution. Either way, you store the document under a key that will allow you to retrieve the document later:
int documentId = _myDocumentRepository.Save(updatedResultsDocument);
and then you return that key to the client:
return RedirectToAction("UpdateConfirmation", "ApplicationPoolController ", new { id = documentId });
When you want to retrieve the document, you simply fetch it based on the key:
public ActionResult UpdateConfirmation(int id)
{
XDocument doc = _myDocumentRepository.GetById(id);
ConfirmationModel model = new ConfirmationModel(doc);
return View(model);
}
Have you tried using ASP.NET MVC TempData ?
ASP.NET MVC TempData dictionary is used to share data between controller actions. The value of TempData persists until it is read or until the current user’s session times out. Persisting data in TempData is useful in scenarios such as redirection, when values are needed beyond a single request.
The code would be something like this:
[HttpPost]
public ActionResult ApplicationPoolsUpdate(ServiceViewModel viewModel)
{
XDocument updatedResultsDocument = myService.UpdateApplicationPools();
TempData["doc"] = updatedResultsDocument;
return RedirectToAction("UpdateConfirmation");
}
And in the ApplicationPoolController:
public ActionResult UpdateConfirmation()
{
if (TempData["doc"] != null)
{
XDocument updatedResultsDocument = (XDocument) TempData["doc"];
...
return View();
}
}
Personally I don't like to use TempData, but I prefer to pass a strongly typed object as explained in Passing Information Between Controllers in ASP.Net-MVC.
You should always find a way to make it explicit and expected.
I prefer to use this instead of TempData
public class Home1Controller : Controller
{
[HttpPost]
public ActionResult CheckBox(string date)
{
return RedirectToAction("ActionName", "Home2", new { Date =date });
}
}
and another controller Action is
public class Home2Controller : Controller
{
[HttpPost]
Public ActionResult ActionName(string Date)
{
// do whatever with Date
return View();
}
}
it is too late but i hope to be helpful for any one in the future
If you need to pass data from one controller to another you must pass data by route values.Because both are different request.if you send data from one page to another then you have to user query string(same as route values).
But you can do one trick :
In your calling action call the called action as a simple method :
public class ServerController : Controller
{
[HttpPost]
public ActionResult ApplicationPoolsUpdate(ServiceViewModel viewModel)
{
XDocument updatedResultsDocument = myService.UpdateApplicationPools();
ApplicationPoolController pool=new ApplicationPoolController(); //make an object of ApplicationPoolController class.
return pool.UpdateConfirmation(updatedResultsDocument); // call the ActionMethod you want as a simple method and pass the model as an argument.
// Redirect to ApplicationPool controller and pass
// updatedResultsDocument to be used in UpdateConfirmation action method
}
}
I'm having an issue with an endpoint, it takes an object which isn't binding and consequently returning a 400 bad request.
I've gotten around this issue by passing in the individual properties of the object rather than the object itself, but would prefer the passing of an object.
WebClient webClient = new WebClient();
webClient.QueryString.Add("firstName", "value1");
webClient.QueryString.Add("lastName", "value2");
string result = webClient.DownloadString(url);
[HttpGet]
public async Task<IActionResult> DoSomething(string firstName, string lastName)
{
// this endpoint works perfectly
return Ok();
}
[HttpGet]
public async Task<IActionResult> DoSomething([FromBody]SubmitModel model)
{
// this endpoint returns a 400 bad request
return Ok();
}
public class SubmitModel
{
public string FirstName {get; set;}
public string LastName {get; set;
}
By design, GET request does not contain data in the request body.
So when your Submit method recieves a request, it can't bind the model from the body as the data does not exist and therefore returns an bad request response.
As your method is named Submit, it sounds like you should use a POST request instead. POST request, by design, sends data in the request body and is suited for submitting data to the server.
Try it like this
[HttpPost]
public async Task<IActionResult> Submit([FromBody]SubmitModel model)
{
// this endpoint returns a 400 bad request
return Ok();
}
I don't know if there is a good way to do what you're wanting. To get a little bit closer you can add this attribute to pull directly from the url
[HttpGet]
public ActionResult Get([FromUri]SubmitModel model)
{
// this endpoint returns a 400 bad request
return Ok();
}
Another thing you can do if need be is create an extension method that reflects over the model and adds all the properties/values to the query string. Some good examples here How do I serialize an object into query-string format?
I am new to ASP.Net WebAPI and I'm trying to use the NorthWind database to practice.
Now, I have a ProductsController which contains a GetAllProducts and a GetAllProductsById actions.
On the client-side, I have a multiselect dropdown control which is been populated with the categories of products from the CategoriesController class.
I want to populate a second dropdown control (not a multiselect) with the Products from the Categories that was selected in the category dropdown list.
My GetAllProductsById controller looks like this:
public IHttpActionResult GetAllProductsById([FromUri]int[] id)
{
using (var ctx = new NorthwindEntities())
{
<some codes...>
}
return Ok(productList);
}
Calling this service from the client, the URL look like this: http://localhost:1234/api/Products/GetAllProductsById?id=1&id=2&id=3
This is looks good with few parameters but what if the User selects more categories (let's say 30 out of 40 categories)? This means the URL would be so long.
So, I decide to use a POST to do a GET's job by decorating my action with HttpPost:
[HttpPost]
public IHttpActionResult GetAllProductsById([FromBody]int[] id)
This allows me to send the id parameters from the Body of my request.
Now, I am wondering if this style is correct or if someone can point me to a much cleaner way of passing a long list of parameters from a client to my webservice.
Thanks in advance.
NB:
Using Elasticsearch is not an option at the as suggested in the link below:
HTTP GET with request body
I tried using a modal class but it also has the same effect
http://localhost:1234/api/Products/GetAllProductsById?input.id=1&input.id=2
You could do something a bit hacky: use HTTP headers.
First the client should add the ID list as a header to its HTTP request (C# example follows):
var webRequest = System.Net.WebRequest.Create(your_api_url);
webRequest.Headers.Add("X-Hidden-List", serialized_list_of_ids);
And then on the API side:
[HttpGet]
public IHttpActionResult GetAllProductsById()
{
string headerValue = Request.Headers.GetValues("X-Hidden-List").FirstOrDefault();
if (!string.IsNullOrEmpty(headerValue))
{
var results = DeserializeListAndFetch(headerValue);
return Ok(results);
}
else
{
var results = ReturnEverything();
return Ok(results);
// or if you don't want to return everything:
// return BadRequest("Oops!");
}
}
DeserializeListAndFetch(...) and ReturnEverything() would do the actual database querying.
You can have the array of Id in a model class and in the method parameters just accept type of that class. Like
Modal Class
Public class modal
{
Public int[] Ids {get;set;}
}
Your Contoller
public IHttpActionResult GetAllProductsById([FromBody] modal m)
{
//Some logic with m.Ids
}
Your JSON if you are using any UI to consume this API
{"Ids" :[1,2,3,4,5,6]}
Hope this Helps,
I have an AngularJS app sending an object array to a Web API controller method through a POST request. On the server I have the following method definition:
[HttpPost]
[Route("Active")]
public async Task<IHttpActionResult> Post(IList<Media> mediaFiles)
{
try
{
foreach (var file in mediaFiles)
{
await Task.Factory.StartNew(() => PublicData.SetInactiveMedia(file.Id, file.Active));
}
return Ok();
}
catch (ArgumentException e)
{
return BadRequest(e.Message);
}
}
I use the $resource factory in order to interact with the server (but I have to mention that I have also tried with $http and no difference showed up). This is the method:
var activeMedia = $resource('/api/adminhotspotsmedia/active', { save: { method: 'POST' } });
var setActiveMedia = function (mediaFiles) {
activeMedia.save(mediaFiles);
}
The mediaFiles variable is an array of objects that absolutely match the Media model on the server.
In the Developer's Console I can see this Request Payload associated with the request:
So the array is trying to get to the server, but it cannot. The server gets a null value for the list of objects. I will add that I have tried using:
[FromBody] tag
dynamic instead of IList<Media>
IList<object> instead of IList<Media>
The problem persists. What could it be?
By default the model binder wont know what to do with the interface unless you have a custom model binder that knows what to expect and what the desired behavior is.
Alternatively you can use an actual List<Media>
[HttpPost]
[Route("Active")]
public async Task<IHttpActionResult> Post(List<Media> mediaFiles) { ... }
or array Media[]
[HttpPost]
[Route("Active")]
public async Task<IHttpActionResult> Post(Media[] mediaFiles) { ... }
I looked up up the documentation for ASP.NET Web API parameter binding, they seem to have either fromURI and fromBody only. Is it possible to do both?
Here is some background info. I'm creating a webhook receiver/handler, where I have control over which URL is the webhook, but I do not have control over what the payload will be like until later stage of the workflow, so I need to take it in as JSON string first.
My hope is to be able to set up the route that can take in querystring and also Json string payload from HTTP POST. For example .../api/incoming?source=A.
If I understand correctly you're trying to use both the Post data from the body and some parameters from the URI. The example below should capture your "source=a" value from the queryString.
[Route("incoming")]
[HttpPost]
public IHttpActionResult Test([FromBody] string data, string source)
{
//Do something
return Ok("my return value");
}
Or you could use as below if you formatted your route as .../api/incoming/source/A.
[Route("incoming/{source:string}")]
[HttpPost]
public IHttpActionResult Test([FromBody] string data, string source)
{
//Do something
return Ok("my return value");
}