I'm trying to perform a POST action to MVC controller like so:
string payload = "hello";
using (var httpClient = new HttpClient())
{
var str = new StringContent(new JavaScriptSerializer().Serialize(payload), Encoding.UTF8,
"application/json");
httpClient.BaseAddress = new Uri("http://localhost:52653/");
var response = httpClient.PostAsync("Home/TestPost", str).Result;
}
MVC Controller:
[HttpPost]
public ActionResult TestPost(string value)
{
var result = value;
return Content("hello");
}
When I debug I see the breakpoint hit in the controller but "value" param is null.
Is this even possible? can objects be sent this way as well? ie (Person)?
Try the FromBody attribute, that should work.
As #maccettura pointed out, you must include the proper namespace for this to work
using System.Web.Http;
[HttpPost]
public ActionResult TestPost([FromBody]string value)
{
var result = value;
return Content("hello");
}
Another method is this:
[HttpPost]
public async Task<string> TestPost(HttpRequestMessage request)
{
var requestString = await request.Content.ReadAsStringAsync();
return requestString;
}
You can use JsonConvert.SerializeObject() method.
var str = new StringContent(JsonConvert.SerializeObject(payload).ToString(), Encoding.UTF8,"application/json");
The serialized JSON string would look like:
{"payload":"hello"}
Also change
public ActionResult TestPost(string value)
TO
public ActionResult TestPost(string payload)
Related
Hi i am developing API's using Web API 2. I know how to add header when using HttpResponseMessage. Now I am using IHttpActionResult.
Below is my sample current code.
return Content(HttpStatusCode.OK, LoginResponse);
How can I add a header when I am returning content?
Whenever I use HttpResponseMessage I will be having request object and I can add header.
Below code I tried in HttpResponseMessage.
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response.Headers.AddCookies(new[] { cookie });
return response;
In this case where can I add header values?
You can continue to use the HttpResponseMessage as you are accustom to and update the header. After which you can use the IHttpActionResult ResponseMessage(HttpResponseMessage) method to convert to IHttpActionResult
Simple example
public class MyApiController : ApiController {
public IHttpActionResult MyExampleAction() {
var LoginResponse = new object();//Replace with your model
var cookie = new CookieHeaderValue("name", "value");//Replace with your cookie
//Create response as usual
var response = Request.CreateResponse(System.Net.HttpStatusCode.OK, LoginResponse);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
response.Headers.AddCookies(new[] { cookie });
//Use ResponseMessage to convert it to IHttpActionResult
return ResponseMessage(response);
}
}
You can create a custom IHttpActionResult which decorates a real one but exposes a way to manipulate the response:
public class CustomResult : IHttpActionResult
{
private readonly IHttpActionResult _decorated;
private readonly Action<HttpResponseMessage> _response;
public CustomResult(IHttpActionResult decorated, Action<HttpResponseMessage> response)
{
_decorated = decorated;
_response = response;
}
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
var response = await _decorated.ExecuteAsync(cancellationToken);
_response(response);
return response;
}
}
Then use this in your action:
return new CustomResult(Content(HttpStatusCode.OK, loginResponse), res => res.Headers.AddCookies(new []{ cookie}));
You can add header by using this code:
HttpContext.Current.Response.AppendHeader("Some-Header", value);
or this
response.Headers.Add("Some-Header", value);
I'm writing an API controller intended to receive and parse the contents of a JSON asynchronous post, and am unable to read the contents of the StringContent object in that post.
Here is the section from my API controller where I expect to see the value. The value arriving in the ApiController method is null. And the jsonContent value is an empty string. What I'm expecting to see is the contents of a JSON object.
public class ValuesController : ApiController
{
// POST api/values
public void Post([FromBody]string value)
{
HttpContent requestContent = Request.Content;
string jsonContent = requestContent.ReadAsStringAsync().Result;
// Also tried this per mybirthname's suggestion.
// But content ends up equaling 0 after this runs.
var content = Request.Content.ReadAsStreamAsync().Result.Seek(0, System.IO.SeekOrigin.Begin);
}
}
here is my controller to show how it's being called.
[HttpPost]
public ActionResult ClientJsonPoster(MyComplexObject myObject)
{
this.ResponseInfo = new ResponseInfoModel();
PostToAPI(myObject, "http://localhost:60146", "api/values").Wait();
return View(this.ResponseInfo);
}
And this is the posting method.
private async Task PostToAPI(object myObject, string endpointUri, string endpointDirectory)
{
string myObjectAsJSON = System.Web.Helpers.Json.Encode(myObject);
StringContent stringContent = new StringContent(myObjectAsJSON, Encoding.UTF8, "application/json");
using (HttpClient httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(endpointUri);
using (HttpResponseMessage responseMessage = await httpClient.PostAsJsonAsync(endpointDirectory, stringContent).ConfigureAwait(false))
{
// Do something
}
}
}
I suspect something is wrong with the signature of the Post method inside the ApiController. But don't know how that should be changed. Thanks for your help.
You are mixing async and sync calls which will lead to deadlocks.
Update controller to
[HttpPost]
public async Task<ActionResult> ClientJsonPoster(MyComplexObject myObject) {
this.ResponseInfo = new ResponseInfoModel();
await PostToAPI(myObject, "http://localhost:60146", "api/values");
return View(this.ResponseInfo);
}
Also [FromBody] is used to force Web API to read a simple type from the request body.
Update Api
public class ValuesController : ApiController {
// POST api/values
[HttpPost]
public async Task Post() {
var requestContent = Request.Content;
var jsonContent = await requestContent.ReadAsStringAsync();
}
}
I have an API I would like to call from my front end MVC site. These two applications run on separate servers on the same network.
The API Controller has functions similar to:
[AllowCrossSiteJson]
public class VerifyMyModelController : ApiController
{
[HttpPost]
public MyResponse Post(MyModel model)
{
return MyHelper.VerifyMyModel(model);
}
[HttpPost]
public async Task<MyResponse> PostAsync(MyModel model)
{
return await MyHelper.VerifyMyModel(model);
}
// ... Gets below as well
}
Where MyHelper performs model verfication, DB lookups etc... and returns a common response object with response code, database id etc
My front end MVC site has a form the user fills out, this data gets posted to the local controller which I would like to forward on to the API. The API application is not accessible to the public, so I cannot post directly to it using AJAX etc. It must come from the sites controller.
I have tried the following but get a 500 internal server error as a response
[HttpPost]
public async Task<MyResponse> VerifyAsync(MyModel model)
{
var MyServer = ConfigurationManager.AppSettings["MyServer"];
var json = Newtonsoft.Json.JsonConvert.SerializeObject(model);
var requestUri = string.Format(#"http://{0}/api/VerifyMyModel/", MyServer);
using (var c = new HttpClient())
{
var response = await c.PostAsJsonAsync(requestUri, json);
}
...
}
The var response contains the error message response 500.
I have also tried using a query string:
public string GetQueryString(object obj)
{
var properties = from p in obj.GetType().GetProperties()
where p.GetValue(obj, null) != null
select p.Name + "=" + HttpUtility.UrlEncode(p.GetValue(obj, null).ToString());
return String.Join("&", properties.ToArray());
}
[HttpPost]
public async Task<MyResponse> VerifyAsync(MyModel model)
{
var MyServer = ConfigurationManager.AppSettings["MyServer"];
string queryString = GetQueryString(model);
var requestUri = string.Format(#"http://{0}/api/VerifyMyModel/?{1}", MyServer, queryString);
using (var c = new HttpClient()){
var response = await c.GetAsync(requestUri); // API Also has GET methods
}
}
But the querystring method returns a 405 method not allowed response.
The MyModel is part of a shared class library with common models in it and is included in both applications.
Is there a better way of posting the entire model to the remote api action?
Thanks.
*Edit
RouteConfig of API:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
I added the following to the HomeController of the API's MVC site, to test it and I receive back the expected result, without error:
public async Task<ActionResult> TestVerifyMyModel(MyModel model)
{
var api = new VerifyMyModelController();
var res = await api.PostAsync(model);
return Json(res, JsonRequestBehavior.AllowGet);
}
So I know that the PostAsync Action of the controller works.. I just cant get it to work when called remotely.
I also enabled Failed Request Tracing on the server and have uploaded the generated XML file. It doesn't mean anything to me but thought it might help.
The posted route config looks more like your MVC route config than a Web Api one. But if it is the Web Api config, then shouldn't you be adding the ActionName to your url.
[HttpPost]
public async Task<MyResponse> VerifyAsync(MyModel model)
{
var MyServer = ConfigurationManager.AppSettings["MyServer"];
var json = Newtonsoft.Json.JsonConvert.SerializeObject(model);
var requestUri = string.Format(#"http://{0}/api/VerifyMyModel/PostAsync", MyServer);
using (var c = new HttpClient())
{
var response = await c.PostAsJsonAsync(requestUri, json);
}
...
}
Update: Sample code to retrieve Model from HttpClient response
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(ConfigurationManager.AppSettings["MyServer"]);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("api/VerifyMyModel/PostAsync");
if (response.IsSuccessStatusCode)
{
var myResponseModel = await response.Content.ReadAsAsync<MyResponseModel>();
}
}
In the process of learning about web API, I have created (or actually Visual Studio has) a simple controller. I have also created a WPF program that can read from web API's GET() part, but I can't access the others.
I have tried a lot and found many blogs and pages that say: "Just do like this..." but nothing works. What am I doing wrong?
MVC part:
namespace MVCWebservice.Controllers
{
public class LaLaController : ApiController
{
// GET: api/LaLa
public string Get()
{
return "Hello from API";
}
// GET: api/LaLa/5
public string Get(int id)
{
return "value";
}
// POST: api/LaLa
public void Post([FromBody]string value)
{
var a = value;
}
// PUT: api/LaLa/5
public void Put(int id, [FromBody]string value)
{
var b = value;
int c = id;
}
// DELETE: api/LaLa/5
public void Delete(int id)
{
int c = id;
}
}
}
And a method from my Console application that actually works:
private static async Task ReadFromWebApi()
{
System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
client.BaseAddress = new Uri("http://localhost:26176/");
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
var resp2 = await client.GetAsync("api/LaLa/");
resp2.EnsureSuccessStatusCode();
var aaa = resp2.Content;
string result = await aaa.ReadAsStringAsync();
Console.WriteLine(result);
}
A method that just stops:
If I remove the EnsureSuccessStatusCode I'll get the following back:
ss = "{\"Message\":\"The requested resource does not support http
method 'PUT'.\"}"
private static async Task SendToWebApi()
{
System.Net.Http.HttpClient client = new System.Net.Http.HttpClient();
client.BaseAddress = new Uri("http://localhost:26176/");
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
Console.WriteLine("-1-");
var resp2 = client.PutAsync("api/LaLa/", new System.Net.Http.StringContent("Hey", Encoding.UTF8, "application/json")).Result;
Console.WriteLine("-2-");
resp2.EnsureSuccessStatusCode();
var ss = await resp2.Content.ReadAsStringAsync();
}
How would I write my access to the other methods?
For one thing, you are calling PUT on the URL api/LaLa/, but from the server's method signature
public void Put(int id, [FromBody]string value)
it appears that the URL should include a numeric id to satisfy the first parameter, e.g. api/LaLa/100 or similar. Perhaps you could modify your client to call PutAsync() like this:
var resp2 = client.PutAsync("api/LaLa/100", new System.Net.Http.StringContent("Hey", Encoding.UTF8, "application/json")).Result;
Does POST work? Look at this answer to a question that looks remarkably like yours, and see if you can get that to work.
I have a situation where I have an MVC Controller that I'm calling a WebApi POST method. I'm passing a DTO as a parameter... The WebApi method is hit, but the parameter is null. What am I missing?
Thanks!
MVC Controller:
[Authorize]
public async Task<ActionResult> Index()
{
Permissions service = new Permissions();
ViewBag.Title = "Deployment Manager";
string uri = System.Web.Configuration.WebConfigurationManager.AppSettings["ApiRestfulUrl"] + "/Permissions";
using (var client = new HttpClient())
{
var permissions = new PermissionsDTO() { UserName = "rherhut" };
var response = await client.PostAsJsonAsync(uri, permissions);
if (response.IsSuccessStatusCode)
{
// Get response data how??
}
}
return View();
}
WebAPI POST Method:
public HttpResponseMessage Post(HttpRequestMessage request, [FromBody]PermissionsDTO permissions)
{
var data = repository.HasAdminRights(permissions.UserName); // permissions.UserName is null
var response = new ApiResponseCreator<PermissionsDTO>();
return response.FormatReturnData(request, data);
}
I resolved the problem by decorating the dto class with a "Serialize" attribute and for some reason this worked.