ASP.Net MVC Controller accepting "application/x-www-form-urlencoded" content type - asp.net

I inherited an old ASP.Net MVC web application. I need to modify it and add a page that can handle an incoming HTTP POST with hidden fields sent as "application/x-www-form-urlencoded" content type. This page's URL will be provided as a webhook URL to an external system that will use it to send back the control to my application and will provide some data in the form of "application/x-www-form-urlencoded" content type.
As I mentioned, this is an old MVC 5 application and is not targeting the .NET Core framework. Therefore I cannot declare my controller's method like this:
[HttpPost]
[Consumes("application/x-www-form-urlencoded")]
public void Webhook([FromForm] Response webHookResponse)]
The "Consumes" and "FromForm" attributes are available in the "Microsoft.AspNetCore.Mvc" namespace, but I don't have access to that in my application. Is there a different way to handle this in MVC 5?
Thanks,
Ed

You shouldn't have to do anything. The DefaultModelBinder will bind your form values to the parameter object without specifying Consumes or FromForm attributes. The same is also true for .NET Core.
Edit - code for clarity:
Also an important note: this (automatic form binding) will only work if you do NOT have the [ApiController] attribute tagging your controller.
Assume you have three hidden fields coming in, one is named foo, the other is named fudge
Either one of these will work:
[HttpPost]
public void Webhook(string foo, string fudge)
{
}
or:
public class WbResponse
{
public string foo {get; set;}
public string fudge {get; set;}
}
[HttpPost]
public void Webhook(WbResponse response)
{
}

