I am working on exposing some REST-based services via ASP.NET MVC 3. These services will be hit via JQuery as well as a Windows Phone Silverligh app. I know how to interact with a typical service. For instance, I currently have ones like the followng:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult AddComment(string username, string comment)
{
// Do stuff
return Json(new { message = "Success" });
}
I want to expose a REST-based service that allows users to upload a file. The trick here is that I also need to pass some data along with each file. However, I'm not sure how to do that. Every example I find only has just a file. But I'm not sure of
How to accept additional data
What to pass from JQuery.
Everything else I passed is just strings. However, in this I seem to have data serialized in binary format because of the file, and some string text. Because of that, I'm not sure what to do. Am I making sense?
The signature for the action should just be:
public ActionResult MyAction(string username, string comment, HttpPostedFileBase file1)
{ ... }
MVC binding should examine the request and match the form submission to the action based on the parameter names and types.
The clientside form must have enctype = "multipart/form-data" with method POST.
JQuery would just post the form with $("#form").submit().
Related
I am creating a web service for user authentication. (I am new in c# using entity framework core)
[HttpPost, Route("login")]
public async Task<ActionResult<Usuario>> Login([FromBody] User user)
{
}
my model User has the next fields:
{
name,
password,
phone,
email
}
I don't know how to specify so that from the client side, my users can see the fields that my web service needs to receive and when I use some plugin to document my API it can be clearly seen that I ONLY need the name to be sent and password only.
I think of something like that, I hope to make myself understand:
public async Task<ActionResult<Usuario>> Login([FromBody] string email, [FromBody] string password)
so in this way when the API is documented, I would like it to be understood that you need to send email and password only
and so from the client side
{"password": "212346", "email": "myemail#hotmail.com" }
is sent
Your view model should contain ONLY the fields each API method requires. If there are fields in your request that are not required, they should not be in the method body. If you use something like Swagger to document your API, then it will show just the fields required for each method.
Generally, I hear questions like this when the developer tries to use a DTO or even a database entity as a view model (both of which are incorrect uses).
I make sure each API method has a different view model (even if the contents are identical), because most of the time, eventually they will be different, but not always at the start.
I am working on a multi-level ASP.NET MVC web platform and I have the requirement to send a file from a System.Web.Mvc.Controller to an ASP.NET MVC web-service's System.Web.Http.ApiController, running remotely on a different machine.
Currently, I have this in my Mvc.Controller:
public ActionResult ForwardThisFile(HttpPostedFileBase file)
{
// TODO: Forward file to remote DocumentApi:
DocumentApi.DocumentApiClient client = new DocumentApi.DocumentApiClient();
client.StoreDocument(file /* <-- How-To? */);
return View();
}
where the DocumentApiClient has been generated via Visual Studio's Add REST API Client... function from a Swagger Url. (The generated client internally uses the Microsoft.Rest.ClientRuntime which is relying on System.Net.Http.HttpClient, HttpRequestMessage, etc.)
The question is, how do I define and implement the DocumentApi to transfer files to it in an efficient and generic way in the ApiController. Currently, I am thinking of three different options:
[HttpPut] public async Task<IHttpActionResult> StoreDocument(HttpPostedFileBase file)
[HttpPut] public async Task<IHttpActionResult> StoreDocument(byte[] fileContents, string contentType, string fileName)
[HttpPut] public async Task<IHttpActionResult> StoreDocument(Stream fileStream, string contentType, string fileName)
I was thinking, that maybe I could just forward the HttpPostedFileBase instance from the Mvc.Controller to the ApiController, so I've tried the first option. However, Add REST API Client... creates a DocumentApi.Models.HttpPostedFileBase model class in that case and is NOT of the original type System.Web.HttpPostedFileBase. Now, I'm not sure what I should do...
new DocumentApi.Models.HttpPostedFileBase() {
InputStream = file.InputStream,
ContentType = file.ContentType,
ContentLength = file.ContentLength,
FileName = file.FileName,
}
^ that doesn't work, because for the InputStream, it creates a DocumentApi.Models.Stream class and I have no idea how to convert the System.IO.Stream from the file parameter into a DocumentApi.Models.Stream.
Is it even possible to send a stream across ASP.NET MVC webservices? (Which would basically be the answer to option 3)
In my current state of knowledge, the only alternative appears to me being option 2, where I would send a byte-array containing the whole file. I am asking myself, however, if there is any more convenient or more efficient way to send a file from a Mvc.Controller to a remote ApiController. Is there?
Which one of the options would work and which one is the way to go?
Additionally, I have a bonus question to the above regarding HttpPostedFileBase: Is the way via HttpPostedFileBase the most efficient way to handle uploaded files in ASP.NET MVC? I have seen various alternatives:
HttpPostedFileBase like shown above in the first code sample
Using the raw HTTP-request via Request.Content.ReadAsStreamAsync() or something similar. Maybe the stream is more efficient for my use case?
Using JavaScript to encode a file into a Base64 string and send it via a hidden input field to the controller. (via FileReader's method readAsDataURL(file))
So many options... Which one is best/most generic/most efficient?
You should use the HttpClient lib in the calling controller to async upload the file to the destination controller. Various examples of using HttpClient to asycn upload is here C# HttpClient 4.5 multipart/form-data upload.
In this case your receiving remote controller just acts as though its having a file uploaded from a browser with no specific handling required.
Since a few days I'm trying to create my own web api controller. Duo to the rest conventions I need to use a post request to create an object. To get concrete, Im having this controller with this action:
public class ReservationController : ApiController
{
[HttpPost]
public void Create(int roomId, DateTime arrivalDate)
{
//do something with both parameters
}
}
This code is not working when I fire a post request at it, I'm receiving a 404 exception something like this:
No action was found on the controller 'Some' that matches the request.
The reason for it is that simple types are read from the query string, complex types from the body, according to this aricle. The web api uses the parameters to match the action to a request and can't therefore map my action to the request.
I do know that I can use the [frombody] tag, but you can only apply that to one parameter and I have 2. I also know that I can create a wrapper object which have both the parameters, but I'm not willing to use wrappers for all my calls.
So I do know that I can work around this by these methods. I also think that this is caused by the fact that the body of the post request can only be read once. But my actual question is:
Why is the source of a parameter determined by it's type and not by it's availability, especially when the conventions state that you should make for example a post request for creation? In MVC this is the case, why isn't it in the web api?
Best regards,
BHD
FINAL UPDATE
Since I'm getting some upvotes, problably more people are facing the same question. In the end it comes to this: Web-Api != MVC. It's simply not the same thing and the web api team made different design decisions than the mvc team I guess.
It seems that you have a fundamental misunderstanding of how Web API actually works.
Web API routing is driven off of verbiage, not the method names. "SomeMethod" actually translates to zero useful information for Web API. As a result, if I post
api/some/some?id=1
OR
api/some/somemethod?id=1
OR EVEN
api/some/?id=1
and the SomeMethod endpoint is the ONLY available POST, it will hit that endpoint.
As such, first of all, make sure you have only one POST on that api controller. If you do, POSTing to it from any test client using either of the query strings above will work just fine.
You can use the [FromBody] attribute on the parameter to force it to read from the body of the HTTP POST instead of the Uri. This is opposed to the [FromUri] attribute which does the opposite.
[HttpPost]
public void SomeAction([FromBody] int id)
{
//do something with id
}
Are you sure you're actually putting the id in the body? It could also be a routing issue. If this still doesn't work then maybe you should use Fiddler and copy the RAW output of your HTTP message here.
If you're packing multiple values into the body such as with JSON then you should use a model which should automatically be deserialized to:
public class PostModel
{
public int ID { get; set; }
public int SomeOtherID { get; set; }
}
[HttpPost]
public void SomeAction(PostModel postModel)
{
//do something with postModel.ID and postModel.SomeOtherID
}
You can actually do this straight out of the box in WebAPI, at least in 2.2 (.Net version 4.5.2). Your controller is correct. Using your controller, if you call it with a HTTP POST like this (tested through Fiddler):
http://localhost:58397/api/Reservation?roomId=123&arrivalDate=2015-12-17
You'll get the correct values of roomId = 123 and arrivalDate = 17.12.2015.
I suspect there's something wrong in your call to the WebAPI. Maybe post that call if you're still not getting it to work.
I am testing a push notification on a calendar application. When I create an event on calendar application, my website gets a HttpPost request with a JSON string. I wrote code like this but I couldn't receive the JSON string in my action method.
[HttpPost]
public ActionResult Push(String jsonReq)
{
Console.write(jsonReq);
return View();
}
When I create model in the same structure as JSON, then I can receive the request. it seems to be tightly coupled to JSON structure ? I am using in ASP.Net MVC 4.
[HttpPost]
public ActionResult Push(JSONModel jsonModel)
{
return View();
}
ASP.NET MVC model binding works the following way - it parses the request, tries to find a name-to-name corresponding between its parameters and Action paramaters, and if found instantiates latter. You are not sending parameter with name jsonReq, so you cannot receive something in your action method.
If you really want to work with plan json string without letting ASP.NET MVC parse it for your, you have two options:
Access it via HttpContext.Request inside the action
Write custom model binder that will map the request body to the jsonReq parameter
The request would not have a value named jsonReq so would not know to map the json to that action parameter.
Where as your JSONModel will have property names that match the JSON named values coming into the request thus the object us populated.
I have been struggling on trying to work this for the last 3 days, how do you pass data from an api controller into an mvc controller and use the data to populate a selectlistitem.
I have seen plenty of examples of calling the api from the webpage which is all well and good, but what if the user has javascript disabled, it will not display the data.
So any help with an example for this would be much appreciated.
My code is:
web.api
public IEnumerable<DisplayCurrencyInDDL> GetCurrencyForDDL()
{
var s = _ICurr.InsetCurrencyIntoDataBase();
return s.AsEnumerable();
}
mvc controller
WebClient wc = new WebClient();
var s = wc.DownloadString("http://localhost:50687/api/Currency");
How do I get the value from var s (currency and currencyid) into a selectlistitem.
Thanks
George
edit data returned as: [ { "strCountry": "Afghan Afghani", "strCountryCode": "AFN" }, { "strCountry": "Albanian Lek", "strCountryCode": "ALL" }, { "strCountry": "Algerian Dinar", "strCountryCode": "DZD" }, { "strCountry": "Andorra Euro1",
I don't understand why you are doing it this way.
If you want to share some code you can do this by moving the code into
some Library and instantiate that class in WebAPI and also in your MVC
Controller.
Ok, so after reading this post on stackoverflow difference between apiController and controller
Its my understanding that if i'm returning data to my own website, then use mvc controller, but if i'm allowing a 3rd party to consume data from my site, then put the data in an api controller.
Also if a user visited my site/your site and had javascript disabled, then json would not work on the client side as requires jQuery etc, so my understanding is use api if you are sure the visitor will not have javascript disabled.
Please let me know if that correct