注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

widebright的个人空间

// 编程和生活

 
 
 

日志

 
 

http post 请求里面的 Content-type头部属性和asp.net 的web service handler的问题, iis的url rewrite扩展  

2014-10-15 22:35:39|  分类: 程序设计 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
http协议的 头部 Content-type 属性 指定 body部分的编码格式(Media Type)
=================================
server端根据这个值使用不同方法来解析,比如普通的form-urlencoded  或者二进制的文件上传内容等。
具体可以参考HTTP 协议的说明文档。


POST方法默认 Content-type用的就是application/x-www-form-urlencoded.比如下面这个


POST http://localhost:88/samples/authenticate HTTP/1.1 
Host: localhost:88 
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
Content-type: application/x-www-form-urlencoded 
Content-Length: 30

Username=ricks&Password=sekrit




System.Web.Services.Protocols.HttpServerProtocol.ReadParameters()
System.Web.Services.Protocols.WebServiceHandler.Invoke()
System.Web.Services.Protocols.WebServiceHandler.CoreProcessRequest()
====================
asp.net的默认web service handler的HTTP_POST WebServiceBinding要求reques里面Content-type必须为application/x-www-form-urlencoded ,才能去解析后面body部分包含的parameter 变量(Username=ricks&Password=sekrit) 放到request.parameter 面去?

底层代码使用的是这个类
System.Web.Services.Protocols.UrlParameterReader
http://msdn.microsoft.com/en-us/library/system.web.services.protocols.urlparameterreader(v=vs.110).aspx

如果client端不指定Content-type , web service handle 是会拒绝处理的。



但client端不兼容web service hander的这个约定,不指定Content-type的话。怎么样才能兼容出来这个client的request呢?

1.  haproxy等代理里面可以通过配置补上这个Content-type

2.  通过实现自定义的HTTP Handler或者HTTP Modules来直接处理原始request。
(1)可以在自己的HTTP Handler 的IHttpHandler.ProcessRequest方法里面,给HttpRequest补上ContentType 属性,然后自己再调用
    System.Web.Services.Protocols.WebServiceHandlerFactory.GetHandler 方法得到web service handler的接口
    再把request转交给web service进行处理?

    asp.net的urlMappings功能这是可能有点用
    <system.web>
    <urlMappings enabled="true">
<add url="~/Default.aspx" mappedUrl="~/Handler.ashx"/>
    </urlMappings>

   相关的类型有HttpContext  HttpRequest 
   http://msdn.microsoft.com/en-us/library/system.web.httpcontext_properties(v=vs.110).aspx
   http://msdn.microsoft.com/en-us/library/system.web.httprequest_properties(v=vs.110).aspx

 (2)实现HTTP Modules ,注册request的某个生命周期事件,在调用web service handler之前就补上补上ContentType 属性
 如果这个方法可行的话,应该比实现HTTP Handler的方法(1)更好一些。

      可能这个事件比较合适
      PreRequestHandlerExecute event
      Occurs just before ASP.NET starts executing an event handler (for example, a page or an XML Web service).

      事件点查找
      ASP.NET Application Life Cycle Overview for IIS 7.0
      http://msdn.microsoft.com/en-us/library/vstudio/bb470252(v=vs.100).aspx
      ASP.NET Application Life Cycle Overview for IIS 5.0 and 6.0
      http://msdn.microsoft.com/en-us/library/vstudio/ms178473(v=vs.100).aspx







HTTP Handlers and HTTP Modules Overview
http://msdn.microsoft.com/en-us/library/vstudio/bb398986(v=vs.100).aspx


HTTP Handlers
=============
根据配置,只指对某个request处理

ASP.NET page handler  处理发给 .aspx 文件的请求
Web service handler   处理发给 .asmx  文件的请求
Generic Web handler   (*.ashx)  默认的handler  找不到对应的UI page, @WebHandler  配置里面也没有,就用这个hanlder处理

写一个自定义的httphandler的话就是要实现IHttpHandler接口的ProcessRequest方法。处理完参数里面的HttpContext 并返回一个response。

IHttpHandler.ProcessRequest
http://msdn.microsoft.com/en-us/library/vstudio/system.web.ihttphandler.processrequest(v=vs.100).aspx


HTTP Modules
============
这个可以对每个请求都进行过滤和处理,类似IIS的ISAPI 扩展,不过是.Net写的,是跟ASP.NET框架整合在一起。
An HTTP module is an assembly that is called on every request that is made to your application. HTTP modules are called as part of the request pipeline and have access to life-cycle events throughout the request. HTTP modules therefore let you examine incoming requests and take action based on the request. They also let you examine the outgoing response and modify it.



asp.net页面回应里面是可以自己指定 Content-type的
=======================================

response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");

Response.ContentType = "text/xml; charset=utf-8; gzip";















ASP.NET的MVC框架,有人写了几篇博客,说怎么支持根据 Content-type 来解析处理这个http request的,
=====================

ASP.NET Web API  可以实现自定义的HttpParameterBinding 类,

http://weblog.west-wind.com/posts/2013/Dec/13/Accepting-Raw-Request-Body-Content-with-ASPNET-Web-API
http://weblog.west-wind.com/posts/2012/Sep/11/Passing-multiple-simple-POST-Values-to-ASPNET-Web-API
http://weblog.west-wind.com/posts/2012/Aug/16/Mapping-UrlEncoded-POST-Values-in-ASPNET-Web-API