Turns out that the request object contains all the hidden fields in its "Form" member which is a NameValueCollection. I declared my method like this:
// POST: /{Controller}/Webhook
[HttpPost]
public virtual ActionResult Webhook()
{
var requestFormFields = HttpContext.Request.Form;
and then I can access each field in the NameValueCollection by its name:
var hiddenFieldValue = requestFormFields["hiddenFieldName"];

Related

How to obtain request parameter from query string or request body inside ASP.NET Web API 2 Controller

In PHP, one can access request property by using the $_REQUEST 'superglobal' variable.
In Java Servlet, one can also do something similar by calling getParameter(string) or getParameterValues(string) on the incoming HttpServletRequest instance.
Both of these method do not care if the data is conveyed on the query string or on the body. They are HTTP-method-agnostic-ways of getting request properties.
How to do the same using ASP.NET 4.x (not Core) Web API 2?
As far as possible, I do not want to use model binding or route parameter. I just want to use built in Properties of the ApiController to access the request parameter directly.
Here's what I'm trying to do:
public class MyController : ApiController
{
[HttpPost]
public IHttpActionResult Index()
{
// somehow obtain 'requestParam ' either from query string OR from request body
var requestParam = Request.???
return Ok(requestParam);
}
}

MediaFormatter or ModelBinder for web api PUT method

I have a PUT method in web api which accepts a JSON data and a route data as follows.
[Route("api/v1/Orders/{orderId}/active")]
public HttpResponseMessage Put(Guid? orderId,List<ActiveRequest> activeRequests)
{
}
public class ActiveRequest
{
public int Id { get; set; }
public bool IsActive { get; set; }
}
Now is it possible to simplify the method signature as:
[Route("api/v1/Orders/{orderId}/active")]
public HttpResponseMessage Put(ActiveRequestModel model)
{
}
public class ActiveRequestModel
{
public Guid OrderId { get; set; }
public List<ActiveRequest> ActiveRequests {get; set;}
}
I tried writing a custom ModelBinder by implementing the System.Web.Http.ModelBinding.IModelBinder interface but could'nt find a way to read the JSON data that is coming inside the Request object.
I doubt that is there a way by which I can bind my model with data coming from three different places i.e. from route data, json & form.
You cannot simplify the parameter as described.
Unlike MVC model binding, beacuse of how the Web API formatter works, in Web API you only can have a single parameter that is deserialized from the payload, and a number of simple type parameters coming from route parameters or url query string. The reason is that the creation of the parameter coming from the payload is done in a single pass deserialization of the payload.
So, for your example you need the two parameters in your original version, i.e.:
public HttpResponseMessage Put(Guid? orderId, List<ActiveRequest> activeRequests)
If you want to use the ActiveRequestModel you need to include a payload which has exactly the same structure, so you should include the orderId in the payload, because it will not be recovered from the url (even if the name matches).
Please, read this article which explains how parameter binding works in Web API:
Parameter Binding in ASP.NET Web API
If you read it thoroughly you'll see that you can create and register your own model binder to make it work the same way that an MVC controller, but I think it's not worth the effort (so I include it only in this last paragraph), and it's not the standard way of working.

Get an instance of WebApi UrlHelper from inside an Mvc Action

I am running WebApi and Mvc from within the same project (so they are in-process). Mvc mostly for serving assets (pages and generated downloads) and web api for ajax data requests.
In order to be RESTish, most of the WebApi requests include a set of links where are generated by the following class:
public class ApiLinkMaker
{
public ApiLinkMaker(UrlHelper url, string authority) {
this.url = url;
this.authority = authority;
}
public ApiLinkMaker(ApiController controller)
: this(controller.Url, controller.Request.RequestUri.Authority) { }
public string MakeLink(string controller, string id) {
return "//" + authority + url.Route("DefaultApi", new { controller = controller, id = id });
}
}
There's a few other methods on there, but this is really the core of things and it works fine.
Now I want to optimize a particular page. Where previously I had two requests
Download the html
Do an Ajax query to get some data (and some links)
Now I realize that for optimization purposes it is better to do just one in this case.
Download the html with the data already JSON embedded into it.
The problem is that since the html is being generated by Mvc, I cannot create an Api UrlHelper that seems to work.
I tried
var url = new UrlHelper(new HttpRequestMessage(verb, controller.Request.Url.AbsoluteUri));
if (!url.Request.Properties.ContainsKey(HttpPropertyKeys.HttpConfigurationKey)) //http://stackoverflow.com/questions/11053598/how-to-mock-the-createresponset-extension-method-on-httprequestmessage
url.Request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration());
But this still blows up
System.ArgumentException was unhandled by user code
HResult=-2147024809
Message=A route named 'DefaultApi' could not be found in the route collection.
Parameter name: name
Source=System.Web.Http
ParamName=name
StackTrace:
at System.Web.Http.HttpRouteCollection.GetVirtualPath(HttpRequestMessage request, String name, IDictionary`2 values)
at System.Web.Http.Routing.UrlHelper.GetHttpRouteHelper(HttpRequestMessage request, String routeName, IDictionary`2 routeValues)
at System.Web.Http.Routing.UrlHelper.GetHttpRouteHelper(HttpRequestMessage request, String routeName, Object routeValues)
at System.Web.Http.Routing.UrlHelper.Route(String routeName, Object routeValues)
at MyProject.Models.ApiLinkMaker.MakeLink(String controller, String id) in w:\MyProject\Models\ApiLinkMaker.cs:line 42
...
This leads me to think that I'm going about this wrong - that I need to create the url helper from the api routing configuration somehow.
Why create one? There is an instance of the UriHelper exposed as a property on both the MVC Controller and ApiController classes.
public ActionResult Index()
{
string url = Url.RouteUrl("DefaultApi", new {httproute = "", controller = "test"});
return View();
}
Edit: Updated code. While the url helpers are different you can use the MVC url helper to resolve the web api url.
Edit2: The correct method to use if you want to get webapi routes from an Mvc UrlHelper is
string url = Url.HttpRouteUrl("DefaultApi", new {httproute = "", controller = "test"});

Add token parameter to all urls inside an asp.net mvc 2 site

