How to POST a DateTime value to a Web API 2 controller - asp.net

I have an example controller:
[RoutePrefix("api/Example")]
public class ExampleController : ApiController
{
[Route("Foo")]
[HttpGet]
public string Foo([FromUri] string startDate)
{
return "This is working";
}
[Route("Bar")]
[HttpPost]
public string Bar([FromBody] DateTime startDate)
{
return "This is not working";
}
}
When I issue a GET request to: http://localhost:53456/api/Example/Foo?startDate=2016-01-01 it works.
When I POST to http://localhost:53456/api/Example/Bar I receive a HTTP/1.1 400 Bad Request error.
This is my POST data:
{
"startDate":"2016-01-01T00:00:00.0000000-00:00"
}
What am I doing wrong?

You can't post non-objects directly, you need to wrap them in side an object container when using FromBody.
[RoutePrefix("api/Example")]
public class ExampleController : ApiController
{
[Route("Foo")]
[HttpGet]
public string Foo([FromUri] string startDate)
{
return "This is working";
}
[Route("Bar")]
[HttpPost]
public string Bar([FromBody] BarData data)
{
return "This is not working";
}
}
public class BarData{
public DateTime startDate {get;set;}
}
The other way it could work is if you form-encode the value like this using the = symbol (note you are sending it as a non-object, the curly braces have been removed).
"=2016-01-01T00:00:00.0000000-00:00"

Try just POSTing:
{
"2016-01-01T00:00:00.0000000-00:00"
}
Specifying the property name would mean your endpoint would need to accept an object with a property named startDate. In this case you only want to pass a DateTime.

The submitted format of the date is important, and depends on your client library. It must look like this (quotes in the raw body payload):
"2015-05-02T00:00:00"
No braces, no property names. The format that's transmitted from your code and/or client library is going to depend on whether you're sending a javascript date, or a string representation of one. So, tweak the submissions code appropriately...

Related

Model is getting bound but not the individual parameters from json request body in ASP.NET Core Web API

Using postman, am trying to post "city" and "country" data.
URL: http://localhost:8080/api/Sample/SendData
RequestBody: {"city": "abc","country": "xyz"}
Headers: Content-Type: application/json
But am unable to retrieve the data instead getting nulls as show below. By encapsulating the properties (city, country) into a model, I am able to see the data.
Below is the code am using
[AllowAnonymous]
[ApiController]
public class SampleController : ControllerBase
{
private readonly ILogger<SampleController> _logger;
public SampleController(ILogger<SampleController> logger)
{
_logger = logger;
}
[HttpPost]
[Route("api/Sample/SendData")]
public ActionResult SendData(string city, string country)
{
try
{
if (ModelState.IsValid)
{
//return
return Ok("Success");
}
else
{
throw new Exception("error");
}
}
catch (Exception ex)
{
//return
return BadRequest(Convert.ToString(ex));
}
}
}
Note: I want to send the data using request body and not through query string as the data am going to pass eventually be larger.
It won't work with individual parameters. When you're using datatype json, the asp.net controller will always assume it's an object you're sending.
If you want individual parameters you could remove the json request header and replace it with text/plain and change your request body to key-value.
There's a similar question here but you would need to install newtonsoftjson nuget package. You'll only get 1 string though, which is the whole json file, then you'll be deserializing it to an object when it gets to the controller.
POST Json without model and Ajax

Input date to webapi

