How do you read POST data in an ASP.Net MVC 3 Web API 2.1 controller? - asp.net-mvc-3

This does not seem to be as easy as I thought. I found some solutions on the web, but they are not working for me. I have an ASP.Net MVC 3 project with the Microsoft ASP.Net Web API 2.1 nuget package installed. Now, I want to be able to read data posted to a web api controller. The data sent will vary, so I cannot used a strongly typed ViewModel.
Here are the solutions I tried:
public void Post([FromBody]string value)
{
...
}
public void Post([FromBody]List<string> values)
{
...
}
public void Post([FromBody]NameValueCollection values)
{
...
}
But my value or values variables are always empty. I know the controller is receiving data however because I can check it by accessing (System.Web.HttpContextWrapper)Request.Properties["MS_HttpContext"].Request.Form. It does not look like the proper way to retrieve the data though. There ought to be a cleaner way.
UPDATE:
Here is how I am posting the information:
I am posting the data from another controller in the same web application:
public ActionResult SendEmailUsingService()
{
dynamic email = new ExpandoObject();
email.ViewName = "EmailTest";
email.From = "fromaddress#yahoo.com";
email.To = "toaddress#gmail.com";
email.Fullname = "John Smith";
email.Url = "www.mysite.com";
IDictionary<string, object> data = email;
using (var wb = new WebClient())
{
string url = BaseUrlNoTrailingSlash + Url.RouteUrl("DefaultApi", new { httproute = "", controller = "Emailer" });
var response = wb.UploadValues(url, "POST", data.ToNameValueCollection());
}
return View();
}
And here is what I am getting in my Post web api controller if I declare an httpContext variable like this:
var httpContext = (System.Web.HttpContextWrapper)Request.Properties["MS_HttpContext"];
httpContext.Request.Form =
{ViewName=EmailTest&From=fromaddress%40yahoo.com&To=toaddress%40gmail.com&Fullname=John+Smith&Url=www.mysite.com}
httpContext.Request.Form is a System.Collections.Specialized.NameValueCollection {System.Web.HttpValueCollection}

I finally found the answer to my question here:
Web API Form Data Collection
The solution is to use FormDataCollection:
public void Post([FromBody]FormDataCollection formData)
{
...
}

Related

Making a Web API method accept an xml

I am new to Web API so if this question sounds trivial or stupid please still give a solution.
I have a requirement where the consumer of my web api wants to post an xml.
I created an empty Web API controller and added a method like so:
public class StudentController : ApiController
{
[HttpPost]
public Student GetStudent(Student studentXml)
{
Student student = new Student() { Id = 502, Course = "Chemistry" };
return student;
}
}
The param is of type Student which is the class that has the same properties as the tags in the xml.
The XML that will be posted is like so:
<StudentStatus>
<AccountID>1231</AccountID>
<Password>2132132</Password>
<Batch/>
<Sequence/>
<Test>0</Test>
<Date>09/25/2014 09:18:24</Date>
<TestNumber>1972</TestNumber>
<StatusCode>3</StatusCode>
<ID>502</ID>
</StudentStatus>
The method is suppose to return an XML in response.
That's easy with the
Accept: application/xml
But how do I make the method accept an XML?
Any help is greatly appreciated.
Regards.
Try this in the controller code...Remove Get Prefix, no need to add [HttpPost]
Put a breakpint to see if it hits there.
public void PostStudent(string studentXml)
{
//Write your logic here
}
Try this in the code from where you are posting data
using (var client = new WebClient())
{
var data = "XML data" //Try HTML.Encode if it does not work
var result = client.UploadString("http://localhost:52996/api/Student", "POST", data);
//Do something with result
}

Web API Routing changes after DB Refresh