I've integrated some pages written in ASP.NET MVC 2, into a classic webform app.
Everything works well except the authentication system.
The authentication system is using some token added to the url like :
/Account/Profil/Details.aspx?AUTHID=2ddc098a-cf0b-fd81-afb7-d41f35010b9f
When i reach my asp.net mvc pages (all these pages must be secured), they must include that AUTHID parameter.
I'm using the core Webform control to secure the pages, and this control check for the AUTHID token in the url. So basicly my route must include the
?AUTHID=2ddc098a-cf0b-fd81-afb7-d41f35010b9f
What the best and clever way to do this ?
I don't want to pass the AUTHID parameter manually in all controller actions.
Thanks for your help.
You can solve your problem by extending the ASP.NET routing mechanism. Just create a custom route and override the GetVirtualPath function.
public class TokenizedRoute : Route
{
public TokenizedRoute(string url, IRouteHandler routeHandler) : base(url, routeHandler)
{
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
string tokenValue = "your token value";
values.Add("AUTHID", tokenValue);
return base.GetVirtualPath(requestContext, values);
}
}
See my blog post for more details.
You could use a jQuery solution to append a token to the query string of all links:
$("a").each(function (index, link)
{
$(link).attr("href", $(link).attr("href") + "?AUTHID=" + token);
});
But I agree with dknaack, I would say you should reconsider your authentication logic if at all possible.
You can save the AuthId in the Session object and create a custom Authorize Attribute.
Attribute
public class CustomAuthorize : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
// your custom logic depending on Session["AuthId"]
return httpContext.Session["AuthId"] != null;
}
}
Controller
public class MyController : Controller
{
[CustomAuthorize]
public ActionResult MyActionMethod()
{
return View();
}
}
hope this helps

ASP.NET MVC 2.0 JsonRequestBehavior Global Setting

ASP.NET MVC 2.0 will now, by default, throw an exception when an action attempts to return JSON in response to a GET request. I know this can be overridden on a method by method basis by using JsonRequestBehavior.AllowGet, but is it possible to set on a controller or higher basis (possibly the web.config)?
Update: Per Levi's comment, this is what I ended up using-
protected override JsonResult Json(object data, string contentType, System.Text.Encoding contentEncoding)
{
return Json(data, contentType, JsonRequestBehavior.AllowGet);
}
This, like other MVC-specific settings, is not settable via Web.config. But you have two options:
Override the Controller.Json(object, string, Encoding) overload to call Json(object, string, Encoding, JsonRequestBehavior), passing JsonRequestBehavior.AllowGet as the last argument. If you want this to apply to all controllers, then do this inside an abstract base controller class, then have all your controllers subclass that abstract class.
Make an extension method MyJson(this Controller, ...) which creates a JsonResult and sets the appropriate properties, then call it from your controller via this.MyJson(...).
There's another option. Use Action Filters.
Create a new ActionFilterAttribute, apply it to your controller or a specific action (depending on your needs). This should suffice:
public class JsonRequestBehaviorAttribute : ActionFilterAttribute
{
private JsonRequestBehavior Behavior { get; set; }
public JsonRequestBehaviorAttribute()
{
Behavior = JsonRequestBehavior.AllowGet;
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
var result = filterContext.Result as JsonResult;
if (result != null)
{
result.JsonRequestBehavior = Behavior;
}
}
}
Then apply it like this:
[JsonRequestBehavior]
public class Upload2Controller : Controller
MVC 2 block Json for GET requests for security reasons. If you want to override that behavior, check out the overload for Json that accepts a JsonRequestBehavior parameter.
public ActionResult Index()
{
return Json(data, JsonRequestBehavior.AllowGet)
}
I also got this error when I first use MVC 2.0 using my old code in MVC 1.0. I use fiddler to identify the cause of the error. See the steps on how to troubleshoot it using Fidder -
http://www.rodcerrada.com/post/2011/07/11/jQuery-getJSON()-does-not-tirgger-the-callback-in-ASPNET-MVC-2.aspx
Is this is the security issue MVC2 was trying to address?
http://haacked.com/archive/2009/06/25/json-hijacking.aspx
If so, it seems like the vulnerability is only an issue if you are trying to do a json call to an outside website. If your MVC2 app is only making json calls to your own website (to fill jqgrids for example), shouldn't you be able to safely override the Json call in your base controller to always allow get?
Just change JSON code from :
$.getJson("methodname/" + ID, null, function (data, textStatus)
to:
$.post("methodname/" + ID, null, function (data, textStatus)

Resources