Posting to WebAPI with ModelClass Object as Parameter - asp.net-web-api

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());
}

Related

How to create HttpResponseMessage in a non-APIController

I have a service layer called GatewayService which calls another WebApi to get the product information and manipulate the Inventory in the response and return the same response to the caller.
Here is the code I have. The problem that I have is, I can't use Request.CreateResponse(...) which will give me a compilation error because GatewayService does not inherit ApiController.
Is there a way to update the response and return as HttpResponseMessage?
public class GatewayService
{
// Code Removed for bravity
public HttpResponseMessage Get(SingleProductSearcRequest request)
{
var response = productServiceWebApi.Get(request); // Returns HttpResponseMessage
var p = response.Content.ReadAsAsync<JObject>().Result;
p["Inventory"] = "Not Available";
return Request.CreateResponse(p); // COMPILER ERROR!!!
}
}
Request.CreateResponse() is just an extension method for HttpRequest. You can manually construct an HttpResponseMessage as well as give it content by doing something like:
var p = response.Content.ReadAsAsync<JObject>().Result;
HttpResponseMessage message = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
message.Content = new ObjectContent(p);
You can transfer headers and other information over as well, if necessary. Depending on the need, there's also StringContent, etc.

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?

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

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'

How to check attribute of action from HttpRequest

I've followed this Prevent Forms authentication in order to try and handle redirecting from ajax gracefully. However I need to be able to determine if certain attributes are decorating the action that this call was made for as I only want to do this for some occasions. Can I get this information from the HttpRequest object that is accessible within this method?.
Essentially taking the part from the code above that I would like to manipulate:
public class SuppressFormsAuthenticationRedirectModule : IHttpModule {
private void OnPostReleaseRequestState(object source, EventArgs args) {
var context = (HttpApplication)source;
var response = context.Response;
var request = context.Request; // request is HttpRequest
if (response.StatusCode == 401 && request.Headers["X-Requested-With"] ==
"XMLHttpRequest") {
// TODO HERE: Check that the controller action contains a particular attribute
// and if so do not suppress redirect
SuppressAuthenticationRedirect(context.Context);
}
}
}
UPDATE:
It's probably worth noting that this code is held within a compiled DLL project that is then encorporated into a host MVC application (which we don't have access to). In that case I don't really have access to changing default implementations unless I can ensure it doesn't effect the rest of the controllers in the application.
I tried to use as much of the framework as possible, which is why I chose to expose the GetControllerType method from the DefaultControllerFactory. You'll notice that routeData contains the area, controller and action, so with a bit of reflection, you can bypass having to create a derived controller factory.
This is definitely not production ready. It is just a way to get the custom attributes from the requested action.
Edit: instead of setting the current controller factory, create a new DerivedControllerFactory
var httpApplication = (HttpApplication)sender;
var httpContext = new HttpContext(httpApplication.Request, new HttpResponse(new StringWriter()));
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
//var factory = ControllerBuilder.Current.GetControllerFactory() as DerivedControllerFactory;
var factory = new DerivedControllerFactory();
var controllerType = factory.GetControllerType(new RequestContext(new HttpContextWrapper(httpContext), routeData), routeData.Values["controller"].ToString());
var methodInfo = controllerType.GetMethod(routeData.Values["action"].ToString());
var attributes = methodInfo.GetCustomAttributes(true);
public class DerivedControllerFactory : DefaultControllerFactory
{
public new Type GetControllerType(RequestContext requestContext, string controllerName)
{
return base.GetControllerType(requestContext, controllerName);
}
}

httpmessagehandler - reading content

I created a message handler which will log the request and the response. ideally I want to
public class LoggingMessageHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
LogRequest(request);
return base.SendAsync(request, cancellationToken).ContinueWith(task =>
{
var response = task.Result;
LogResponse(response);
return response;
});
}
private void LogRequest(HttpRequestMessage request)
{
var writer = request.GetConfiguration().Services.GetTraceWriter();
var content = request.Content;
(content ?? new StringContent("")).ReadAsStringAsync().ContinueWith(x =>
{
writer.Trace(request, "request", System.Web.Http.Tracing.TraceLevel.Info, t =>
{
t.Message = x.Result;
});
});
}
private void LogResponse(HttpResponseMessage response)
{
var request = response.RequestMessage;
var writer = request.GetConfiguration().Services.GetTraceWriter();
var content = response.Content;
(content ?? new StringContent("")).ReadAsStringAsync().ContinueWith(x =>
{
writer.Trace(request, "response", System.Web.Http.Tracing.TraceLevel.Info, t =>
{
t.Status = response.StatusCode;
t.Message = x.Result;
});
});
}
}
and here is my client code.
public ActionResult Index()
{
var profile = Client.GetAsync("Vendor").Result.EnsureSuccessStatusCode().Content.ReadAsAsync<VendorProfileModel>().Result;
return View(profile);
}
Logging appears to be working. However, when this handler is registered my client code returns an empty object. If I remove this handler the model is successfully read from the response and displayed on screen.
Is there a way to read the content and display the results on the client?
after a few more days for digging around on the net I finally found the root problem and a solution. First the problem:
everything in webapi is async
my action uses Controller.User which in turn is calling Thread.CurrentPrinciple
I am using ITraceWriter as my logging abstraction
apparently there is a bug in the ITraceWriter mechanicism where the current profile is not propagated across threads. therefore, i loose the principle when i get to my controller action. therefore, my query returns an empty result, rather than a fully populated result.
solution: don't use ITraceWriter to log messages. It would have been nice to use the built in mechanics, but that doesn't work. here is the link to the same issue which provides more detail/context.
https://aspnetwebstack.codeplex.com/workitem/237

Resources