Creating a Web API to work with XML requests
For a project on the side I’m creating a Web API which has to parse XML requests in a POST. The first method I’ve written looks like this:
[HttpPost]
public HttpResponseMessage IndexPost(RequestModel requestMessage)
{
return new HttpResponseMessage(HttpStatusCode.Accepted) { Content = new StringContent("This is the POST API response from BusinessPartner!") };
}
To test the new API I’m using the Postman Chrome plugin. With this plugin you are able to send requests to an endpoint and see what the response is. Of course you can also use Fiddler for this, but I like the user experience of Postman a bit more.
There were a few problems I encountered during the development of the Web API.
The first problem was this error:
{
"message": "An error has occurred.",
"exceptionMessage": "No MediaTypeFormatter is available to read an object of type 'RequestModel' from content with media type 'text/plain'.",
"exceptionType": "System.InvalidOperationException",
"stackTrace": " at System.Net.Http.HttpContentExtensions.ReadAsAsync[T](HttpContent content, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger)\r\n at System.Net.Http.HttpContentExtensions.ReadAsAsync(HttpContent content, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger)\r\n at System.Web.Http.ModelBinding.FormatterParameterBinding.ReadContentAsync(HttpRequestMessage request, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger)\r\n at System.Web.Http.ModelBinding.FormatterParameterBinding.ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)\r\n at System.Web.Http.Tracing.Tracers.FormatterParameterBindingTracer.<>c__DisplayClass3.<executebindingasync>b__1()\r\n at System.Web.Http.Tracing.ITraceWriterExtensions.TraceBeginEndAsync(ITraceWriter traceWriter, HttpRequestMessage request, String category, TraceLevel level, String operatorName, String operationName, Action`1 beginTrace, Func`1 execute, Action`1 endTrace, Action`1 errorTrace)\r\n at System.Web.Http.Tracing.Tracers.FormatterParameterBindingTracer.ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)\r\n at System.Web.Http.Controllers.HttpActionBinding.<>c__DisplayClass1.<executebindingasync>b__0(HttpParameterBinding parameterBinder)\r\n at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()\r\n at System.Threading.Tasks.TaskHelpers.IterateImpl(IEnumerator`1 enumerator, CancellationToken cancellationToken)"
}
This was quite surprising as I thought I was sending a proper request which could be serialized. For reference, I was sending a POST request to https://localhost:11210/api/businesspartnerapi/ in this format:
<RequestModel><RequestMessage>1</RequestMessage></RequestModel>
As I couldn’t find anything on the internet explaining what I did wrong, I started looking through the options of Postman. Apparently you can add information to the header of the request, like a content type. After having added the contenttype text/xml
to the message the API returned the response which I had expected.
Success!
Apparently you need to specify the correct content type in order for ASP.NET to know how to deserialize the object.
But there is more. The second problem I faced was the requestMessage being null.
Quite strange as the model is serializable. I’ve also added the [Serializable]-attribute to the model, but to no avail.
From the back in the WCF days I remembered you have to add the [DataContract]- and [DataMember]-attributes to a model if you to use them in services. Therefore I added them to my model.
The RequestModel now looks like this:
[DataContract(Namespace="")]
public class RequestModel
{
[DataMember]
public string RequestMessage { get; set; }
}
After POST-ing another request to the API I was able to see the result.
As you can see in the Watch window, the object is successfully created with the value I expected.
So, if you want to debug a Web API project, don’t forget to add the proper content type to your request and make sure you specify the attributes to your model so it can be (de)serialized.