Using a Dictionary as input parameter on Attribute Routing - c#

I have the following RESTful Action on an MVC-project:
[Route("save")]
[HttpPut]
public ActionResult UpdateAllSettings(Dictionary<string,object> values){}
Currently I am stuck sending values to that Action. I tried using the following JSON-Structure:
fancyData="'values':[{'key':'k1','value':'v1'}]";
$http({url:'myurl',data: fancyData}
but values is always null.
I also tried replacing Dictionary<string,object> by List<KeyValuePair<string,object>> with the same results

You have to tell the action to look for data in the request body:
public ActionResult UpdateAllSettings([FromBody]Dictionary<string,object> values){}

In this case it would be better that you decorate the values parameter with the FromBody attribute. This gives you the benefit that when creating the PUT request you can add the JSON-content as content to the request.
httpClient.PutAsync("URL", new StringContent("YOUR JSON HERE",
Encoding.UTF8,
"application/json"));
To have better reusability just derive from StringContent and create a JSONContent-class:
public sealed class JsonContent : StringContent
{
public JsonContent(object content)
: base(JsonConvert.SerializeObject(content))
{
}
public JsonContent(object content, Encoding encoding)
: base(JsonConvert.SerializeObject(content), encoding, "application/json")
{
}
}
Note I use Json.NET as JSON library
now you can modify it to:
httpClient.PutAsync("URL", new JsonContent(yourDictionaryHere,
Encoding.UTF8,
"application/json"));

Related

Bad request 400 returns from API Patch method

I'm using .net core web api. in my API controller class have PATCH method as follows,
[HttpPatch("updateMessageTemplate/{templateId}")]
public IActionResult UpdateMessageTemplate([FromHeader] int tenantId, int templateId,[FromBody] testClass msg)
{
try
{
//Some implementation is here
return Accepted();
}
catch
{
return StatusCode(500);
}
}
testClass as follows,
public class testClass
{
public string body { get; set; }
}
I called the API from postman and its returns 400 BadRequest.
I placed the breakpoint in Controller method, but its not hitting. after I removed the [FromBody] testClass msg from the method parameter breakpoin hit without return 400 . why its returns 400 when I use [FromBody] testClass msg ? And how can I call this controller method from the HTTP Client ?.
I tried this, its also returns 400 BadRequest
string serviceUrl = string.Format("{0}/notification/updateMessageTemplate/{1}", ConfigurationManager.AppSettings["LtApiUrl"], templateID);
string json = "[{\"body\":\"sample text\"}]";
HttpClient client = new HttpClient();
HttpMethod method = new HttpMethod("PATCH");
HttpRequestMessage message = new HttpRequestMessage(method, serviceUrl);
StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
client.DefaultRequestHeaders.Add("tenantId", tenantId.ToString());
client.DefaultRequestHeaders.Add("Authorization", string.Format("bearer {0}", token));
message.Content = content;
var response = client.SendAsync(message).Result;
return response.StatusCode.ToString();
How can I solve this? please help me. I deleted the previous question and this is my real problem
Updated:
I changed the postman request as.
after that its works. but when I call it through http client code its provides 400 BadRequest. how to provide JSON body correct way through http client
For you use FromBody,you need to send request with json instead of form data.You could change your postman like below:
1.change the ContentType to application/json:
2.change the Body to raw and choose the style JSON:
UPDATE:
You need change your json like below:
string json = "{\"body\":\"sample text\"}";
Could you please try this.
[HttpPatch("updateMessageTemplate/{templateId}")]
public IActionResult UpdateMessageTemplate([FromHeader] int tenantId, int templateId,
[FromBody] JsonPatchDocument<testMsg> msg)
{
try
{
//Some implementation is here
return Accepted();
}
catch
{
return StatusCode(500);
}
}

How to fetch body data from IHttpActionResult in MVC Controller?

I have ApiController which receives a specific object of a class. That works perfect but what if, HTTP Request which contains a body with JSON is not matching with the object of a class? I will receive a null value of object because there is not a match between JSON and object of a class. My question is, how to get original JSON request when a user sends JSON with an incorrect format?
public class Document{
string name;
int number;
}
JSON REQUEST
{
"name":"Default name",
"number":91526861713"
}
JSON IS INCORRECT BECAUSE DATA TYPE OF number is int, not string "234" !
Automatically documentObject in function is equal to null.
How to get original JSON REQUEST?
[HttpPost]
public IHttpActionResult Receiving([FromBody]Document documentObject)
{
}
you can use Request.Content , But output is raw string.
like this:
[HttpPost]
public async Task<IHttpActionResult> Receiving([FromBody]Document documentObject)
{
var content = await Request.Content.ReadAsStringAsync();
return Json(content); // output => "name=xxx&number=123"
}

IHttpActionResult with JSON string

I have a method that originally returned an HttpResponseMessage and I'd like to convert this to return IHttpActionResult.
My problem is the current code is using JSON.Net to serialize a complex generic tree structure, which it does well using a custom JsonConverter I wrote (the code is working fine).
Here's what it returns:
string json = NodeToJson(personNode);
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(json, Encoding.UTF8, "application/json");
return response;
The NodeToJson method is where the custom converter comes into play ...
private static string NodeToJson(Node<Person> personNode) {
var settings = new JsonSerializerSettings {
Converters = new List<JsonConverter> { new OrgChartConverter() },
Formatting = Formatting.Indented
};
return JsonConvert.SerializeObject(personNode, settings);
}
Note this returns a string, formatted as JSON.
If I switch this to IHttpActionResult, it seems to fail regardless of what I try. I can just leave it (it works) but I am supposed to be using best practices for this and IHttpActionResult seems to be what I should be using.
I have tried to return Json(json); but this results in invalid, unparsable JSON, presumably because it's trying to do a double conversion?
return Ok(json); results in the JSON string being wrapped in XML.
What is the right way to do this?
EDIT:
I have successfully converted every method in this project to use IHttpActionResult now except this particular method.
It's a serialization of a generic tree to JSON. Regardless of what approach I try, I get back invalid JSON. The HttpResponseMsessage approach works fine, but I can not get valid JSON back with IHttpActionResult.
You can create your own IHttpActionResult class instance to return the JSON and a method in your controller or base controller class to utilize it.
Create the IHttpActionResult instance that sets the content and status code:
public class JsonTextActionResult : IHttpActionResult
{
public HttpRequestMessage Request { get; }
public string JsonText { get; }
public JsonTextActionResult(HttpRequestMessage request, string jsonText)
{
Request = request;
JsonText = jsonText;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(Execute());
}
public HttpResponseMessage Execute()
{
var response = this.Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(JsonText, Encoding.UTF8, "application/json");
return response;
}
}
Add a method to your controller to create the result. Here is a Web API example:
public class MyApiController : ApiController
{
protected internal virtual JsonTextActionResult JsonText(string jsonText)
{
return new JsonTextActionResult(Request, jsonText);
}
[HttpGet]
public IHttpActionResult GetJson()
{
string json = GetSomeJsonText();
return JsonText(json);
}
}
Another recommendation is as below;
var json = JToken.FromObject(yourObject);
return Ok(json);
I've got the same problem and this piece of code worked for me (Using Newtonsoft.Json nuget package to deserialize the json):
var unserializedContent = JsonConvert.DeserializeObject(json);
return Json(unserializedContent);
It seems we must have an object in order to Json() work as it should.
Some of the solutions here are converting string to JSON, that's not necessary.
You are just using computer resources for nothing.
// Instead of
// return Ok(jsonstring);
// do:
HttpResponseMessage response = this.Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(jsonstring, Encoding.UTF8, "application/json");
Request.RegisterForDispose(response); //To avoid the Pragma CA2000 warning
return ResponseMessage(response);
Another solution At client side
You can make a small change to be prepared to receive a string and convert it if necessary. The code bellow is Javascript
var data;
if (typeof weapiresponse == "string")
data = JSON.parse(weapiresponse);
else
data = weapiresponse;
If you have no intention of using XML as a return type, you can also remove the XmlFormatter in your WebApiConfig:
config.Formatters.Remove(config.Formatters.XmlFormatter);
The correct way is to return:
Ok(json);
It's converting the result to XML because that's the default accepted return type. Try adding:
Accept: application/json into your API request headers, I think that should resolve the issue.
I had the same problem with web-service returning JSON string in a XML-tag. I tried all the simple solutions Like :
return Json(text) , json deserialize and adding config.Formatter for json, but that did't help. I got double cotes around the json object or it was malformed.
Only the solution written by TGRA worked for me.
create your own IHttpActionResult class instance to return the JSON
For me, the only way to return an IHttpActionResult with the string content as Json in the following.
[HttpGet]
public IHttpActionResult ReturnStringAsJson()
{
return this.ResponseMessage(new HttpResponseMessage
{
Content = new StringContent("[json string]"),
Encoding.UTF8,
"application/json"),
});
}

