发布

时间:2017-02-08 15:41:17

标签: c# ajax post recaptcha

我有一个从Javascript代码到C#Web Api的Ajax请求,我在尝试在实际处理请求之前验证reCaptcha信息。当我在VS2015社区本地测试它时(localhost:port /下的IIS Express)就像魅力一样,但当我将它部署在我的测试服务器(在机器/文件夹下的单独机器上的IIS)或甚至在我的本地测试IIS中时(在localhost但在locahost / folder /下,请求失败,代码为500,所以我得到了自己的“服务器错误”消息

这是我的API方法

    [HttpPost]
    public List<Thing> GetThings(string parameter1, string parameter2)
    {
        List<Thing> ListThings = new List<Thing>();
        try
        {
            var serializer = new JsonSerializer();
            bool recaptchaValidation = false;
            using (var sr = new StreamReader(Request.Content.ReadAsStreamAsync().Result))
            {
                using (var jsonTextReader = new JsonTextReader(sr))
                {
                    dynamic captchaResponse = serializer.Deserialize(jsonTextReader, typeof(ReCaptchaEncodedResponse));
                    recaptchaValidation = ReCaptcha.ReCaptcha.Validate(captchaResponse.captchaResponse, System.Configuration.ConfigurationManager.AppSettings["recaptchaPrivateKey"]) == "True" ? true : false;
                }
            }
            if (!recaptchaValidation)
                return null;
         ... Code for getting the list of things...
         return ListThings;
         }
         ... Code for processing exceptions ... 
    }

这是我要求它的JS

$scope.submitForm = function () {
    var captchaResponse = grecaptcha.getResponse();
    if (!captchaResponse) { 
        refreshCatcha();
        alert("Wrong captcha");
        return;
    }
    $("input").prop('disabled', 'disabled'); 
    $http.post('api/ManageThingsApi/GetThings/' + $scope.Thing.Param1 + "/" + $scope.Thing.Param2,
        { "captchaResponse": captchaResponse }).success(function (data) {
        if (data === null) {
            refreshCatcha();
            alert("Not found");
        }
        else if (data.length === 0) {
            refreshCatcha();
            alert("Not found");

        }
        else {
            $scope.Data = data;
            $location.path('/ShowThings');
            $("#Search").css({ 'display': "none" });

        }
        $("input").prop('disabled', ''); 
        }).error(function (error) {
            $("input").prop('disabled', '');
            alert("Server error");
    });
}

这是我的ReCaptchaEncodedResponse类

public class ReCaptchaEncodedResponse
{
    public string captchaResponse { get; set; }
}

我不确定,但我开始相信问题出现在POST参数(我读过的正文)和服务器中的Request.Content之间。

那么什么错?

已更新:更改了API方法,使其仅接收来自帖子的所有信息,并删除了难看的阅读请求正文。

修改后的API方法:

    public List<Thing> GetThings([FromBody]SearchRequest srequest)
    {
        List<Thing> ListThings = new List<Thing>();
        bool recaptchaValidation = false;
        recaptchaValidation = ReCaptcha.ReCaptcha.Validate(srequest.ValidationToken, System.Configuration.ConfigurationManager.AppSettings["recaptchaPrivateKey"]) == "True" ? true : false;
        if (!recaptchaValidation)
            return null;
       ...
     }

修改了JS:

$scope.submitForm = function () {
    $scope.captchaResponse = grecaptcha.getResponse();
    if (!$scope.captchaResponse) {
        alert("Captcha verification required");
        return;
    }
    $("input").prop('disabled', 'disabled');
    var requestData = {
        Param1: $scope.Thing.Param1,
        Param2: $scope.Thing.Param2,
        ValidationToken: $scope.captchaResponse
    };
    $http.post('api/ManageThingsApi/GetThings',
                requestData, 'json').success(function (data) {
        if (data === null) {
            grecaptcha.reset();
            alert("Not found");
        }
        else if (data.length === 0) {
            grecaptcha.reset();
            alert("Not found");

        }
        else {
            $scope.Data = data;
            $location.path('/ShowThings');
            $("#Search").css({ 'display': "none" });

        }
        $("input").prop('disabled', '');
        }).error(function (error) {
            $("input").prop('disabled', '');
            alert("Server error");
    });
};

参数类:

public class SearchRequest {
        public string Param1 { get; set; }
        public string Param2 { get; set; }
        public string ValidationToken { get; set; }
    }

再次像VS2015 IIS Express中的魅力一样,来自本地IIS的错误500.

1 个答案:

答案 0 :(得分:0)

<强>解决!

错误(像往常一样)来自最意想不到,愚蠢而且简单的地方,你几乎从未检查过。

这两个代码都运行良好,但我忘记设置服务器代理以连接到Google以验证令牌。

请参阅第一次对Google的请求来自客户端(在javascript代码中),因此它使用的是浏览器的代理设置通常是对的。现在,对Google的第二次请求来自服务器(在C#代码中),因此它将使用服务器的配置和凭据进行连接。

使用本地IIS Express 进行测试时,由于IIS Express在已记录用户的凭据和配置上运行,因此它也使用与客户端相同的配置。但标准IIS 本身通常使用自己的用户凭据来运行,因此没有代理配置。

修改(和工作!)代码

    [HttpPost]
    public List<Thing> GetThings(ValidatedRequest requestParams)
    {
        List<Thing> ListThings = new List<Thing>();
        if (requestParams == null)
            throw new UnauthorizedAccessException("Unauthorized");
        if (string.IsNullOrEmpty(requestParams.ValidationToken))
            throw new UnauthorizedAccessException("Unauthorized");
        JObject obj = null;
        try
        {
            var wc = new WebClient();
            var googleVerificationURL = string.Format("https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}&remoteip={2}", ConfigurationManager.AppSettings["gReCaptchaPrivateKey"], requestParams.ValidationToken, remoteIpAddress);
            if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["ProxyAddress"].Trim()))
            {
                wc.Proxy = new WebProxy(ConfigurationManager.AppSettings["ProxyAddress"].Trim());
                wc.Proxy.Credentials = new NetworkCredential(ConfigurationManager.AppSettings["ProxyUsername"].Trim(), ConfigurationManager.AppSettings["ProxyPassword"]);
            }
            var result = wc.DownloadString(googleVerificationURL);
            wc.Dispose();
            obj = JObject.Parse(result);
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException("Failed to connect to reCaptcha", ex);
        }
        if(obj == null || !(bool)obj.SelectToken("success"))
            throw new UnauthorizedAccessException("Unauthorized");
        ... Rest of the code ... 
     }

如您所见,我还在 web.config 的AppSettings部分内创建了代理地址,代理用户和代理传递的变量(可能存在安全问题)

对SearchRequest类进行了一些微小的修饰,现在已成为ValidatedRequest类

    public class ValidatedRequest
    {
        public string ValidationToken { get; set; }
        public string Data1 { get; set; }
        public string Data2 { get; set; }
    }

就是这样,Sayonara!