Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC Api Action & System.Web.Http.AuthorizeAttribute - How to get post parameters?

I have the following API Controller:

public class TestController : ApiController
{
    [HttpPost]
    [APIAuthorizeAttribute]
    public IQueryable<Computers> ListOfComputersInFolder(Guid folderId)
    {
        return GetListOfComputersForFolder(folderId);
    } // End of ListOfComputersInFolder
} // End of TestController 

And the following is my basic APIAuthorizeAttribute.

public class APIAuthorizeAttribute : System.Web.Http.AuthorizeAttribute
{
    public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        var Request = System.Web.HttpContext.Current.Request;
        var folderId = Request.RequestContext.RouteData.Values["folderId"] ?? Request.Params["folderId] as string;
        if(null == folderId)
        {
            folderId = actionContext.ControllerContext.RouteData.Values["folderId"];
        }

        base.OnAuthorization(actionContext);
    }
}

The problem that I'm having is that folderId is coming out null in the onAuthorize method. (I based the fetcher on this code).

It seems to me that this should be working, but I cannot seem to get it to. Any ideas on what I am doing wrong and how I should go about getting the posted parameter?

Edit: I tried reading the post data directly with the following:

using (StreamReader inputStream = new StreamReader(request.InputStream))
{
    output = inputStream.ReadToEnd();
}
request.InputStream.Position = 0;

Which gets me the post data in JSON format which I could then parse, but then my call never makes it though. I get the following exception in the Response:

  <h2>500 - Internal server error.</h2>
  <h3>There is a problem with the resource you are looking for, and it cannot be displayed.

at System.Json.JXmlToJsonValueConverter.JXMLToJsonValue(Stream jsonStream, Byte[] jsonBytes)\u000d\u000a   at System.Net.Http.Formatting.JsonMediaTypeFormatter.<>c__DisplayClass7.<OnReadFromStreamAsync>b__6()\u000d\u000a   at System.Net.Http.Internal.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken)"}

EDIT: In the end, it seems like this could possibly be a bug with the combination of ApiController, System.Web.Http.AuthorizeAttribute and HttpPost (it does work when using HttpGet). A bug report has been submitted.

like image 894
Kyle Avatar asked Aug 31 '12 13:08

Kyle


People also ask

What is an MVC action?

ASP.NET MVC Action Methods are responsible to execute requests and generate responses to it. By default, it generates a response in the form of ActionResult. Actions typically have a one-to-one mapping with user interactions.

How does API work in MVC?

The Web API returns the data on request from the client, and it can be in the format XML or JSON. The MVC architecture is the Model-View-Controller Pattern. It is used for isolating an application to simplify testing and the maintenance of the code of the application.

What is action in Web API?

In ASP.NET Web API, a controller is a class that handles HTTP requests. The public methods of the controller are called action methods or simply actions. When the Web API framework receives a request, it routes the request to an action. To determine which action to invoke, the framework uses a routing table.

What is MVC action result?

An action result is what a controller action returns in response to a browser request. The ASP.NET MVC framework supports several types of action results including: ViewResult - Represents HTML and markup. EmptyResult - Represents no result. RedirectResult - Represents a redirection to a new URL.


2 Answers

The AuthorizeAttribute should have an AuthorizationContext parameter rather than a HttpActionContext one, from that you should be able to access the RouteData e.g.

public override void OnAuthorization(AuthorizationContext filterContext)
{
    var folderId = filterContext.RouteData.Values["folderId"];
    ...
}

Update

Noticed you are using ApiController and as such using Http.AuthorizeAttribute (explains why you don't have an AuthorizationContext). In that case, you can get the RouteData via the action context e.g.

var folderId = actionContext.Request.GetRouteData().Values["folderId"];
like image 147
James Avatar answered Nov 15 '22 04:11

James


I have also encountered this problem.

To work around it I wrote the following method which I call from within the OnAuthorization method:

private static object GetValueFromActionContext(HttpActionContext actionContext, string key)
{
    var queryNameValuePairs = actionContext.Request.GetQueryNameValuePairs();

    var value = queryNameValuePairs
        .Where(pair => pair.Key.Equals(key, StringComparison.OrdinalIgnoreCase))
        .Select(pair => pair.Value)
        .FirstOrDefault();

    var methodInfo = ((ReflectedHttpActionDescriptor) (actionContext.ActionDescriptor)).MethodInfo;
    var parameters = methodInfo.GetParameters();
    var parameterType =
        parameters.Single(p => p.Name.Equals(key, StringComparison.OrdinalIgnoreCase)).ParameterType;

    var converter = TypeDescriptor.GetConverter(parameterType);

    return converter.ConvertFromString(value);
}

This code makes the following assumptions:

  • The key you are extracting matches an argument name on the action method.

  • The parameter type you are obtaining will have a converter valid for the type.

  • You are not using any custom binding or formatting on the parameter.

In the scenario that I am using the code I am only expecting simple types such as Guid, Boolean, String etc and could be customised as per your requirements.

The extension method GetQueryNameValuePairs is part of the System.Net.Http.HttpRequestMessageExtensions class and will read querystring / form data.

Example use:

object folderId = GetValueFromActionContext(actionContext, "folderId");
like image 45
jim.taylor.1974 Avatar answered Nov 15 '22 04:11

jim.taylor.1974