Веб-API и HTTP-модуль
У нас есть HTTP-модуль, который декодирует все закодированные запросы.
Он отлично работает со всеми запросами WCF, но НЕ в запросах Web Api - в Web Api запрос (как POST, так и GET) попадает на еще закодированную службу
Я вижу, что он попадает в HTTP-модуль, но, опять же, добирается до шифрования сервиса.
Как я могу это исправить? или что я делаю неправильно?
я знаю, что лучше работать с обработчиками сообщений в веб-Api, но HTTP-модули, вероятно, тоже работают?
Модуль HTTP:
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
context.EndRequest += context_PreSendRequestContent;
}
void context_PreSendRequestContent(object sender, EventArgs e)
{
string encodedQuerystring = HttpContext.Current.Request.QueryString.ToString();
if (!string.IsNullOrEmpty(encodedQuerystring))
{
System.Collections.Specialized.NameValueCollection col = new System.Collections.Specialized.NameValueCollection();
col.Add("q", encodedQuerystring);
WebFunction.CreateQuerystring(HttpContext.Current, col);
}
}
void context_BeginRequest(object sender, EventArgs e)
{
string encodedQueryString = String.Empty;
if (HttpContext.Current.Request.QueryString.Count > 0 && HttpContext.Current.Request.QueryString["q"] != null)
{
object _p = HttpContext.Current.Request.QueryString;
encodedQueryString = HttpContext.Current.Server.UrlDecode(HttpContext.Current.Request.QueryString["q"].ToString());
string originalQueryString = HttpContext.Current.Server.UrlDecode(WebFunction.Base64Decode(encodedQueryString));
if (!string.IsNullOrEmpty(originalQueryString))
{
WebFunction.CreateQuerystring(HttpContext.Current, WebFunction.ConvertQueryToCollection(originalQueryString));
}
}
}
WebFunction:
public static void CreateQuerystring(HttpContext context, System.Collections.Specialized.NameValueCollection nameValueCollection)
{
// reflect to readonly property
PropertyInfo isreadonly = typeof(System.Collections.Specialized.NameValueCollection).GetProperty("IsReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
// make collection editable
isreadonly.SetValue(context.Request.QueryString, false, null);
context.Request.QueryString.Clear();
context.Request.QueryString.Add(nameValueCollection);
// make collection readonly again
isreadonly.SetValue(context.Request.QueryString, true, null);
}
Web Api:
public class NamesController : ApiController
{
[HttpGet]
[ActionName("GET_NAMES")]
public Drugs_ResponseData Get(string q)
{
//need to add the decode function to get it to work
string[] arrAmpersant = Commonnn.DecodeFrom64(q).Split('&');
Names_obj = new Names();
return _obj.GetResult(Convert.ToInt32(Commonnn.GetValFromEqual(arrAmpersant[0])));
}
}
Ответы
Ответ 1
Кажется, что веб-API не использует коллекцию QueryString в запросе, но сам анализирует URL-адрес.
См. метод GetQueryNameValuePairs
в этот файл - они берут Uri и анализируют его свойство Query.
Итак, у вас есть два варианта:
- Грязный должен изменить Uri запроса в вашем HTTP-модуле. Я не знаю, возможно ли это, но какое-то отражение может сделать трюк.
- Лучше всего использовать обработчик сообщений Web API.
Ответ 2
Могу ли я предложить вам использовать Context.Items и предоставить QueryString кодированную версию.
Это не очень известный встроенный словарь ключей/значений, который длится всюду по запросу, где вы легко храните любой объект, а затем делитесь им между модулем, обработчиками и т.д.
Использование этого очень похоже на лучшую производительность, чем разблокировка объекта QueryString, но что более важно, вы обрабатываете значение в одном месте и повторно используете его во многих, а при необходимости вы просто добавляете второе значение, полный QueryString коллекцию или любое другое значение, которое вы хотите разделить на запрос.
void context_BeginRequest(object sender, EventArgs e)
{
string encodedQueryString = String.Empty;
if (HttpContext.Current.Request.QueryString.Count > 0 && HttpContext.Current.Request.QueryString["q"] != null)
{
string encodedQueryString = HttpContext.Current.Server.UrlDecode(HttpContext.Current.Request.QueryString["q"].ToString());
HttpContext.Current.Items("qs_d") = HttpContext.Current.Server.UrlDecode(WebFunction.Base64Decode(encodedQueryString));
}
}
Web Api:
public class NamesController : ApiController
{
[HttpGet]
[ActionName("GET_NAMES")]
public Drugs_ResponseData Get(string q)
{
string[] arrAmpersant = Commonnn.DecodeFrom64(HttpContext.Current.Items("qs_d").ToString()).Split('&');
Names_obj = new Names();
return _obj.GetResult(Convert.ToInt32(Commonnn.GetValFromEqual(arrAmpersant[0])));
}
}
Боковое примечание. Я вижу, что вы дважды вызываете HttpContext.Current.Server.UrlDecode
. Я не думаю, что вам это нужно, если ваш метод Base64Decode
не закодирует значение снова.
Ответ 3
Вы можете справиться с этим
protected void Application_BeginRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
string path = app.Context.Request.Url.PathAndQuery;
int pos = path.IndexOf("?");
if (pos > -1)
{
string[] array = path.Split('?');
app.Context.RewritePath(array[0]+"?"+ HttpContext.Current.Server.UrlDecode(array[1]));
}
}
Ответ 4
Добавляя к ответу @Tomáš Herceg, я бы использовал обработчик сообщения Web Api, а не модифицировал ваш HttpModule для размещения веб-Api.
public class DecodeQueryStringMessageHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (request.Method == HttpMethod.Get)
{
var originalQueryString = request.RequestUri.Query;
if (!string.IsNullOrEmpty(originalQueryString))
{
var ub = new UriBuilder(request.RequestUri) { Query = HttpUtility.UrlDecode(originalQueryString) };
request.RequestUri = ub.Uri;
}
}
return base.SendAsync(request, cancellationToken);
}
}
Ответ 5
Возможно, но вам понадобится размышление, что означает, что здесь существует риск. Пожалуйста, позвольте мне предложить вам то, что я считаю более чистым решением после решения.
Решение
if (!string.IsNullOrEmpty(originalQueryString))
{
var request = HttpContext.Current.Request;
request.GetType().InvokeMember("QueryStringText", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty, null, request, new[] { "q=" + originalQueryString });
//WebFunction.CreateQuerystring(HttpContext.Current, WebFunction.ConvertQueryToCollection(originalQueryString));
}
Этот обновит следующие свойства запроса:
Request.Param
Request.QueryString
Request.ServerVariables
Request.Url
но не обновит:
Request.RawUrl
Чистое решение
Модуль перезаписи URL-адресов IIS
http://www.iis.net/learn/extensions/url-rewrite-module/developing-a-custom-rewrite-provider-for-url-rewrite-module