Passing JSON parameter via GET request to MVC controller - c#

I'm trying to pass some JSON on the querystring of a GET request to an MVC controller, but can't seem to get it to come through as anything other than null.
Ajax (through TypeScript)
$.ajax(url, {
method: 'GET',
data: { 'request': JSON.stringify(this.request) },
dataType: 'json'
})
MVC Controller
[Route("stuffAndThings/{request?}")]
public async Task<HttpResponseMessage> GetStuff(requestType request)
{
}
Because this is TypeScript, the object being passed is the TypeScript representation of the C# model, including several custom objects
TS class
class requestType {
pageData: PageData;
}
C# class
public class requestType
{
public PageData pageData { get; set; } = new PageData();
}
Looking at the request in devtools, it appears to be being passed correctly on the querystring, but always comes through on the controller as null.
What am I missing?
EDIT
To address a couple of comments, the controller method is purely for data retrieval, and does have the potential in the future to be turned into a WebAPI method, so I would like to keep it as a GET request if possible.

In MVC controller you will get the parameter as string because you have passed parameters as string through GET request
[Route("stuffAndThings/{request?}")]
public async Task<HttpResponseMessage> GetStuff(string request)
{
}
Make requestType class serializable,
now in your method you have to deserialize the json string into your object
using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(request)))
{
DataContractJsonSerializer deserializer = new DataContractJsonSerializer(typeof(requestType));
requestType requestObj = (requestType)deserializer.ReadObject(ms);
//your code here
}

Json.Stringfy convert your request in string form, and in controller your are fetching with particular type. So, to get the proper result update with strin g instead of RequestType.
if still face issue you reach out to me.
I am here
Please response or like if its helpful.

Related

Can't bind json in POST request body to class argument in .net-core

I'm trying to make a post request from my Angular frontend to the .net Core 3.1 backend, but in the controller method the argument object only gets by default 0 and null values;
let response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(obj)
});
[ApiController]
[Route("[controller]")]
[ApiExplorerSettings(IgnoreApi = true)]
public class RequestController : ControllerBase
{
[HttpPost]
public async Task<IAResponseViewModel> PostAnswer([FromBody] IAResponseViewModel something)
{
var temp = something.ToString();
if (!ModelState.IsValid)
{
Console.WriteLine();
}
return something;
}
}
public class IAResponseViewModel
{
public string AdministrationId { get; }
public int RoundUsesItemId { get; }
public int ResponseOptionId { get; }
}
The JSON object I see being submitted
{AdministrationId: "12345678-00e8-4edb-898b-03ee7ff517bf", RoundUsesItemId: 527, ResponseOptionId: 41}
When inspecting the controller method the 3 values of the IAResponseViewModel are null or 0
When I change the argument to object I get something with a value of
ValueKind = Object : "{"AdministrationId":"12345678-00e8-4edb-898b-03ee7ff517bf","RoundUsesItemId":523,"ResponseOptionId":35}"
I've tried with and without the [FromBody] attribute, changing the casing of the properties of the controller method's argument and the frontend argument, copy pasting the viewmodel attributes on to the submitted object keys, wrapping the posted object in a 'something' object. the ModelState.IsValid attribute shows as true.
I've red other answers such as Asp.net core MVC post parameter always null &
https://github.com/dotnet/aspnetcore/issues/2202 and others but couldn't find an answer that helped.
Why isn't the model binding working and how do I populate the viewmodel class I'm using with the json data?
From a comment on my original question:
Could it be because the properties in IAResponseViewModel are 'get only'?
Indeed this was the problem. Giving the properties (default) set methods fixed the problem.

How to fix - The requested resource does not support http method 'POST'