How to serialise Web API 2 HttpRequestMessage into my custom object

I have the following method in my WebAPI 2 project.
public class TestController : ApiController
{
[HttpPost]
public void Test(HttpRequestMessage request)
{
var content = request.Content;
string jsonContent = content.ReadAsStringAsync().Result;
}
}
My custom object looks like this;
public class Test
{
public int Id { get; set; }
public string Name { get; set; }
}
If I post some sample data like
<Test>
<Id>12345</Id>
<Name>My Name</Name>
</Test>
The resulting value in jsonContent is correct. My question is how best should I serialise the HttpRequestMessage (content) into my object Test so that I can perform additional validation / tasks etc.
Should I pass HttpRequestMessage into the method or is it possible to pass something like
public void Test(Test oTest)
It sounds like you're asking how to deserialize jsonContent into a new instance of your Test class, as the first comment mentions above. I would suggest looking into Json.NET. Then you could do something like:
public class TestController : ApiController
{
[HttpPost]
public void Test(HttpRequestMessage request)
{
var content = request.Content;
string jsonContent = content.ReadAsStringAsync().Result;
Test test = new Test();
test = JsonConvert.DeserializeObject<Test>(jsonContent);
//Do validation stuff...
}
}
You can use a parameter in your action method like this.
[HttpPost]
public void Test(Test oTest)
ASP.NET Web API will deserialize the request message body (JSON or XML) into Test. Based on the content type header in the request, web API can handle both JSON and XML content out-of-box. In the case of XML, Web API uses DCS, by default. The XML you have shown in your post will not get deserialized as-is. Try returning a Test object and see how it gets serialized by web API and use the same XML in your POST request for binding to work correctly.
BTW, if you use the Test parameter in your action method, Web API will consume the request body stream. So, you will not be able to read it inside the action method like what you are doing.