This is an exceedingly strange problem and I'm only including it as an oddity in case someone's seen it before.
I am on C#, ASP.Net web api, Fluent Nhibernate and SQL Server 2012
I have a Meal object that I pass to a web api controller method :
[HttpPost]
[ActionName("PostMeal")]
public HttpResponseMessage PostMeal(Meal mealToPost)
{
var helper = new Datahelper();
try
{
var fshelper = new FoodServiceHelper(helper);
fshelper.SaveMeal(mealToPost);
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (Exception ex)
{
var response = Request.CreateErrorResponse(HttpStatusCode.NotAcceptable, ex);
return response;
}
}
The method that posts is similarly very simple:
public void SaveMeal(Meal mealToPost)
{
var url = _connectionString + "food/PostMeal";
var client = new HttpClient();
var formatter = new JsonMediaTypeFormatter
{
SerializerSettings = { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }
};
var clientTask = client.PostAsync(url, mealToPost, formatter)
.ContinueWith(posttask => posttask.Result.EnsureSuccessStatusCode());
clientTask.wait();
}
The meal object is pretty straightforward. Some elementaries, some generic collections.
I am able to post normally for several (usually about 20) meals, at which point it is somehow no longer able to find the route. I verified this by adding breakpoints to the server side. It hits it the first several times, then it won't even find the controller method.
Here's the weird part. If I drop and recreate the database, it's ok again for another 20 or so meals.
I suspect that this is some kind of mapping issue, but I cannot prove it.
Curious.

Post Binary array to Web API Controller

I am trying to POST form data which consists of few string variable and binary array.
Below is the Model for the form data.
public class FileModel
{
public string Path { get; set; }
public byte[] File { get; set; }
}
Below is my Web API Controller.
[Route("")]
public IHttpActionResult Post([FromBody]FileModel media)
{
// Can I use ??
byte[] requestFile = media.File;
string requestFilePath = media.Path;
//Process the above variables
return Ok();
}
I would like to know Can I use the following code to de-serialize the following code snippet to to read the values from the JSON payload including the binary data?
byte[] requestFile = media.File;
string requestFilePath = media.Path;
If Yes, Do I need to define any formatter class to get it working?
I normally use POSTMAN to test my RESTful endpoints.
Is it possible to use POSTMAN still to POST binary array? May be not need to write my own client
You'll need to use a serializer to serialize complex objects (multiple fields) as content for a Http Request.
For your code snippet to read the object from the content you can use this:
var requestContent = Request.Content.ReadAsAsync<FileModel>(GetJsonSerializer()).Result;
Here's the serializer boilerplate code.
private JsonMediaTypeFormatter GetJsonSerializer()
{
JsonSerializerSettings settings = new JsonSerializerSettings()
{
PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.All,
TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All
};
return new JsonMediaTypeFormatter() { SerializerSettings = settings };
}
I'm not sure how to use POSTMAN to test this. A simple .net client would be:
var Client = new HttpClient();
Client.BaseAddress = new Uri("localhost"); //whatever your endpoint is
FileModel objectToSend = new FileModel();
var objectContent = new ObjectContent<FileModel>(objectToSend, GetJsonSerializer() );
var response = Client.PostAsync("uri", objectContent);
You are able to use POSTMAN to test binary file input. Selecting the body tab, you can then pick the radio button "binary" and then choose file.

Instantiate new System.Web.Http.OData.Query.ODataQueryOptions in nunit test of ASP.NET Web API controller

I have an ASP.NET MVC4 Web API project with an ApiController-inheriting controller that accepts an ODataQueryOptions parameter as one of its inputs.
I am using NUnit and Moq to test the project, which allow me to setup canned responses from the relevant repository methods used by the ApiController. This works, as in:
[TestFixture]
public class ProjectControllerTests
{
[Test]
public async Task GetById()
{
var repo = new Mock<IManagementQuery>();
repo.Setup(a => a.GetProjectById(2)).Returns(Task.FromResult<Project>(new Project()
{
ProjectID = 2, ProjectName = "Test project", ProjectClient = 3
}));
var controller = new ProjectController(repo.Object);
var response = await controller.Get(2);
Assert.AreEqual(response.id, 2);
Assert.AreEqual(response.name, "Test project");
Assert.AreEqual(response.clientId, 3);
}
}
The challenge I have is that, to use this pattern, I need to pass in the relevant querystring parameters to the controller as well as the repository (this was actually my intent). However, in the case of ODataQueryOptions-accepting ApiController methods, even in the cases where I would like to use just the default parameters for ODataQueryOptions, I need to know how to instantiate one. This gets tricky:
ODataQueryOptions does not implement an interface, so I can't mock it directly.
The constructor requires an implementation of System.Web.Http.OData.ODataQueryContext, which requires an implementation of something implementing Microsoft.Data.Edm.IEdmModel, for which the documentation is scarce and Visual Studio 2012 Find References and View Call Hierarchy do not provide insight (what implements that interface?).
What do I need to do/Is there a better way of doing this?
Thanks.
Looks like someone else already answered this in the comments here, but it's not a complete solution for my use-case (see comment below):
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Customer>("Customers");
var opts = new ODataQueryOptions<Customer>(new ODataQueryContext(modelBuilder.GetEdmModel(),typeof(Customer)), request);
This is the solution I have been using in my NUnit tests to inject ODataQueryOptions
private static IEdmModel _model;
private static IEdmModel Model
{
get
{
if (_model == null)
{
var builder = new ODataConventionModelBuilder();
var baseType = typeof(MyDbContext);
var sets = baseType.GetProperties().Where(c => c.PropertyType.IsGenericType && c.PropertyType.GetGenericTypeDefinition() == typeof(IDbSet<>));
var entitySetMethod = builder.GetType().GetMethod("EntitySet");
foreach (var set in sets)
{
var genericMethod = entitySetMethod.MakeGenericMethod(set.PropertyType.GetGenericArguments());
genericMethod.Invoke(builder, new object[] { set.Name });
}
_model = builder.GetEdmModel();
}
return _model;
}
}
public static ODataQueryOptions<T> QueryOptions<T>(string query = null)
{
query = query ?? "";
var url = "http://localhost/Test?" + query;
var request = new HttpRequestMessage(HttpMethod.Get, url);
return new ODataQueryOptions<T>(new ODataQueryContext(Model, typeof(T)), request);
}

Visiting an other URL at the server by code and extracting some data from there

I am working in Asp.net MVC and have a peculiar requirement for which I have no idea.
What I want is that when a user request a particular URL from my site, I want to visit some preset URL in the database and extract some data and bind them to View before rendering.
For example, If you visit mysite.com/Search/Index, then in my action method Index, i want to visit the anothersite.com/someparticular/url, extract the value in <div> with id="searclbl", bind it to my view and render the page.
I need to read the HTML because the sites I am working with don't offer any Web services or RSS.
Any sort of help or guidance in this matter is appreciated.
I believe you might be able to pull this off using HtmlAgilityPack (which can be installed via a NuGet package inside your project).
For example:
Let’s assume your Index View of the SearchController is strongly typed to the following ViewModel:
public class SearchViewModel
{
public string DivElement { get; set; }
//other properties...
}
This is the Index ActionResult():
public ActionResult Index()
{
var model = new SearchViewModel();
model.DivElement = GetDivFromWebSite();
return View(model);
}
The GetDivFromWebSite() method is where I use HtmlAgilityPack to fetch information from another web site and is defined like so:
private string GetDivFromWebSite()
{
var baseUrl = new Uri("http://www.anotherdomaine.com");
HtmlAgilityPack.HtmlDocument document = new HtmlDocument();
using (WebClient client = new WebClient())
{
document.Load(client.OpenRead(baseUrl));
}
if (document == null) return "nothing found!";
var div = document.DocumentNode.SelectNodes("//div[#id='missing-category']").FirstOrDefault();
return div.InnerHtml;
}
This might do the trick!

Resources