1

I have a web api 2 application created with some controllers that uses authentication. This is written in .NET using C#. Then I am consuming this via an android application. The initial request works fine to validate the user and return true. It is on the 2nd URL request where I am calling a method via the web api 2 that uses authentication to work where everything bombs out with a Request.Path exception message.

Below are the android .java files in question.

HttpHelper class:

package com.example.helpers;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CookieStore;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.DefaultHttpClient;

import android.util.Log;

public abstract class HttpHelper {

    private final static String TAG = "HttpHelper";
    private final static String API_URL = "http://10.0.2.2/application/api";

    private static CookieStore sCookieStore;

    public static String invokePost(String action, List<NameValuePair> params) {
        try {
            String url = API_URL + action + "/";
            Log.d(TAG, "url is" + url);
            HttpPost httpPost = new HttpPost(url);
            if (params != null && params.size() > 0) {
                HttpEntity entity = new UrlEncodedFormEntity(params, "UTF-8");
                httpPost.setEntity(entity);
            }
            return invoke(httpPost);
        } catch (Exception e) {
            Log.e(TAG, e.toString());
        }

        return null;
    }

    public static String invokePost(String action) {
        return invokePost(action, null);
    }

    public static String invokeGet(String action, List<NameValuePair> params) {
        try {
            StringBuilder sb = new StringBuilder(API_URL);
            sb.append(action);
            if (params != null) {
                for (NameValuePair param : params) {
                    sb.append("?");
                    sb.append(param.getName());
                    sb.append("=");
                    sb.append(param.getValue());
                }
            }
            Log.d(TAG, "url is" + sb.toString());
            HttpGet httpGet = new HttpGet(sb.toString());
            return invoke(httpGet);
        } catch (Exception e) {
            Log.e(TAG, e.toString());
        }

        return null;
    }

    public static String invokeGet(String action) {
        return invokeGet(action, null);
    }

    private static String invoke(HttpUriRequest request)
            throws ClientProtocolException, IOException {
        String result = null;
        DefaultHttpClient httpClient = new DefaultHttpClient();

        // restore cookie
        if (sCookieStore != null) {
            httpClient.setCookieStore((org.apache.http.client.CookieStore) sCookieStore);
        }

        HttpResponse response = httpClient.execute(request);

        StringBuilder builder = new StringBuilder();
        BufferedReader reader = new BufferedReader(new InputStreamReader(
                response.getEntity().getContent()));
        for (String s = reader.readLine(); s != null; s = reader.readLine()) {
            builder.append(s);
        }
        result = builder.toString();
        Log.d(TAG, "result is ( " + result + " )");

        // store cookie
        sCookieStore = (CookieStore) ((AbstractHttpClient) httpClient).getCookieStore();
        return result;
    }
}

Consuming Code:

 @Override
     protected String doInBackground(String... arg0) {

         StringBuilder stringBuilder = new StringBuilder();

        String authResult = HttpHelper.invokeGet("<validate_user_via_web_api>");

        String nextResult = HttpHelper.invokeGet("<call_web_api_other_method>");

         return stringBuilder.toString();
     }

The value of sCookieStore within the HttpHelper class:

[[version: 0][name: .ASPXAUTH][value: <long_string_of_numbers_and_characters>][domain: 10.0.2.2][path: /][expiry: null]]

This is what gets stored in the sCookieStore variable in the HttpHelper class that gets applied to the httpClient via the setCookieStore.

And the code for web api 2. First the 'validate_user_via_web_api' /api/account?userName=&userPassword=:

using Application.Models;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Security;

namespace Application.Controllers
{
    [RoutePrefix("api/Account")]
    public class AccountController : ApiController
    {
        private AuthRepository _repo = null;

        public AccountController()
        {
            _repo = new AuthRepository();
        }

        public bool Get(String userName, String userPassword)
        {
            Task<IdentityUser> iu = _repo.FindUser(userName, userPassword);

            if (iu != null)
            {
                FormsAuthentication.SetAuthCookie(userName, false);
                return true;
            }

            return false;
        }

        [Authorize(Roles="Application")]
        [Route("Register")]
        public async Task<IHttpActionResult> Register(UserModel userModel)
        {
            String userID = "";

            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            IdentityResult result = await _repo.RegisterUser(userModel);

            IHttpActionResult errorResult = GetErrorResult(result);

            if (errorResult != null)
            {
                return errorResult;
            }
            else
            {
                Task<IdentityUser> iu = _repo.FindUser(userModel.UserName, userModel.Password);

                using (var context = new AuthContext())
                {
                    var userStore = new UserStore<IdentityUser>(context);
                    var userManager = new UserManager<IdentityUser>(userStore);

                    userID = iu.Result.Id;

                    result = await userManager.AddToRoleAsync(userID, "Users");

                    errorResult = GetErrorResult(result);

                    if (errorResult != null)
                    {
                        return errorResult;
                    }
                }
            }

            return Ok("userID:" + userID);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
                _repo.Dispose();

            base.Dispose(disposing);
        }

