AJAC MVC3 Request object and raw Ajax data, where the heck is it? - ajax

If this was a regular post of a form I could go to Request.Form['somevalue'] and get the value. If this was a get with a query string I could go to Request.QueryString["somevalue"] and get the value.
Where is the raw data when you post an ajax request. I need a value out of the raw data string in a filter method.
Any help will be appreciated!!
Edits below:
public class ValidateAntiForgeryId : FilterAttribute, IAuthorizationFilter {
public void OnAuthorization(AuthorizationContext filterContext) {
if (filterContext == null) {
throw new ArgumentNullException("filterContext");
}
Guid filterGuid;
Guid.TryParse(filterContext.RequestContext.HttpContext.Request.Form["__sessionId"], out filterGuid);
if (filterGuid == Guid.Empty)
throw new AuthenticationException("Authentication failure");
try {
var cookieGuid = (Guid)filterContext.RequestContext.HttpContext.Items["SessionId"];
} catch {
throw new AuthenticationException("Authentication failure");
}
}
The posted data looks like this:
{"SsnLastFour":"2222","AccountNumber":"B112233","__sessionId":"dca0a504-3c40-4118-ae19-afefb9bfc8bd"}
I need access to the __sessionId chunk inside the filter.

There's nothing magic about AJAX posts. They're just plain old HTTP. That means you have plain old HTTP post values, and/or plainold HTTP Get values.
If you're not seeing them, it probably means you're not actually submitting them.
EDIT:
Two issues you did not include in your original question: 1) That this is JSON, and 2) That this is in an AuthorizationFilter (rather than an action method).
Both change the answers. Since ASP.NET does not natively understand JSON post values, you will have to parse them, via Request.InputStream. MVC3 by default has a JSON model binder, but AuthorizationFilters execute before model binders do, so you will be accessing things prior to the model binders being executed, and as such FormsCollection won't be populated (Request.Form[] won't work either, because as I said, asp.net doesn't natively understand JSON).
You may find that installing JSON.net via nuget may help with this task. Or you might just write a simple parse routine, since you know exactly what you're looking for.

You can accept the parameter values the same way you accept in normal form post.
Ex :
$.get("User/Get", { userId : "24"} ,function(data){
alert(data);
});
or
$("#yourDivId").load("User/Get?userId=23");
Your action method should look like
public ActionResult Get(int userId)
{
// you have value present in userId
if(Request.IsAjax())
{
return View("Partial/MyPartialView");
}
return View();
}
One thing you have to remember is, the parameter name of your action method should be same as of what your parameter/querystring name.

The fitlerContext has an ActionParameters collection which should have the parsed JSON properties (in case that helps). This may be easier than parsing the InputStream.
var sessionId = filterContext.ActionParameters["__sessionId"];

Related

WebApi + OData: limit maximum results

I have a WebAPI controller that takes an ODataOptions parameter.
I want to make sure the user can't download the whole database in one swoop.
So I validated the options object:
public IHttpActionResult Get(ODataQueryOptions<ViewModel> options)
{
var oDataValidationSettings = new ODataValidationSettings
{
MaxTop = 100
}
try
{
options.Validate(oDataValidationSettings);
}
catch (ODataException ex)
{
return BadRequest("OData query validation failed: " + ex.Message);
}
//return results
}
This works great for calls like
http://host/api/controller?$filter=...&$top=1000
This returns the expected validation error message.
But it is trivially easy to circumvent by simply making a request to:
http://host/api/controller?
No $top, no nothing. This in effect returns the whole table!
The validator is not triggered if the $top parameter is not specified at all.
I could append a .Take(100) when constructing the query from the oData options, but it seems hacky.
Is there any better way to deal with a missing $top?
You can try to use PageSize which will limit the number of entity been returned.
Refer to this example for how to use it.
https://github.com/OData/ODataSamples/tree/master/WebApi/v4/ODataPagingSample

How to pursuade the ApiExplorer to create documentation for ExpandoObject?

