Null parameter in web api call when using class containing System.Xml.Serialization attributes - asp.net-web-api

I have created a class from a schema using xsd.exe. This class contains System.Xml.Serialization attributes.
I have used this class as a parameter for a web api method. I need to serialise the parameter to xml so I can validate against schema and create a Oracle xmltype.
My web api method is as follows
[HttpPost]
public HttpResponseMessage Create([FromBody]MyClass obj)
I switched the default Serializer to XmlSerializer in webapi.config as follows
config.Formatters.XmlFormatter.UseXmlSerializer = true;
From the client using HttpWebRequest or WebClient I can successfully serialise (XmlSerializer) an instance of the class and post it to the web api using application/xml content type. So far so good.
However, if I try to send application/json content type the parameter object proerties at the web api is always null. The parameter itself is not null just the properties within.
I create the json content as follows
MyClass data = new MyClass();
// assign some values
string json = new JavaScriptSerializer().Serialize(data);
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(json);
The instance of the class serialises to JSON ok and contains values assigned, however, when I post the byte array, always null at web api.
I am sure it is something to do with the System.Xml.Serialization attributes in the class at the web api.
Does anyone have any suggestion on how to get around this?
Ade
Update
My class generated with xsd
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://Ade.interface")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://Ade.interface", IsNullable = false)]
public partial class MyClass
{
private string nameField;
/// <remarks/>
public string Name
{
get
{
return this.nameField;
}
set
{
this.nameField = value;
}
}
}
Web api
[HttpPost]
public HttpResponseMessage Create([FromBody]MyClass payload)
{
// payload.Name is null
}
Fiddler
POST http://myhostname/Create HTTP/1.1
Content-Type: application/json
Host: myhostname
Content-Length: 14
Expect: 100-continue
{"Name":"Ade"}
Client
string json = new JavaScriptSerializer().Serialize(data);
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(json);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://myhostname/Create");
request.Method = "POST";
request.ContentLength = bytes.Length;
request.ContentType = "application/json";
try
{
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(bytes, 0, bytes.Length);
}
// code removed
} catch (WebException we)
{
// code removed
}

This worked for me using version="4.0.20710.0" of Microsoft.AspNet.WebApi.Core
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
json.SerializerSettings.ContractResolver = new DefaultContractResolver()
{
IgnoreSerializableInterface = true,
IgnoreSerializableAttribute = true
};

Based on the repro, I noticed that Json formatter works fine if your request body was rather {"nameField":"Ade"}...
You can change this behavior by modifying the serialization settings on the contract resolver. After this change, you should be able to use {"Name":"Ade"}
Example:
JsonContractResolver resolver = (JsonContractResolver)config.Formatters.JsonFormatter.SerializerSettings.ContractResolver;
resolver.IgnoreSerializableAttribute = true; // default is 'false'

Related

Caching an HttpResult/memorystream using newer CacheControl attribute

I just discovered the new CacheControl Attribute and it's working well for standard POCOs - I was wondering if something different was required to cache a service that returned an HttpResult as a PDF. (The service is working but I don't see any records in my cache after the service is called).
[Authenticate]
[CacheResponse(Duration = CacheExpirySeconds.TwentyFourHours)]
public class AdvReportPDFService : Service
{
public object Get(AdvRptPitchPercentages request)
{
var ms = SomeFunctionThatReturnsAMemoryStream();
ms.Position = 0;
return new ServiceStack.HttpResult(ms, "application/pdf");
}
}
ServiceStack's Cache isn't able to cache the metadata in a HttpResult that's defined in your Service Implementation (when returning Cached Responses). Instead you should use the [AddHeader] Request Filter Attribute to specify the custom ContentType your Service returns and return the naked Stream instead, e.g:
[Authenticate]
[AddHeader(ContentType = "application/pdf")]
[CacheResponse(Duration = CacheExpirySeconds.TwentyFourHours)]
public class AdvReportPDFService : Service
{
public object Get(AdvRptPitchPercentages request)
{
var ms = SomeFunctionThatReturnsAMemoryStream();
return ms;
}
}

Posting to WebAPI with ModelClass Object as Parameter