补充:
测试一下,使用http module,自己加上content-type的方法是可以行的。测试代码如下:



public class CustomContentTypeModule : IHttpModule
{

public String ModuleName
{
get { return "CustomContentTypeModule"; }
}

// In the Init function, register for HttpApplication
// events by adding your handlers.
public void Init(HttpApplication application)
{
application.PreRequestHandlerExecute += (new EventHandler(this.Application_PreRequestHandlerExecute));
}

// Your BeginRequest event handler.
private void Application_PreRequestHandlerExecute(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
if (context.Request != null &&
context.Request.ContentType == "" &&
context.Request.Path.EndsWith("webservice_method_name")) { // 是不是对应的method

// client端的request如果不指定的话,我们强制指定它。
context.Request.ContentType = "application/x-www-form-urlencoded";
}
}


public void Dispose()
{
}
}

web.config

<?xml version="1.0" encoding="utf-8"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />

</system.web>
<system.webServer>
<modules>
<add name="CustomContentTypeModule" type="testnamespace.CustomContentTypeModule" />
</modules>
</system.webServer>
</configuration>


client, 用go语言写的一个简单的HTTP POST方式的web service调用

package main

import (
"fmt"
"net/http"
"io/ioutil"
"bytes"
// "net/url"
)

type HelloWorld struct{}

func (h *HelloWorld) ServeHTTP(w http.ResponseWriter, r *http.Request) {
body, _ := ioutil.ReadAll(r.Body)
fmt.Printf("\n%v\n\n", string(body))


fmt.Fprint(w, ` hello world ` )
}

func main() {
// resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf)
// resp, err := http.PostForm("http://example.com/form",
// url.Values{"key": {"Value"}, "id": {"123"}})

// client := &http.Client{
// CheckRedirect: redirectPolicyFunc,
// }
// req, err := http.NewRequest("GET", "http://example.com/form", nil)
// req.Header.Add("If-None-Match", `W/"wyzzy"`)
// resp, err := client.Do(req)


// url := "http://example.com/form"
// fmt.Println("URL:>", url)

// var jsonStr = []byte(`{"title":"Buy cheese and bread for breakfast."}`)
// req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
// req.Header.Set("X-Custom-Header", "myvalue")
// req.Header.Set("Content-Type", "application/json")

// client := &http.Client{}
// resp, err := client.Do(req)
// if err != nil {
// panic(err)
// }
// defer resp.Body.Close()

// fmt.Println("response Status:", resp.Status)
// fmt.Println("response Headers:", resp.Header)
// body, _ := ioutil.ReadAll(resp.Body)
// fmt.Println("response Body:", string(body))






// 正确的方式, web service的method 对应函数四个字符串类型输入参数, 用url encode方法
// var request_body = []byte(`key1=value1&key2=value2&key3balue3key4=value4`)
// resp, err := http.Post("http://localhost:29861/TestListener.asmx/webservice_method_name", "application/x-www-form-urlencoded", bytes.NewBuffer(request_body))

// 正确的方式2
// resp, err := http.PostForm("http://localhost:29861/TestListener.asmx/webservice_method_name",
// url.Values{"key1": {"value1"},
// "key2": {"value2"},
// "key3": {"value3"},
// "key4": {"value4"}})

url := "http://localhost:29861/TestListener.asmx/webservice_method_name"
var request_body = []byte(`key1=value1&key2=value2&key3balue3key4=value4`)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(request_body))
// req.Header.Set("X-Custom-Header", "myvalue")
// req.Header.Set("Content-Type", "application/json")

client := &http.Client{}
resp, err := client.Do(req)


if err != nil {
// handle error
fmt.Println("response error\n")
}

defer resp.Body.Close()

fmt.Println("response Status:", resp.Status)
fmt.Println("response Headers:", resp.Header)
body, err := ioutil.ReadAll(resp.Body)
fmt.Println("response Body:", string(body))






//=====================简单的server =================
// var h HelloWorld
// fmt.Println("hello world!!! 启动了http服务器了\n")
// http.ListenAndServe("0.0.0.0:22318", &h)

// fmt.Printf("ddddddddd\n")
}



利用iis的url rewrite extensions ,修改url地址映射也可以同时修改http 头部变量达到同样的目的。
这个应该是iis的官方扩展吧,当然你可以自己协议http module来完成同样功能了。 asp.net本身内置url mapping,但好像没有这个url rewrite功能强大
参考  URL Rewrite Module Configuration Reference
http://www.iis.net/learn/extensions/url-rewrite-module
http://www.iis.net/learn/extensions/url-rewrite-module/url-rewrite-module-configuration-reference#UsingServerVars

<system.webServer>
<rewrite>
<rules>
<rule name=“test" patternSyntax="ExactMatch">
<match url=“test" />
<action type="Rewrite" url="/test.asmx/testmethod" />
<serverVariables>
<set name="HTTP_Content-Type" value="application/x-www-form-urlencoded" />
</serverVariables>
</rule>
</rules>
</rewrite>
</system.webServer>


  评论这张
 
阅读(723)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017