Below is WebAPI action. On googling about the below error:-
The requested resource does not support http method 'POST'
I got number of links & updated my api accordingly but still I am getting the same error.
Web api not supporting POST method
ASP.NET Web Api: The requested resource does not support http method 'GET'
[AcceptVerbs("POST")]
[HttpPost]
[Route("rename/{userId}/{type}/{title}/")]
public IHttpActionResult Rename([FromBody] int userId, [FromBody] string type, [FromBody] string title)
{
//my api stuff
}
But still when calling the above via post man throws the error.
How do I get rid of this error??
Also is it possible to fix this without using [FromBody] attribute in the method parameters list?
Any help/suggestion highly appreciated.
Thanks.
You have declared route which requires url parameters
[Route("rename/{userId}/{type}/{title}/")]
So when you send request to api/customer/rename it does not match this method. You should remove parameters which you are passing in request body from route parameters
[Route("rename")]
Make sure that you have appropriate RoutePrefix("api/customer") attribute on your controller.
Second problem is multiple [FromBody] parameters. You will get can't bind multiple parameters error. There is limitation - you can mark only one parameter as FromBody. See Sending Simple Types notes:
Web API reads the request body at most once, so only one parameter of
an action can come from the request body. If you need to get multiple
values from the request body, define a complex type.
You should create complex type which will hold all parameters
public class RenameModel
{
public int UserId { get; set; }
public string Type { get; set; }
public string Title { get; set; }
}
And change method signature to
[HttpPost]
[Route("rename")]
public IHttpActionResult Rename(RenameModel model)
And send request data as application/x-www-form-urlencoded
[Route("rename/{userId}/{type}/{title}/")]
public IHttpActionResult Rename([FromBody] int userId, [FromBody] string type, [FromBody] string title)
The last answer is correct, you're asking for these parameters in the route, but saying that you expect them in the post body. Also, usually the route would begin with a noun rather than a verb. What is it you're renaming? (i.e. [Route("users/rename/{userId}/{type}/{title}")]
Based on your initial post, try this instead:
[HttpPost]
[Route("rename/{userId}/{type}/{title}" Name = "RenameUser"]
public IHttpActionResult Rename(int userId, string type, string title)
{
_myServiceMethod.Rename(userId, type, title);
return new StatusCodeResult(HttpStatusCode.Created, this);
}
Or, if you wanted to do a post with the info in the body:
Declare your data contract:
public class User
{
public string Type { get; set; }
public string Title { get; set; }
}
Then on the endpoint:
[HttpPost]
[Route("rename/{userId}", Name = "RenameUserPost")]
public IHttpActionResult RenameUserPost(int userId, [FromBody] User userData)
{
return new StatusCodeResult(HttpStatusCode.Created, this);
}
Note that in both returns 'this' refers to your controller class that inherits from ApiController. Verified both of these in swagger, and they accept POSTs and return status codes.
Hope this helps.
I had this error for wrong string in Route string on top of my action.
[Route("api/TestReaderPercentStudyHomework/AddOrUpdate")]

Passing multiple parameters in Ajax Call with Razor

So essentially, I'm building a web API, and I'm trying to use ajax to query a web service that accepts two arguments. I need to pass a List of Stings (SSNs) and I need to pass a single string to determine which environment it queries. ("a" for acceptance, and "i" for integration).
I'm using cshtml with razor. And so far this section is successfully giving me what I want from the divs.
var pInputSssns = document.GetElementById(“SSNinput”).value;
var pTestRegion = document.GetElementById(“testRegion’).value;
However, just beneath it. I'm trying to insert both these params into an ajax call.
$.ajax({
type: 'GET',
contentType: "application/json; charset=utf-8"
url: "myurl",
data:"{}",
success: function (data) {
// populate a table
}
});
I've heard multiple opinions on how that can be done. Can I serialize them both as JSON, and then put two JSON objects in the data parameter? I've also heard that I can pass a c-sharp object, but if I try and do that I can't access the html variable while I'm inside the Razor code.
#{ AjaxParam ap = new AjaxParam(); ap.pInputSsns = pInputSsns;}
Is there a good way to pass both of them in there? For reference, here's the Controller:
[ScriptMethod (ResponseFormat = Response Format.Json)]
[System.Web.Http.HttpGet]
public JArray GetSSNAssociations([FromUri] List <string> pInputSsns, string pTestRegion)
{
\\ do something
}
Appreciate it everyone.
First, create a DTO class for the data you want to send.
public class SearchRequest
{
public List<string> SsnList { set; get; }
public string Environment { set; get; }
}
Now in your WebApi controller's Post method will have this class as the parameter so that when the client sends a request, the default model binder will be able to map the request values to the properties of an object of this class.
public class ProductsController : ApiController
{
public HttpResponseMessage Post([FromBody]SearchRequest req)
{
// I am simply returning the same object back. But you can do whatever you want
// Check req.SsnList and req.Environment properties and do something useful.
return Request.CreateResponse(HttpStatusCode.OK, req);
}
}
Now from the client side code, You can build a javascript object which resemebles the structure of our DTO class and send it using jQuery ajax.
var model = { Environment: "I", SssList: ["375329567", "3583345"] };
var url="../api/products";
$.ajax({
type: "POST",
data: JSON.stringify(model),
url: url,
contentType: "application/json"
}).done(function (res) {
console.log('res', res);
// Do something with the result :)
});
Here is a post explaining the different options to send client side data to web api in detail.
I hardcoded the value of url variable. You should consider using the Html helper methods like Url.Action to generate the correct relative path to your app endpoint as explained in this post.

Unable to post simple string data to Web API from AngularJS

I am trying to get a json string from my angular app to a Web API. I have looked all over the internet the past 6 hours trying and failing miserably to figure out what I am doing wrong.
I have checked the network console and I can see the json as form data, but my WEB api for some reason is NOT getting it. I have looked at several other posts but none seem to help with my particular issue. Any direction would be great. I already tried using the "transform" fix, but that didn't help.
ENTRY POINTS TO WEB API
[HttpPost]
[Route("api/SkeltaInterfaceController/SaveWorkflow")]
public bool SaveWorkflow([FromBody] string json)
{
...
}
ANGULAR CALL
$scope.SaveWorkFlow = function () {
$http({
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
method: "POST",
url: webAPI,
data: {'DATA' : 'TEST DATA'
}
})
}
EDIT: I changed the angular call to this
$scope.SaveWorkFlow = function () {
$http({
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
method: "POST",
url: webAPI,
data: {'DATA' : 'TEST DATA'}
})
}
The Web API looks like this
[HttpPost]
[Route("api/SkeltaInterfaceController/SaveWorkflow")]
public bool SaveWorkflow([FromBody] TestModel json)
{
...
}
And the model
public class TestModel
{
public string DATA { get; set; }
}
I am still getting a null value for DATA though, something I setup wrong?
Though you have got an solution, there are some ways to POST simple string data (not an object) to a Web API service.
Let's say you have a POST API like this (in Test ApiController)
public void Post([FromBody]string value)
{
//do something with value
}
From AngularJS you can post to this method like
(1) data as JSON (default)
$scope.Test = function () {
$http({
method: "POST",
url: "/api/Test",
data: JSON.stringify("test")
});
};
This will use Content-Type: application/json by default. And server will treat the data as JSON. If you look at the request, you'll see the request body is a simple string, like
"test"
For complex objects, you'd see them JSON formatted.
(2) data as application/x-www-form-urlencoded (as in your example)
$scope.Test = function () {
$http({
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
method: "POST",
url: "/api/Test",
data: $.param({ "": "test" }),
});
};
Here, we are explicitly specifying the content type to be application/x-www-form-urlencoded, so we have to send data in that format (much like url query string). And, here, the empty key in data is just to satisfy Web API's strange model binding requirement! The resulting data will be encoded like
=test
which we'have done with $.param({ "": "test" }). One reason for this, FromBody is used mainly to send object, not simple primitive values.
So, the basic problem with your code was, you had specified Content Type : application/x-www-form-urlencoded and you were sending the data as JSON!
I think your issue comes down to the WebApi controller method expecting to receive a string, but you are passing it an object. Is it at least hitting the method but receiving null?
It depends what you are trying to send to the WebApi controller method, but if you mean to send an object, you should create a model that represents that object in your WebApi project and have the method take an object as the parameter.
For example, the way you have it right now you would do something like:
public class SaveModel
{
public string DATA {get; set;}
}
[HttpPost]
[Route("api/SkeltaInterfaceController/SaveWorkflow")]
public bool SaveWorkflow([FromBody] SaveModel model)
{ ...

How to implement Request / Response pattern with Web Api?

I'm new to MVC Web Api but I have some experience working with ServiceStack framework. Some of the web api examples look a lot like RPC with more action methods and less parameters. In fact most examples seem to limit the request parameter to an id. I would like to create some reporting services using request/response because the request gets quite complex with all the reporting criteria.
Here's my simplified request / response types:
public class PendingRequest
{
public string Id { get; set; }
public int AccountId { get; set; }
public DateTime? FromDate { get; set; }
public DateTime? ToDate { get; set; }
}
public class PendingResponse
{
public string Id { get; set; }
public IEnumerable<Pending> Data { get; set; }
}
And my outline reports controller:
public class ReportsController : ApiController
{
public async Task<PendingResponse> GetPending(PendingRequest request)
{
return new PendingResponse
{
Id = request.Id,
// Data = await Repo.GetPending()
};
}
public async Task<ShippedResponse> GetShipped(ShippedRequest request)
{
return new ShippedResponse
{
Id = request.Id,
// Data = await Repo.GetShipped()
};
}
public async Task<ProductsResponse> GetProducts(ProductsRequest request)
{
return new ProductsResponse
{
Id = request.Id,
// Data = await Repo.GetProducts()
};
}
}
And my routing and config for a self-hosting app:
class Program
{
static void Main()
{
var config = new HttpSelfHostConfiguration("http://localhost:8080");
config.Routes.MapHttpRoute(
name: "Reports",
routeTemplate: "api/reports/{action}",
defaults: new
{
controller = "Reports"
});
using (var server = new HttpSelfHostServer(config))
{
server.OpenAsync().Wait();
Console.WriteLine("Press Enter to quit.");
Console.ReadLine();
}
}
}
So with this routing I intend the action to be the name of the report. Note i've not included the request parameter here. Not sure if I should or not.
The problem is actually on the client side. With the HttpClient the GetAsync method does not allow me to include the JSON request object. I see an extension method PostAsJsonAsync but surely this shouldn't be a POST? Without a GetAsJsonAsync or similar then I can't see a way of including a request with a GET?
To be honest I prefer ServiceStack but there appears to be no async support yet. I need to develop some high performance services which are I/O intensive and I want to reduce the blocking as much as possible.
UPDATE:
It seems Web Api will perform the model binding if, rather than using the request body, I include the model's parameters as part of the query string. In order for this to work, the Get method needs to prefix the model type with the [FromUri] attribute, which informs the model binder that the model should be constructed from the request query string. Messy, but works.
[ActionName("Pending")]
public async Task<PendingResponse> GetPending([FromUri] PendingRequest request)
{
return new PendingResponse
{
Id = request.Id,
// query = ...build from request params
// Data = await Repo.GetPending(query)
};
}
And now on the client side I perform the following:
var result = await _httpClient.GetAsync("api/reports/pending?Id=123&AccountId=456");
result.EnsureSuccessStatusCode();
var response = await result.Content.ReadAsAsync<PendingResponse>();
A call to the service results in the GetPending method being called with a copy of the PendingRequest object materialised from the supplied query string parameters.
The fundamental problem here is you are trying to provide a request body to HTTP GET, which is not allowed. Well, you can still go ahead and format a GET with request body and submit it but that is not according to the spec. Strictly speaking, HTTP spec does not forbid GET requests from having a body but the response for a GET request must not change based on the request body, which basically means you cannot use search criteria in the GET request body. You can use the URI path and query string to specify the search criteria and if you want to bind them into a complex type parameter, you will need to use [FromUri] like this: public async Task<PendingResponse> GetPending([FromUri]PendingRequest request).
Bit of a guess, but what about just URL encoding the JSON request object and putting it in the query string segment of the GET URI?
Also, you might need [FromUri] attribute on your request method parameter in the action as it is a complex type. I may have missed something, but this article on parameter binding doesn't mention anything about GET being a special case for complex types, hence I think they will always try and read from the POST body.

Categories

Resources