Using WebClient do send an array to an ApiController via query string I get the error 400.
The api method looks like
public IHttpActionResult List([FromUri] Model model)
In the Model class I have
public int[] Ids { get; set; }
In the client side the code looks like:
webClient.QueryString.Add("ids", "1");
webClient.QueryString.Add("ids", "2");
...
await webClient.DownloadStringTaskAsync(url);
If I send just one "ids" parameter the code works fine, but not with two or more.
I found that the client creates the url like "url?ids=1,2" instead "url?ids=1&ids=2".
Is there some configuration I missed?
WebClient will automatically turn multiple values with the same key into a comma separated string. You can change this behavior, see: How to build WebClient querystring with duplicate keys?
I would recommend using HttpClient instead of WebClient though.
Related
In PHP, one can access request property by using the $_REQUEST 'superglobal' variable.
In Java Servlet, one can also do something similar by calling getParameter(string) or getParameterValues(string) on the incoming HttpServletRequest instance.
Both of these method do not care if the data is conveyed on the query string or on the body. They are HTTP-method-agnostic-ways of getting request properties.
How to do the same using ASP.NET 4.x (not Core) Web API 2?
As far as possible, I do not want to use model binding or route parameter. I just want to use built in Properties of the ApiController to access the request parameter directly.
Here's what I'm trying to do:
public class MyController : ApiController
{
[HttpPost]
public IHttpActionResult Index()
{
// somehow obtain 'requestParam ' either from query string OR from request body
var requestParam = Request.???
return Ok(requestParam);
}
}
I would like to filter a list of vehicles, by their makeId using httpGet. The URL I would expect to use is:
https://localhost:5001/api/vehicle?makeId=2
Below, I will define the DTO and controller methods I used for this task:
FilterDto
public class FilterDTO
{
public int? MakeId { get; set; }
}
Below are the 2 HTTPGet methods in my controller class. I expect the first method to be called.
[HttpGet]
public async Task<IEnumerable<VehicleDTO>> Get(FilterDTO filterDto)
{
var filter = _mapper.Map<Filter>(filterDto);
var vehicles = await _vehicleRepository.GetAll(filter);
return _mapper.Map<IEnumerable<VehicleDTO>>(vehicles);
}
[HttpGet("{id}")]
public async Task<ActionResult<VehicleDTO>> Get(long id)
{
var vehicle = await _vehicleRepository.GetWithRelated(id);
if (vehicle == default)
{
return BadRequest("Vehicle not found");
}
var result = _mapper.Map<VehicleDTO>(vehicle);
return Ok(result);
}
With the above code, when I call the URL above, in Postman I get a 400 Error, saying "The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0."
I get the same result for https://localhost:5001/api/vehicle
If I change the first Get method like below, I am able to get a response:
[HttpGet]
public async Task<IEnumerable<VehicleDTO>> Get(int? makeId)
{
var filter = new Filter { MakeId = makeId};
var vehicles = await _vehicleRepository.GetAll(filter);
return _mapper.Map<IEnumerable<VehicleDTO>>(vehicles);
}
After this (lengthy) introduction, my questions are:
Why does HttpGet support 'int?' but not the data transfer object 'FilterDto'?
Should I be using a different verb instead of HttpGet?
I might have to filter in the future for some other types (say customerId). Is there any way I can change the method to support custom objects, like FilterDto, ideally without changing the verb?
Change your code as follow:
[HttpGet]
public async Task<IEnumerable<VehicleDTO>> Get([FromQuery] FilterDTO filterDto)
{
var filter = _mapper.Map<Filter>(filterDto);
var vehicles = await _vehicleRepository.GetAll(filter);
return _mapper.Map<IEnumerable<VehicleDTO>>(vehicles);
}
and call it like:
baseUrl/Controller/Get?MarkId=1
Take a look at the docs.
Basically the primitive types are supported, but the controller has no idea how to convert your web request data into C# object. You need to explicitly tell it how you want this custom object to be created out of web request.
You may have in mind that HttpGet methods are only able to receive primitiveTypes (string, int, short, datetime -using a specific format-) because the arguments are being sent through query string, for example:
myAddres.com/api/mymethod?id=5&filter1=value1&filter2=value2
Having this consideration in mind you'll notice there's no way to send any object because you need to use a json or another notation, remember querystring has a limit and because of that is better using "argument=value" notation.
On the other hand PUT and POST are able to send their data through a "body" property where you may use a json notation and this way you may create almost any object on your Backend side.
If you need to use an object as an argument it is a better idea using POST or PUT (better POST than PUT).
I am returning a string object from spring controller like
#RequestMapping(value = "/persons.html", method = RequestMethod.GET)
public #ResponseBody String listPersonHtml(Model model) {
return "{\"abc\":\"test\"}";
}
I am getting response on ui like "{\"abc\":\"test\"}",i want this response as
{"abc":"test"}
i.e pure json object.
what type of configuration I need?
On UI side,if I set Accept */* then I face this issue,if I set Accept text/html or Accept text/plain then no issue is there,but I can't change accept header.
I found the way.Its all about spring message-converters.I added MappingJackson2HttpMessageConverter in this list and this converter tries to convert string to json and produces this result.
Just add org.springframework.http.converter.StringHttpMessageConverter before MappingJackson2HttpMessageConverter so that StringHttpMessageConverter can come into action and string can be returned as it is.
Old question, but I just had to solve the same issue and most of the answers I found resulted misleading, so here's mine:
It all starts with the Controller, and Spring trying to answer a mapped request in the format that the invoking client is expecting. The client can inform this using different HTTP features, and there is where the different HttpMessageConverter implementations are involved. Spring pick's the format to answer based on different strategies, applied by the ContentNegotiationManager.
By prioritizing StringHttpMessageConverter over MappingJackson2XmlHttpMessageConverter you are only telling Spring to answer in "text/plain" format over "application/json", and it will work until a client specifies that is expecting a json response (this is mostly done by setting the Accept header in the request, although there are other ways to do it). The important thing is that if a client sets that header to "application/json", Spring will use MappingJackson2XmlHttpMessageConverter that will translate the Java String to a Json String, ending up with something like "{\"abc\":\"test\"}" instead of {"abc":"test"}
So, the real issue that every developer faces in this case is that MappingJackson2XmlHttpMessageConverter translates a Java String to a Json String, and in some cases, you might not need that, because the string contains valid json that needs to be returned without modifications. There are some configuration classes for this MessageConverter but I did'n went that road, because I need to return Strings like "raw" Json only in some specific endpoints (performance is the key driver). Here's an expample that resumes my "approach":
#RestController
#RequestMapping(value = "test", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class TestController {
#RequestMapping(method = RequestMethod.GET, value = "endpoint")
public JsonObject getSomeJson() {
return new JsonObject("{\"abc\":\"test\"}");
}
private static class JsonObject {
private String rawJsonValue;
JsonObject(String rawJsonValue) {
this.rawJsonValue = rawJsonValue;
}
#JsonValue #JsonRawValue
public String getRawJsonValue() {
return rawJsonValue;
}
}
}
#JsonValue and #JsonRawValue are Jackson annotations that tell MappingJackson2XmlHttpMessageConverter to treat the getRawJsonValue method result as the Json representation of JsonObject, without making any modification. The response of the endpoint will be {"abc":"test"}
I have a PUT method in web api which accepts a JSON data and a route data as follows.
[Route("api/v1/Orders/{orderId}/active")]
public HttpResponseMessage Put(Guid? orderId,List<ActiveRequest> activeRequests)
{
}
public class ActiveRequest
{
public int Id { get; set; }
public bool IsActive { get; set; }
}
Now is it possible to simplify the method signature as:
[Route("api/v1/Orders/{orderId}/active")]
public HttpResponseMessage Put(ActiveRequestModel model)
{
}
public class ActiveRequestModel
{
public Guid OrderId { get; set; }
public List<ActiveRequest> ActiveRequests {get; set;}
}
I tried writing a custom ModelBinder by implementing the System.Web.Http.ModelBinding.IModelBinder interface but could'nt find a way to read the JSON data that is coming inside the Request object.
I doubt that is there a way by which I can bind my model with data coming from three different places i.e. from route data, json & form.
You cannot simplify the parameter as described.
Unlike MVC model binding, beacuse of how the Web API formatter works, in Web API you only can have a single parameter that is deserialized from the payload, and a number of simple type parameters coming from route parameters or url query string. The reason is that the creation of the parameter coming from the payload is done in a single pass deserialization of the payload.
So, for your example you need the two parameters in your original version, i.e.:
public HttpResponseMessage Put(Guid? orderId, List<ActiveRequest> activeRequests)
If you want to use the ActiveRequestModel you need to include a payload which has exactly the same structure, so you should include the orderId in the payload, because it will not be recovered from the url (even if the name matches).
Please, read this article which explains how parameter binding works in Web API:
Parameter Binding in ASP.NET Web API
If you read it thoroughly you'll see that you can create and register your own model binder to make it work the same way that an MVC controller, but I think it's not worth the effort (so I include it only in this last paragraph), and it's not the standard way of working.
I have a Web API controller that returns data to my client. The code looks like this:
[HttpGet]
[ActionName("Retrieve")]
public IEnumerable<Reference> Retrieve(int subjectId)
{
return _referenceService.Retrieve(subjectId);
}
Can someone tell me is it necessary to specify the ActionName?
Also should I return an IEnumerable, an IList or something else?
I believe if your ASP.NET routing is setup correctly you don't need to specify the ActionName, for example:
protected void Application_Start()
{
RouteTable.Routes.MapHttpRoute("0", "{controller}/{action}/{arg1}");
}
Will match /YourControllerName/Retrieve/132
What you return is based entirely on your media-type formatters, of which the default is XmlFormatter and JsonFormatter. These can be found in GlobalConfiguration.Configuration.Formatters and will be chosen based on the Accept header provided by the client.
We, for example, use JSON.Net for our response formatting, configured by:
protected void Application_Start()
{
RouteTable.Routes.MapHttpRoute("0", "{controller}/{action}/{arg1}");
MediaTypeFormatterCollection formatters = GlobalConfiguration.Configuration.Formatters;
formatters.Remove(formatters.XmlFormatter);
var jsonFormatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;
jsonFormatter.Formatting = Formatting.Indented;
jsonFormatter.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
This tells WebApi to disallow any XML formatting and only return JSON using the provided JSON.Net contract resolver. JSON.Net supports serializing IEnumerable.
I would, however, recommend returning a HttpResponseMessage instead. This allows you to set the status code as well (This still uses the media type formatter, it's just a cleaner wrapper). You can use this like so:
[HttpGet]
public HttpResponseMessage Retrieve(int subjectId)
{
var response _referenceService.Retrieve(subjectId);
return Request.CreateResponse(HttpStatusCode.OK, response);
}
You should return HttpStatusCode instead of data if have not requirement, like POST method should return OK or whatever.
or if want record like Get method should return type of record.
also you no need to add attribute on method like Get,Put,Delete etc because webapi automatically detect method according to action like if you are getting data then your method name should be start with Get like GetEmployee etc.