Web API 和 HTTP 模块
Web API and HTTP Module
我们有一个 HTTP 模块可以解码所有编码的请求。
它适用于所有 WCF 请求,但在 Web Api 请求中 NOT - 在 Web Api 中请求(POST 和 GET)到达服务仍然编码
我看到它命中了 HTTP 模块,但同样,它仍然到达编码的服务。
我该如何解决?或者我做错了什么?
我知道在 Web Api 中使用消息处理程序会更好,但是 HTTP 模块应该也可以工作- 不是吗?
HTTP Module:
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])));
}
}
好像WebAPI在请求中没有使用QueryString集合,而是自己解析了URL
参见 this file 中的 GetQueryNameValuePairs
方法 - 他们获取 Uri 并解析其查询 属性。
所以你有两个选择:
- 肮脏的方法是更改 HTTP 模块中请求的 Uri。我不知道这是否可能,但一些反思可以解决问题。
- 更好的方法是使用 Web API 消息处理程序。
我可以建议您使用 Context.Items 并让 QueryString 具有编码版本。
这是一个不太知名的内置 key/value 字典,它贯穿整个请求,您可以在其中轻松存储任何对象,然后在模块、处理程序等之间共享它。
与解锁 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));
}
}
网络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
方法再次对值进行编码。
你可以这样处理
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]));
}
}
有可能,但你需要反思,什么意思是这里存在风险。请让我在解决方案后向您推荐我认为更干净的解决方案。
解决方案
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
清洁剂
IIS URL 重写模块
http://www.iis.net/learn/extensions/url-rewrite-module/developing-a-custom-rewrite-provider-for-url-rewrite-module
添加到@Tomáš Herceg 的回答中,我将实现一个 Web Api 消息处理程序,而不是修改您的 HttpModule 以适应 Web 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);
}
}
我们有一个 HTTP 模块可以解码所有编码的请求。 它适用于所有 WCF 请求,但在 Web Api 请求中 NOT - 在 Web Api 中请求(POST 和 GET)到达服务仍然编码
我看到它命中了 HTTP 模块,但同样,它仍然到达编码的服务。 我该如何解决?或者我做错了什么? 我知道在 Web Api 中使用消息处理程序会更好,但是 HTTP 模块应该也可以工作- 不是吗?
HTTP Module:
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])));
}
}
好像WebAPI在请求中没有使用QueryString集合,而是自己解析了URL
参见 this file 中的 GetQueryNameValuePairs
方法 - 他们获取 Uri 并解析其查询 属性。
所以你有两个选择:
- 肮脏的方法是更改 HTTP 模块中请求的 Uri。我不知道这是否可能,但一些反思可以解决问题。
- 更好的方法是使用 Web API 消息处理程序。
我可以建议您使用 Context.Items 并让 QueryString 具有编码版本。
这是一个不太知名的内置 key/value 字典,它贯穿整个请求,您可以在其中轻松存储任何对象,然后在模块、处理程序等之间共享它。
与解锁 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));
}
}
网络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
方法再次对值进行编码。
你可以这样处理
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]));
}
}
有可能,但你需要反思,什么意思是这里存在风险。请让我在解决方案后向您推荐我认为更干净的解决方案。
解决方案
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
清洁剂
IIS URL 重写模块
http://www.iis.net/learn/extensions/url-rewrite-module/developing-a-custom-rewrite-provider-for-url-rewrite-module
添加到@Tomáš Herceg 的回答中,我将实现一个 Web Api 消息处理程序,而不是修改您的 HttpModule 以适应 Web 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);
}
}