I've created a very neat way of implementing a PATCH method for my Web.API project by making use of an ExpandoObject as a parameter. As illustrated below:
[HttpPatch, Route("api/employee/{id:int}")]
public IHttpActionResult Update(int id, [FromBody] ExpandoObject employee)
{
var source = Repository.FindEmployeeById(id);
Patch(employee, source);
Repository.SaveEmployee(source);
return Ok(source);
}
However, when generating documentation ApiExplorer is at a loss as to what to do with the ExpandoObject, which is totally understandable. Would anyone have any ideas on how to manipulate the ApiExplorer to provide some sensible documentation?
My idea was to maybe introduce an new attribute which points to the actual Type that is expected:
public IHttpActionResult Update(int id, [FromBody, Mimics(typeof(Employee))] ExpandoObject employee)
{
...
}
But I have no idea where to start, any ideas or suggestions are welcome.
So this has been the source of some late evenings in order to get the Api Explorer to play along with our developed Http Patch mechanism. Truth be told, I'd probably should do a bit of a proper write up to full explain the mechanics behind the whole idea. But for those of you who landed on this page because you want the Api explorer to use a different type in the documentation, this is where you need to look:
Open HelpPageConfigurationExtensions.cs and locate the following method:
//File: Areas/HelpPage/HelpPageConfigurationExtensions.cs
private static void GenerateRequestModelDescription(HelpPageApiModel apiModel, ModelDescriptionGenerator modelGenerator, HelpPageSampleGenerator sampleGenerator)
{
....
}
this is the location where the parameter information is available to you and also provides you with the ability to replace/substitute parameter information with something else. I ended up doing the following to handle my ExpandoObject parameter issue:
if (apiParameter.Source == ApiParameterSource.FromBody)
{
Type parameterType = apiParameter.ParameterDescriptor.ParameterType;
// do something different when dealing with parameters
// of type ExpandObject.
if (parameterType == typeof(ExpandoObject))
{
// if a request-type-attribute is defined, assume the parameter
// is the supposed to mimic the type defined.
var requestTypeAttribute = apiParameter.ParameterDescriptor.GetCustomAttributes<RequestTypeAttribute>().FirstOrDefault();
if (requestTypeAttribute != null)
{
parameterType = requestTypeAttribute.RequestType;
}
}
}
Just, note that the RequestTypeAttribute is something I devised. My WebApi endpoint looks like this now:
public IHttpActionResult Update(int id,
[FromBody, RequestType(typeof(Employee))] ExpandoObject employee)
Thank you to everyone who took time to look into the problem.

Restricting auto Help Page contents when using Attribute Routing in Web API 2