We have web api like below:
[HttpPost]
public CustomAuthenticateModel AuthenticateByUsername(LoginModel model)
{
return employeeService.AuthenticateByUsername(model.Username, model.AdDomain, model.IsAdAuthentication);
}
In my PCL Project I am trying to access via:
try
{
HttpResponseMessage response = null;
LoginModel l = new LoginModel();
l.Username = model.Email;
response = await apiClient.PostAsJsonAsync(uri, l); // Exception is fired at this line
}
catch(exception etc){}
and every time I am getting exception like:
ex = {System.TypeInitializationException: The type initializer for 'System.Net.Http.FormattingUtilities' threw an exception. ---> System.NotImplementedException: The method or operation is not implemented.
at System.Runtime.Serialization.XsdDataContractExporte...
This is an existing project, all API consume Model Class object as parameter. what is the right way to do this? I am trying to use MVVM helper library for this project.
Serialize your object before making a request. Javascript Serializer should work as good as Newtonsoft serializer
var jsonRequest = Newtonsoft.Json.JsonConvert.SerializeObject(argument);
var content = new StringContent(jsonRequest, Encoding.UTF8, "text/json");
//url is the api, l is your object that you are passing
var response = await client.PostAsync(url, l);
if (response.IsSuccessStatusCode)
{
//R is your object type, in this case LoginModel
result = Newtonsoft.Json.JsonConvert.DeserializeObject<R>(await response.Content.ReadAsStringAsync());
}

How do I download a file from a Byte[] array in a Web Api2 method that returns IHttpActionResult?

Below is the method I've got in my ApiController class.
The _fileContents dictionary is populated in another WebApi2 method BuildContent(params).
When the user makes an ajax call to the BuildContent(params) method, the method builds
the string end of the dictionary, which contains full HTML content including a table tag and passes back a the Guid, end of the dictionary. The javascript in turn does the following:
window.location = 'api/MyController/DownloadFile/' + guid;
ApiController static:
private static Dictionary<Guid, String> _fileContents = new Dictionary<Guid, String>();
ApiController method:
public IHttpActionResult DownloadFile(Guid guid)
{
try
{
if (_fileContents.ContainsKey(guid))
{
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var content = Encoding.ASCII.GetBytes(_fileContents[guid]);
using (var stream = new MemoryStream(content))
{
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition =
new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "MyFile.bin";
}
return Ok(result);
}
else
{
return NotFound();
}
}
catch (Exception ex)
{
return InternalServerError(ex);
}
return BadRequest();
}
The calling sequence works perfectly but the DownloadFile() method throws the following exception:
<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.
</ExceptionMessage>
<ExceptionType>System.InvalidOperationException</ExceptionType>
<StackTrace/>
<InnerException>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Type 'System.Net.Http.StreamContent' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.
</ExceptionMessage>
<ExceptionType>
System.Runtime.Serialization.InvalidDataContractException
</ExceptionType>
Can anyone tell me what is going on and how to accomplish my goal of downloading a simple html file?

Post Binary array to Web API Controller

I am trying to POST form data which consists of few string variable and binary array.
Below is the Model for the form data.
public class FileModel
{
public string Path { get; set; }
public byte[] File { get; set; }
}
Below is my Web API Controller.
[Route("")]
public IHttpActionResult Post([FromBody]FileModel media)
{
// Can I use ??
byte[] requestFile = media.File;
string requestFilePath = media.Path;
//Process the above variables
return Ok();
}
I would like to know Can I use the following code to de-serialize the following code snippet to to read the values from the JSON payload including the binary data?
byte[] requestFile = media.File;
string requestFilePath = media.Path;
If Yes, Do I need to define any formatter class to get it working?
I normally use POSTMAN to test my RESTful endpoints.
Is it possible to use POSTMAN still to POST binary array? May be not need to write my own client
You'll need to use a serializer to serialize complex objects (multiple fields) as content for a Http Request.
For your code snippet to read the object from the content you can use this:
var requestContent = Request.Content.ReadAsAsync<FileModel>(GetJsonSerializer()).Result;
Here's the serializer boilerplate code.
private JsonMediaTypeFormatter GetJsonSerializer()
{
JsonSerializerSettings settings = new JsonSerializerSettings()
{
PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.All,
TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All
};
return new JsonMediaTypeFormatter() { SerializerSettings = settings };
}
I'm not sure how to use POSTMAN to test this. A simple .net client would be:
var Client = new HttpClient();
Client.BaseAddress = new Uri("localhost"); //whatever your endpoint is
FileModel objectToSend = new FileModel();
var objectContent = new ObjectContent<FileModel>(objectToSend, GetJsonSerializer() );
var response = Client.PostAsync("uri", objectContent);
You are able to use POSTMAN to test binary file input. Selecting the body tab, you can then pick the radio button "binary" and then choose file.

How to pass a complex type parameter (DTO object) to GET requests?

I have an n-tier application, whereas the core web service is built with Web API. many of the web service's methods are set as HTTPGET and accept a DTO object as parameter. my client app, built with MVC 5 is using HttpClient to call this API.
so it seems that by using client.PostAsJsonAsync() I can pass an object, whereas client.GetAsync() doesn't allow me to do that. this forces me to explicitly specify the properties of DTO in the URL, which works, but seem a bit redundant.
Can somebody explain why this is not possible through a GET call and suggest a better practice?
Why does passing data in the URI seem redundant? The HTTP spec says that GET methods are not to use content sent in the body. This is primarily to facilitate caches being able to cache responses based only on the URI, method and headers. Requiring caches to parse the body of a message to identify a resource would be very inefficient.
Here is an basic extension method that will do the grunt work for you,
public static class UriExtensions
{
public static Uri AddToQuery<T>(this Uri requestUri,T dto)
{
Type t = typeof (T);
var properties = t.GetProperties();
var dictionary = properties.ToDictionary(info => info.Name,
info => info.GetValue(dto, null).ToString());
var formContent = new FormUrlEncodedContent(dictionary);
var uriBuilder = new UriBuilder(requestUri) {Query = formContent.ReadAsStringAsync().Result};
return uriBuilder.Uri;
}
}
and assuming you have a DTO like this,
public class Foo
{
public string Bar { get; set; }
public int Baz { get; set; }
}
you can use it like this.
[Fact]
public void Foo()
{
var foo = new Foo()
{
Bar = "hello world",
Baz = 10
};
var uri = new Uri("http://example.org/blah");
var uri2 = uri.AddToQuery(foo);
Assert.Equal("http://example.org/blah?Bar=hello+world&Baz=10", uri2.AbsoluteUri);
}

Resources