        private IHttpActionResult GetErrorResult(IdentityResult result)
        {
            if (result == null)
                return InternalServerError();

            if (!result.Succeeded)
            {
                if (result.Errors != null)
                {
                    foreach (string error in result.Errors)
                    {
                        ModelState.AddModelError("", error);
                    }
                }

                if (ModelState.IsValid)
                {
                    return BadRequest();
                }

                return BadRequest(ModelState);
            }

            return null;
        }
    }
}

The 'call_web_api_other_method' code example:

[Authorize(Roles="Users")]
        [Route("routinename")]
        public IHttpActionResult getapplicationmethod(string param1, string param2)
        {
            //rest of code...
        }

This is the one that is causing the Request.Path error. When I just go to the URL provided, without going first to validating the user authentication, everything bombs out instead of getting an unauthorized message. The url looks something similar to below and nothing within the URL looks to be something that would cause that error to be thrown: http://hostname/application/api/data/getapplicationmethod?param1=param_value1&param2=param_value2. Also when I go to this link directly via the browser, I get the Authorization has been denied for this request message that I should get without authenticating the request.

Putting [System.Web.Mvc.ValidateInput(false)] on the given method still produces the same error as well. Even with that and the httpRuntime targetFramework="2.0" the error still gets thrown. Obviously I wouldn't keep these changes in place they were just for testing purposes.

Handled exception message:

A potentially dangerous Request.Path value was detected from the client (:)

            <b> Description: </b>An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information 
            about the error and where it originated in the code.            <br><br>            
            <b> Exception Details: </b>System.Web.HttpException: A potentially dangerous Request.Path value was detected from the client (:).<br><br>            
            <b>Source Error:</b> <br><br>            An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location 
            of the exception can be identified using the exception stack trace below.<br>            <b>Stack Trace:</b> <br><br>            

            [HttpException (0x80004005): A potentially dangerous Request.Path value was detected from the client (:).]   
            System.Web.HttpRequest.ValidateInputIfRequiredByConfig() +12715107   System.Web.PipelineStepManager.ValidateHelper(HttpContext context) +166 <br>                
            <b>Version Information:</b>&nbsp;Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.34237
iCobot
  • 175
  • 15

1 Answers1

1

On Android side, in invokeGet , you are passing in a url without encoding it properly

change

    HttpGet httpGet = new HttpGet(sb.toString());

to

    HttpGet httpGet = new HttpGet(URLEncoder.encode(sb.toString(), "UTF-8"));

If you are using any special characters in url, please see this for adding asp.net config exception

And, in your invoke() method add

    request.addHeader("Host", "http://10.0.2.2");

before HttpResponse response = httpClient.execute(request);

you may consider adding other headers depending on what your server expects.

Community
  • 1
  • 1
ashoke
  • 6,441
  • 2
  • 26
  • 25
  • That produces the following error: java.lang.IllegalStateException: Target host must not be null, or set in parameters. scheme=null, host=null, path=`http://10.0.2.2/application/api/data/getapplicationmethod?param1=param1_value&param2=param2_value`. – iCobot Nov 07 '14 at 19:33
  • The link that bombs out, as described in my post, works fine in the browser with an authentication error which is what should be expected when not authenticating the request. The 10.0.2.2 needs to be in place due to it coming from an android application as localhost can't be used. – iCobot Nov 07 '14 at 19:43
  • @ashhoke - java.lang.IllegalStateException: Target host must not be null, or set in parameters. scheme=null, host=null, path=`http://10.0.2.2/application/api/getapplicationmethod?param1=param1_va‌​lue&param2=param2_value`. – iCobot Nov 07 '14 at 20:14
  • @iCobot now we are dealing with entirely different kind of error, you need to [setup headers properly please see this](http://stackoverflow.com/questions/7867712/target-host-must-not-be-null-or-set-in-parameters-scheme-null-host-null). Atleast add the host as mentioned in my previous comment. – ashoke Nov 07 '14 at 20:16
  • I did add the header and same the same error came up. My last post included the `request.addHEader("Host", "localhost");` – iCobot Nov 07 '14 at 20:22
  • I removed the request.addHeader and the initial change in your answer. I then added, from the initial link you provided with your answer, this: `requestPathInvalidCharacters=""` to the web.config httpRuntime portion and I no longer get the request.Path error. That includes my other changes with validatepage request false in web.config as well as validateinpu on controller method as well as pointing to targetFramework = 2.0. Not a fan of keeping these changes just to get rid of an error. – iCobot Nov 07 '14 at 20:40
  • @iCobot if it helped diagnose/fix the error, can you please upvote/mark answer – ashoke Nov 07 '14 at 22:02
  • I just did. I am going to tinker around with it some more as I am not a fan of keeping some of those in place long term but the link did provide that one last key step to remove the error for now. – iCobot Nov 07 '14 at 23:05