I am using webapi2. I have a property in model is start date whose datatype is datetime. I want to pass the date as "dd-mm-yyyy" format. But if i send, i am getting 400 bad request. Could you please help me out. Note, I am using Fluent validation for the model validation.
public class Item
{
public DateTime? StartDate { get; set; }
public string Id { get; set; }
}
I want to pass the date as "dd-mm-yyyy"`
You have 3 options.
Option ISO8601
Don't pass it as "dd-mm-yyyy". Pass it instead in ISO8601 format (yyyy-MM-dd). That is the correct way to serialize DateTimes to string and also for then communicating that string representation between tiers. This format is a standard, widely used, unambiguous, and almost all frameworks that I am aware of have built in mechanisms for outputting DateTimes to that format and parsing them from that format.
Displaying a DateTime formatted as "dd-mm-yyyy" is a presentation layer concern and it should stay there and not "bleed" into the other application layers.
Option Formatters
Use custom code, like a Json Converte or an ActionFilterAttribute, to read the incoming DateTime.
Option String
Accept a string parameter instead and handle your own parsing inside the controller's method.
I honestly do not recommend the last 2 options. Instead use ISO8601: a standard, unambiguous, widely accepted means of communicating a DateTime.
I have created a custom value provider factory and am using the default model binding.
public class OrderValueProviderFactory<T> : ValueProviderFactory where T : class
{
public override IValueProvider GetValueProvider(HttpActionContext actionContext)
{
var querystring = actionContext.Request.GetQueryNameValuePairs().ToDictionary(x => x.Key.ToLower(), x => x.Value);
return new OrderValueProvider<T>(querystring);
}
}
public class OrderValueProvider<T> : IValueProvider
{
readonly Dictionary<string, string> querystring;
public OrderValueProvider(Dictionary<string, string> _querystring)
{
querystring = _querystring;
}
public bool ContainsPrefix(string prefix)
{
return true;
}
public ValueProviderResult GetValue(string key)
{
T obj = (T)Activator.CreateInstance(typeof(T));
PropertyInfo[] properties = typeof(T).GetProperties();
foreach (var property in properties)
{
if (property.PropertyType == typeof(string))
{
property.SetValue(obj, querystring.GetStringValue(property.Name.ToLower()));
}
else if (property.PropertyType == typeof(DateTime?))
{
property.SetValue(obj, querystring.GetDateTimeValue(property.Name.ToLower()));
}
else if (property.PropertyType == typeof(int))
{
property.SetValue(obj, querystring.GetIntValue(property.Name.ToLower()));
}
}
return new ValueProviderResult(obj, "", CultureInfo.InvariantCulture);
}
}

ValueProvider never being called

I'm working with MVC 4 Web API and I have this dummy ValueProvider:
DummyValueProvider.cs
class DummyValueProvider : IValueProvider
{
public DummyValueProvider()
{
}
public bool ContainsPrefix(string prefix)
{
return true;
}
public ValueProviderResult GetValue(string key)
{
return new ValueProviderResult("testing", "testing", System.Globalization.CultureInfo.InvariantCulture);
}
}
class DummyValueProviderFactory : System.Web.Http.ValueProviders.ValueProviderFactory
{
public override IValueProvider GetValueProvider(System.Web.Http.Controllers.HttpActionContext actionContext)
{
return new DummyValueProvider();
}
}
This ValueProvider should return true for any key asked, so it will always supply a value to the model binder when it needs. The ValueProvider is registered in the WebApiConfig like this:
WebApiConfig.cs
config.Services.Add(typeof(ValueProviderFactory), new DummyValueProviderFactory());
The code compiles and runs fine.
I also have this action in the Account API controller:
AccountController.cs
public HttpResponseMessage Register(string foo) { ... }
The action gets called fine when I call it like below:
/register?foo=bar
And foo is filled with bar as expected; but if I call:
/register
The server returns 404 with the message No HTTP resource was found that matches the request URI 'http://localhost:14459/register'.
Also, I put breakpoints inside methods ContainsPrefix() and GetValue(), but they never get triggered.
What am I doing wrong? Shouldn't DummyValueProvider be providing the value testing to parameter foo?
Try this
public HttpResponseMessage Get([ValueProvider(typeof(DummyValueProviderFactory))] string foo) {... }
I higly suggest you to read this recent article to customize Web Api Binding.
Update:
After reading the article the OP was able to discover the solution. It was that using the parameter attribute [ModelBinder] was required for it to work. This was because unless the parameter is annotated, [FromUri] is assumed. Once annotated with [ModelBinder] the registered handlers are executed.

Return literal JSON strings in spring mvc #ResponseBody

I am storing objects in my database as JSON strings. I want to make a REST service that exposes these strings. When I write my methods however, the strings I get back have their quotes escaped. For example, I have included a method that returns a String,
#RequestMapping(value = "test", method = RequestMethod.GET)
public #ResponseBody
String getTest() {
return "{\"a\":1, \"b\":\"foo\"}";
}
but when I call this method in the browser I get a back "{\"a\":1, \"b\":\"foo\"}" when what I really want to happen is {"a": 1, "b": "foo"}. I think "String" as the return type is likely the problem, but what else can I do? A wrapper class does the same thing:
{
"value" : "{\"a\":1, \"b\":\"foo\"}"
}
I could serialize it and then return the object, but that seems a bit ridiculous.
Here is a possibly the relevant portion of my configuration file:
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
super.configureMessageConverters(converters);
converters.add(mappingJacksonHttpMessageConverter());
}
#Bean
MappingJacksonHttpMessageConverter mappingJacksonHttpMessageConverter() {
MappingJacksonHttpMessageConverter mappingJacksonHttpMessageConverter = new MappingJacksonHttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
mappingJacksonHttpMessageConverter.setObjectMapper(objectMapper);
mappingJacksonHttpMessageConverter.setPrettyPrint(true);
return mappingJacksonHttpMessageConverter;
}
Thanks
EDIT: as was suggested below, it seems the string is being double encoded. Commenting out the 2 classes in my configuration fixes this issue. However, I still have other places where I want to return Objects and would like to keep those running through that common serializing bean that I know where to configure. So I see my options as:
a) Do all the serializing myself. All methods return Strings, and those that are already JSON return themselves, and those that are objects all return JSONUtil.toJson(object). I don't love this approach, but I know it will work.
b) Use a wrapper class that looks kind of like:
public static class Wrapper {
#JsonRawValue
private final String value;
}
This leads to an awkward "value" at the front though that has no real meaning.
Basically what I want is #JsonRawValue, but to have it work on RequestMapping methods instead of properties.
Thoughts? Opinions? Other suggestions?
This works with Jackson 2 (at least):
#Controller
public class YourController {
#RequestMapping(..)
public #ResponseBody Json get() {
return new Json("{ \"attr\" : \"value\" }");
}
}
class Json {
private final String value;
public Json(String value) {
this.value = value;
}
#JsonValue
#JsonRawValue
public String value() {
return value;
}
}
Not particularly pretty but works. I only wish Spring supported this:
#RequestMapping(..)
public #JsonRawValue #ResponseBody String get() {
// ...
}
I guess what you want is producing a response with content-type application/json. In your case, when you have the json-data as a raw string, do the following:
In your controller add produces="application/json" to your #RequestMapping attribute:
#RequestMapping(value = "test", method = RequestMethod.GET, produces="application/json")
public #ResponseBody
String getTest() {
return "{\"a\":1, \"b\":\"foo\"}";
}
Then you have to configure the StringHttpMessageConverter to accept the application/json media-type.
With Java-config:
#Override
public void configureMessageConverters(
List<HttpMessageConverter<?>> converters) {
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(
Charset.forName("UTF-8"));
stringConverter.setSupportedMediaTypes(Arrays.asList( //
MediaType.TEXT_PLAIN, //
MediaType.TEXT_HTML, //
MediaType.APPLICATION_JSON));
converters.add(stringConverter);
}
With XML-config:
<bean class = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<array>
<bean class = "org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json; charset=UTF-8" />
</bean>
</array>
</property>
</bean>
I used this:
#RequestMapping(..)
#ResponseBody
public JsonNode myGetRequest(){
...
//rawJsonString is the raw Json that we want to proxy back to the client
return objectMapper.readTree(rawJsonString);
}
And the Jackson converter knew how to transform the JsonNode into plain Json.
If you want to convert JSON String to JSON object in your browser, keep string convertor before Jackson convertor.
Follow this link for complete example. It works with custom converter configuration plus spring validation.
It Works
converters.add(stringConverter());
converters.add(mappingJackson2HttpMessageConverter());
super.configureMessageConverters(converters);
It Doesn't
converters.add(mappingJackson2HttpMessageConverter());
converters.add(stringConverter());
super.configureMessageConverters(converters);
In my case, I wanted the response type to be determined by a request parameter, so had to specify the content type in the code, e.g.:
#RequestMapping("/myurl")
public void radiusSearch(#RequestParam responseType, HttpServletResponse response) throws IOException {
String jsonResponse = makeSomeJson();
response.setContentType(responseType);
try {
response.getOutputStream().write(jsonResponse.getBytes());
} finally {
response.getOutputStream().close();
}
}
Today we had the same issue and solved it with multiple converters. Now every String will treated as a string and every other Object will get serialised by Jackson. This allows to serialise manually (by returning String) or automatically (by returning something else) in Spring controllers.
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(stringConverter());
converters.add(mappingJackson2HttpMessageConverter());
super.configureMessageConverters(converters);
}
#Bean
public StringHttpMessageConverter stringConverter() {
final StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(UTF_8);
stringConverter.setSupportedMediaTypes(Arrays.asList(
MediaType.TEXT_PLAIN,
MediaType.TEXT_HTML,
MediaType.APPLICATION_JSON));
return stringConverter;
}
#Bean
public GenericHttpMessageConverter<Object> mappingJackson2HttpMessageConverter() {
final ObjectMapper objectMapper = objectMapperBuilder().build();
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(objectMapper);
return converter;
}
The \" means the character " is being escaped, which is standard. If it's being printed like that, you're probably double-serializing the object.
I know this is an old question, but I was just dealing with the opposite problem myself (I was returning a String and WANTED it to get converted to JSON). In your case, it sounds like you simply want to have your String treated as a plain string and not have any sort of JSON conversion done on it as you already have JSON.
So in your case you don't want to use the MappingJacksonHttpMessageConverter (or the MappingJackson2HttpMessageConverter if you're now using Jackson2). You want no conversions done at all, and that converter converts Java objects to/from JSON. So instead you should just use the plain StringHttpMessageConverter. You can do that by changing your setup method like this:
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new StringHttpMessageConverter());
}
This converter is applicable to */* types (the document is incorrect which says text/*, I found out the hard way in the debugger). So whether your content type is application/json or not, either way Spring won't mess with your Strings if you use this converter.
The solution to your problem is, this works perfectly without changing any configurations
import com.fasterxml.jackson.databind.JsonNode;
import com.github.fge.jackson.JsonLoader;
JsonNode getTest() {
return JsonLoader.fromString("{\"a\":1, \"b\":\"foo\"}");
}

Can we have DataContractSerializer, XMLFormatSerializer both in same service(ServiceContract) for different methods(OperationContract)?

Code:
public interface IServices
{
[OperationContract]
[XmlSerializerFormat]
GetProductsResponse Getproducts(GetProductsRequest productsrequest);
[OperationContract]
SaveProductsResponse SaveProducts1(SaveProductsRequest1 productsrequest);
}
[DataContract]
public class SaveProductsRequest1
{
[DataMember]
public List<Person> Persons;
}
[DataContract]
public class Person
{
[DataMember]
public int Id;
}
Client :
ServicesClient client = new ServicesClient();
SaveProductsRequest1 req = new SaveProductsRequest1();
req.Persons = new List<Person> { new Person { Id = 10 } }.ToArray();
client.SaveProducts1(req);
I am invoking the SaveProducts1 call from client side and not able to get the value '10' in my service side(seems deserialization issue). But when I remove [XmlSerializerFormat] attribute from Getproducts call, it just works fine and I am able to see the value 10.
Why is it happening(Why SaveProducts1 depends on Getproducts OperationContract)? What workaround I should provide, when I want to use both xml and datacontract serialization? Any help appreciated.
Note: I have very updated proxy. I am not seeing any issue in proxy. Even I tried with one sample and getting the same issue
Did you refresh your client service reference after adding the XmlSerializerFormat attribute? It could be that the contracts don't match any longer.

Resources