How to get an object from the HttpResponseMessage? - asp.net

I have a Post method that returns an HttpResponseMessage:
HttpResponseMessage response =
Request.CreateResponse(HttpStatusCode.Created, updatedItemDto);
I'm writing some tests for this and would like to get the updated item from the HttpResponseMessage (particularly the ItemId). I tried inspecting the object and it looks like the object lives in Response.Content, but I don't know how to get it from the Content.

If HttpResponseMessage contain serialized data model of a known class then you can use ReadAsAsync<>() extension method like this
MyObject obj = await response.ReadAsAsync<MyObject>();
And this is the most simple and easy way.

You can inspect the response in the debugger since you mention "it looks like the object lives in Response.Content".
You might need to cast it to something meaningful, like in this response,
Request.CreateResponse(HttpStatusCode.Created, updatedItemDto) as MyObject;

Related

Should I try to take BadRequest(ModelState) returned from my API, and deserialize to *what* with JSON.NET?

TL;DR;
"I like how my generated AutoRest client deserializes my main entities when dealing with the 200 scenarios.. but, MUST I manually parse the 400 scenarios?", said the lazy programmer
DETAILS:
So, I have an API, (Web API 2), and I do all the standard stuff.. using POCO's that implement IValidatable in addition to property-level validation using System.Data.DataAnnotations my Web API returns 400 errors like this (just an example):
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
And, where appropriate I use SwaggerResponse attributes, and my swagger.json is documented thus so that my generated client knows that a 400 is a viable response.
Now, my unit tests which directly instantiate the api controllers, I purposely try to test for invalid model state.. I can take the IHttpActionResult response from the controller invocation, and cast it to InvalidModelStateResult and iterate over the ModelState dictionary.
But, I find writing something similar for my 'production HTTP calls' with an actual HTTP client -- not as straightforward.
So, getting closer to the heart of my question:
Is there a preferred method for deserializing the InvalidModelStateResult?
So, when interacting with my API with actual http calls.. via the Microsoft.Rest.ServiceClient the JSON that I get back is in a slightly different shape..
Example MVC controller code interacting with my API:
HttpOperationResponse resp = await client.SpecialLocations.PatchByIdWithHttpMessagesAsync(id, locationType, "return=representation");
if (!resp.Response.IsSuccessStatusCode)
{
//The JSON returned here is not really in the form of an InvalidModelStateResult
ViewBag.Error = await resp.Response.Content.ReadAsStringAsync();
return View(locationType);
}
So, for now, I'm using Newtonsoft's JObject to parse ModelState returned from my WebAPI (again - it's not really named as such once retrieved via http request) and now pushing it into my MVC controller's ModelState.
This is my answer for now. But will consider others that have any merit. It just seems like a weird thing to have to do.
HttpOperationResponse resp = await client.SpecialLocations.PatchByIdWithHttpMessagesAsync(id, locationType, "return=representation");
if (resp.Response.StatusCode == HttpStatusCode.BadRequest)
{
string jsonErrStr = await resp.Response.Content.ReadAsStringAsync();
JObject err = JObject.Parse(jsonErrStr);
string[] valPair = ((string)err["error"]["innererror"]["message"]).Split(":".ToCharArray());
//now push into MVC controller's modelstate, so jQuery validation can show it
this.ModelState.AddModelError(valPair[0].Trim(),valPair[1].Trim());
return View(locationType);
}

Return JSON via MVC controller vs webmethod

I'm trying to convert a web forms page with a number of code-behind webmethod functions into an MVC view with a controller.
The data I get back is different. They have properly formatted JSON, but the webmethod returns JSON like this:
{"d":"{\"Success\":true,\"Data\":{\"QuoteId\":340439,\"LoginId\":40,
And the controller returns:
"{\"QuoteId\":340444,\"LoginId\":40,
Its not wrapping it in data.d like it is set up to handle in the javascript and there are no Success or Data objects. And when I try to parse it ($.parseJSON(data)) like I did with the webmethod, it gives me the old error at line 1 message.
I'm sure that if I played with it enough I could get it to work with the way the data is coming through, but I have many pages that I need to covert in the future and I'm just wondering if there is an easy way to get the controller to format it like the webmethod does.
I'm pretty sure that I know why the data is formatted differently, but it would make my life easier if I could just return the data in the same way. I've tried returning a JsonConvert.SerializeObject(obj), which is just a string, and a Return Json(obj), which I'm guessing is just a string as well, but they both return stuff the same, non-data.d way.
Okay, so I've decided to wrap the data in a "d", so that I can have a "data.d" in the Ajax result.
But for some reason the dates aren't being formatted correctly when I just send the data back via:
return Json(thewrappeddata);
they turn out weird.
So basically what I have to do is this:
public class JSONReturn {
public object d;
public static object Wrap(object data) {
return new JSONReturn() { d = data };
}
}
var thedata = new TonsofData();
return Json(
JSONReturn.Wrap(
JsonConvert.SerializeObject(thedata,
new JsonSerializerSettings {
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
}
)
)
);
It seems like I'm "jsonifying" the data twice here, once with JsonConvert.SerializeObject() and once with Json() (I THINK that serializes), but it seems to work and now I get what I need and all the JS just works as it did before. If I leave out the JsonConvert.SerializeObject(), the Json() formats the dates incorrectly and it causes all sorts of failure down the line.

EditorForModel and DisplayForModel don't work on Expando objects

Or at least, they don't do what I was expecting. My Action method looks like this:
Function Test() As ActionResult
Dim model As Object = New ExpandoObject()
model.FieldA = 123
model.FieldB = "This is a string"
model.FieldC = DateTime.Now
Return View(model)
End Function
and the view looks like this:
#ModelType Object
#Html.DisplayForModel
I was expecting it to produce the same thing you'd get if the model were a real object with those fields, but instead I get this:
[FieldA, 123][FieldB, This is a string][FieldC, 3/29/2012 12:10:24 PM]
EditorForModel does the exact same thing. Is this the correct behavior, and what's the best way to implement the behavior that I'm looking for?
Yes, this is the correct behavior. Default metadata provider (DataAnnotationsModelMetadataProvider) uses reflection to get model information and that is the reason you get this result.
I would suggest to create a custom metadata provider that will work correctly with dynamics. Try to inherit from this class - http://msdn.microsoft.com/en-us/library/system.web.mvc.dataannotationsmodelmetadataprovider.aspx.

What is the point of JObject's CreateWriter if using the writer just throws ArgumentException?

I have a "legacy" method that takes a JsonWriter and dumps a bunch of json.. Out of this, I want a JObject instance, so I tried the following:
JObject myObj = new JObject();
using (var writer = myObj.CreateWriter())
{
TheLegacyMethod(writer);
}
// TODO: Do stuff with the nicely initialized JObject instance
The issue is that the first time "TheLegacyMethod" attempts to actually use the writer to do a:
writer.WriteStartObject();
I get an ArgumentException complaining that a JObject can't be added to a JObject.
I've used a smelly workaround, writing the Json to a StringBuilder first, then doing a JObject.Parse() on the result... but I would like to avoid this intermediate.
What is the point of JObject.CreateWriter() if you can't use it? Am I missing something? (I'm hoping I am).
Thanks,
Tyler
When writing values to an object the property name must be written before the value.

Newbie question on MvcContrib TestHelpers

I'm just starting to use the TestHelpers in MvcContrib. I want to try and test an action method on my controller that itself tests if IsAjaxRequest() is true.
I've used the same code that is shown in the TestHelper samples to set up the TestControllerBuilder
_controller = new StarsController();
_builder = new TestControllerBuilder();
_builder.InitializeController(_controller);
So that _controller has all the faked/mocked HttpContext inside it, which is really great. But what do I do now to force IsAjaxRequest() on the internally faked Request object to return true?
Here's the code I used, the code in my question at the top of the page uses MvcContrib testhelpers to create a nicely faked controller (_controller) that internally has faked versions of HttpRequest, HttpResponse etc. Then as per Patrick's advice I created a new headers collection containing an entry for X-Requested-With. Then told _controller.HttpContext.Request.headers to return my headers collection whenever it attempts to look at headers (i.e. which is what occurs when IsAjaxRequest() is called).
var headers = new NameValueCollection();
headers.Add("X-Requested-With", "XMLHttpRequest");
_controller.HttpContext.Request.Stub(r => r.Headers).Return(headers);
Works like a treat.
You need to stub out the HttpRequest.Headers property to return a NameValueCollection that contains an entry for "X-Requested-With" with the value "XMLHttpRequest".

Resources