I'm currently implementing a Web API using Web API 2's attribute routing (http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2). I am also using the Help Pages module in order to automatically generate documentation from XML comments (http://www.asp.net/web-api/overview/creating-web-apis/creating-api-help-pages).
For this API I am providing support for optional return format extensions, so that every API method has a pair of routes defined on it like so:
[HttpGet]
[Route("Path/Foo")]
[Route("Path/Foo.{ext}")]
public HttpResponseMessage DoFoo()
{
// Some API function.
}
This allows a user to hit any of these and get a result:
www.example.com/api/Controller/Path/Foo
www.example.com/api/Controller/Path/Foo.json
www.example.com/api/Controller/Path/Foo.xml
My issue is that when Help Pages uses MapHttpAttributeRoutes() to generate documentation, it is picking up both routes for each method. So right now I see help for:
api/Controller/Foo
api/Controller/Foo.{ext}
But I want to only see:
api/Controller/Foo.{ext}
I would prefer to hide the non-extension route on each method, so that every method only shows a single Help Page entry.
Has anyone else tried something similar? Is there a work around that I am missing?
My question would be is that, would consumers of your api figure out easily that the {ext} is optional?...personally, I would prefer the default behavior...but anyways following are some workarounds that I can think of:
A quick and dirty workaround. Split the DoFoo into 2 actions like DoFoo() and DoFooWithExt maybe. Notice that I am using an attribute called ApiExplorerSettings, which is for HelpPage purposes. Example below:
[HttpGet]
[Route("Path/Foo")]
[ApiExplorerSettings(IgnoreApi=true)]
public HttpResponseMessage DoFoo()
{
return DoFooHelper();
}
[HttpGet]
[Route("Path/Foo.{ext}")]
public HttpResponseMessage DoFooWithExt()
{
return DoFooHelper();
}
private HttpResponseMessage DoFooHelper()
{
//do something
}
Create a custom ApiExplorer (which HelpPage feature uses internally) and check for specific routes like the following and can decide whether to show the action or not for that particular route.
// update the config with this custom implementation
config.Services.Replace(typeof(IApiExplorer), new CustomApiExplorer(config));
public class CustomApiExplorer : ApiExplorer
{
public CustomApiExplorer(HttpConfiguration config) : base(config)
{
}
public override bool ShouldExploreAction(string actionVariableValue, HttpActionDescriptor actionDescriptor, IHttpRoute route)
{
if (route.RouteTemplate.EndsWith("Path/Foo", StringComparison.OrdinalIgnoreCase))
{
return false;
}
return base.ShouldExploreAction(actionVariableValue, actionDescriptor, route);
}
}
Get list of all ApiDescription from the default ApiExplorer and then filter out the descriptions which you do not like. Example:
Configuration.Services.GetApiExplorer().ApiDescriptions.Where((apiDesc) => !apiDesc.RelativePath.EndsWith("Path/Foo", StringComparison.OrdinalIgnoreCase))

MediaTypeFormatter WriteToStreamAsync not called unless I add to Accept headers

I have a MediaTypeFormatter that converts an internal rep of an image to a png/jpeg/etc. if someone asks for it. However, my WriteToStreamAsync never gets called unless I add an image/png or similar to the accept headers.
First, here is my webapi method, with some key bits removed for brevity:
public ImageFormatter.BinaryImage GetImage(int cId, int iId)
{
....
using (var input = iFIO.OpenRead())
{
input.Read(b.data, 0, (int)iFIO.Length);
}
// With this next line my mediatypeformatter is correctly called.
Request.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("image/png"));
return b;
}
And here is the write portion of my MediaTypeFormatter (there is also a read portion, and that works great, actually).
namespace PivotWebsite.MediaFormatters
{
public class ImageFormatter : MediaTypeFormatter
{
public class BinaryImage
{
public byte[] data;
public string metaData;
}
public ImageFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/jpg"));
SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/jpeg"));
SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/png"));
}
public override bool CanWriteType(Type type)
{
return true;
}
public override async Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
{
var b = value as BinaryImage;
if (b == null)
throw new InvalidOperationException("Can only work with BinaryImage types!");
await writeStream.WriteAsync(b.data, 0, b.data.Length);
}
}
}
What I expected to be able to do was, in WriteToStreamAsync, to alter the outgoing headers to include Content-Type as "image/png" (or whatever, depending on the data type).
However, when I call this from a web browser with a URL like "http://my.testdomain.net:57441/api/Images?cID=1&iID=1", the WriteToStreamAsync never gets called (accepted headers are listed as {text/html, application/xhtml+xml, */*}). If I add the line above that adds the proper image type, then everything is called as I would expect.
What am I missing here? The accepted header of "*/*" should have triggered my media formatter, right? Or... am I missing something basic about the plumbing in Web API.
Do you want the image formatter to always get used if the Accept header is "/"? If that's the case, then you should insert your formatter first in the Formatters collection like this:
config.Formatters.Insert(0, new ImageFormatter());
What happens when there isn't an exact Accept header match like in your case is that the first formatter that can write the type gets selected to serialize the object. So if you register your formatter first, it would get used.
This could have unintended side-effects because it would affect all your controllers though. I would suggest changing the CanWriteType implementation to only return true if it's a BinaryImage. That should make the formatter only get used when that's your return type.
Another thing you could do is select the formatter directly in your action by returning an HttpResponseMessage:
return Request.CreateResponse(HttpStatusCode.OK, image, new ImageFormatter());
That's basically saying "this action should always use the image formatter, regardless of content-type, accept headers etc". That might be reasonable in your case if you're always just returning an image and you need it serialized with your formatter.
I'm writing a CsvFormatter and I want to be able to call the API from the browser to trigger a file download. Since I didn't have control over the Accept header, I wanted to use an extension to trigger my CSV formatter, but the XML formatter kept getting the request. I found that by adding a "text/html" media type, I could handle the CSV extension. Hopefully this doesn't cause other problems down the line :).
public CsvFormatter()
{
var header = new MediaTypeHeaderValue("text/csv");
SupportedMediaTypes.Add(header);
MediaTypeMappings.Add(new UriPathExtensionMapping("csv", header));
// From Chrome: Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
// Allow the formatter to run from a standard browser request.
header = new MediaTypeHeaderValue("text/html");
SupportedMediaTypes.Add(header);
MediaTypeMappings.Add(new UriPathExtensionMapping("csv", header));
}

use camel case serialization only for specific actions

I've used WebAPI for a while, and generally set it to use camel case json serialization, which is now rather common and well documented everywhere.
Recently however, working on a much larger project, I came across a more specific requirement: we need to use camel case json serialization, but because of backward compatibility issues with our client scripts, I only want it to happen for specific actions, to avoid breaking other parts of the (extremely large) website.
I figure one option is to have a custom content type, but that then requires client code to specify it.
Is there any other option?
Thanks!
Try this:
public class CamelCasingFilterAttribute : ActionFilterAttribute
{
private JsonMediaTypeFormatter _camelCasingFormatter = new JsonMediaTypeFormatter();
public CamelCasingFilterAttribute()
{
_camelCasingFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
ObjectContent content = actionExecutedContext.Response.Content as ObjectContent;
if (content != null)
{
if (content.Formatter is JsonMediaTypeFormatter)
{
actionExecutedContext.Response.Content = new ObjectContent(content.ObjectType, content.Value, _camelCasingFormatter);
}
}
}
}
Apply this [CamelCasingFilter] attribute to any action you want to camel-case. It will take any JSON response you were about to send back and convert it to use camel casing for the property names instead.

Resources