Web api not supporting POST method

In my web api controller i have a function with following codes
[HttpPost]
public HttpResponseMessage Post(string schooltypeName)
{
_schoolTypeService.RegisterSchoolType(schooltypeName);
var message = Request.CreateResponse(HttpStatusCode.Created);
return message;
}
When i am calling with fiddler i am getting this error
{"Message":"The requested resource does not support http method 'POST'."}
my fiddling parameters are
Header
User-Agent: Fiddler
Host: myhost:8823
Content-Type: application/json; charset=utf-8
Content-Length: 26
Request body
{"schooltypeName":"Aided"}
Requesting url are
http://myhost:8823/SchoolType
( i configured url ,GET is working with this url)
Whats wrong here ?
Change your action to be like Post([FromBody]string schooltypeName) as by default string type is expected to come Uri.
Updated:
Change your body to just "Aided" as currently you would need a class to make the deserialiation work otherwise (ex:class School { public string SchoolTypeName { get; set; } }
See the using namespace at the top of the controller, if you're using System.Web.Mvc, then this problem might be occurred:
Use this:
using System.Web.Http;
The Problem comes down to this:
if your routes in startup is registered with routes.MapRoute(
you must decorate your post methods with [System.Web.Mvc.HttpPost]
If your routes in startup is registered with Routes.MapHttpRoute(
you must decorate your post methods with [System.Web.Http.HttpPost]
if you use MapRoute() with [System.Web.Http.HttpPost] it wont work
if you use MapHttpRoute() with [System.Web.Mvc.HttpPost] it wont work
For future readers. I found this question..but found (my) answer elsewhere.
I decorated the method with the attribute seen below.
[System.Web.Http.HttpPost]
public MyOutputObject DoSomething([FromBody]MyInputObject args)
{
Console.Writeline(args.ToString());
return new MyOutputObject();
}
My client code (C#, console app) for completeness. Please note it is NOT an good example of async/await since it has .Result in it.
private static Task<MyOutputObject> MyClientCall(string baseAddress, MyInputObject args)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(baseAddress);
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
/* Your class name would be "MyEntityController" most likely */
string serviceUrl = baseAddress + #"api/MyEntity/DoSomething";
HttpResponseMessage response = client.PostAsJsonAsync(serviceUrl, args).Result;
Console.WriteLine(response);
Console.WriteLine(response.Content.ReadAsStringAsync().Result);
if (!response.IsSuccessStatusCode)
{
Console.WriteLine("ERROR: :( " + response.ReasonPhrase);
return null;
}
Task<MyOutputObject> wrap = response.Content.ReadAsAsync<MyOutputObject>();
return wrap;
}
I found my answer here:
http://blog.dontpaniclabs.com/post/2013/01/23/That-Pesky-Requested-Resource-Does-Not-Support-HTTP-Method-POST-Error-When-Using-MVC-Web-API
Please Check your GET Action method name you are using.
Chances are you might be using the same Route names to GET method and POST method and expecting the result.
Example :
Controller name : StudentController
[Route("api/Student/names")]
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "student1", "student2" };
}
Url for method (GET): http://localhost:59342/api/student/names
[HttpPost]
[Route("api/Student/names")]
public String Post(String name)
{
return "success";
}
For POST method to url : http://localhost:59342/api/student/names
Then you will get the above mentioned error
Solution: Change the POST action method name like below
[HttpPost]
[Route("api/Student/getName")]
public String Post(String name)
{
return "success";
}
Then url which is used to get the response for post method is :
http://localhost:59342/api/student/getName
What helped to me at the end was adding the Route attribute, and just repeating there the same route which as registered globally.
If you add attribute [RoutePrefix("samplePrefix")] for controller class, but not add attribute [Route("sampleRoute")] for specified post method, this issue may occurs.

Categories