Add custom headers to WebRequest from ResourceClient

Jul 24, 2011 at 7:56 AM

I'm connecting to service that requires authentication with WSSE (example here http://www.codeproject.com/KB/IP/WSSEClient.aspx by implementing IAuthenticationModule). Thus I need to add some custom headers to the WebRequest. It would be nice to extend the ResourceClient so we can modify the request before sending. This could be done by overriding the class and supplying new factory or by composition and fluent methods (similar to WithHandlerDefinitions).

Jul 25, 2011 at 12:39 PM

So you want to do something like the below, taken from the codeproject example:

request.Headers.Add("X-WSSE", string.Join("", new string[] 
	{ "UsernameToken ", "Username=\"", credential.UserName, "\", ", 
	"PasswordDigest=\"", digest, "\", ", "Nonce=\"", 
	Convert.ToBase64String(nonce), "\", ", "Created=\"", createtime, "\"" }));
Jul 25, 2011 at 12:46 PM

Having never done authentication with WSSE on WP7, I presume this can be achieved because it is not attempting to access a restricted header?

http://msdn.microsoft.com/en-us/library/system.net.webheadercollection(v=VS.95).aspx

Jul 25, 2011 at 12:57 PM
Edited Jul 25, 2011 at 12:59 PM

AS you suggest I am thinking the use of fluent method, something like 'WithCustomHeader(ICustomHeader wsseHeader)'. Then the developer can implement their own implementations.

Where the interface is defined something like:

public interface ICustomHeader
{
	public string Name { get; }
	public string Value { get; }
}

pulbic class ExampleCustomHeader : ICustomHeader
{
	public string Name
	{
		get { return "ollie"; }
	}

	public string Value
	{
		get { return BuildValue(); }
	}

	ppivate string BuildValue()
	{
		return "Blah " + "Blah " + "Blah ";
	}
}

If you like this approach can I get this implementation today :)

Jul 25, 2011 at 1:23 PM

I was rather thinking to add interface that would expose a method that gets access to full request just before sending. This is in case the header value will depend on the data in the request. However for my particular scenario your solution would work fine as well.

One more question: would I need to configure this header for each instance of ResourceClient (after calling Create on handlerFactory)? Maybe it could be made global by registering on the IHandleResourcesFactory ?

Jul 25, 2011 at 1:52 PM

I was thinking exposing the HttpWebRequest was a not a good idea - leaky abstraction, but know I think it's not a bad thing to do per-sa, so I'll go with what you suggest for and expose the HttpWebRequest via the interface method.

As for placing on the factory, think this is a good idea, I'll still expose on the instance but for easy of use it needs to be on the factory.

Are you using it for only POST methods or GET as well?

And are you using a public accessible API for testing? cos I'll probably want to create a WSSE as an example.

Jul 26, 2011 at 12:57 PM
Edited Jul 26, 2011 at 2:16 PM

I've implemented this feature on both the ResourceClient & Factory - I decided not to expose the HTTPWebRequest, we know expose the request type and arguments.

Change set - http://wp7contrib.codeplex.com/SourceControl/changeset/changes/68284

 

Hopefully the code sample shows how to use:

 

public partial class MainPage : PhoneApplicationPage
    {
        // Constructor
        public MainPage()
        {
            InitializeComponent();
        }

        private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
        {
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            var url = "http://api.bing.net/xml.aspx?AppId=EF79EAD2898E05B37E805F8547919DCA67154CBE&Version=2.2&Market=en-GB&Latitude=51.5561981201172&Longitude=-0.0771760940551758&Radius=10&Query=taxi&Sources=Phonebook&Web.Count=50";

            var factory = new ResourceClientFactory()
                .WithHeader(new MyHeader());

            factory.Create()
                .ForType(ResourceType.Xml)
                .UseUrlForGet(url)
                .Get<XDocument>()
                .Subscribe(result => Debug.WriteLine("SUCCESSFUL!"),
                exception => Debug.WriteLine("MAIN PAGE EXCEPTION - " + exception.Message));
        }
    }

    public class MyHeader : IHeader
    {
        public string Value(object request, params object[] @params)
        {
            return "bytes=0-100000";
        }

        public string Name
        {
            get { return "Range"; }
        }
    }

 

 

This produces the following output in visual studio when using debug build of WP7Contrib:

 

7/26/2011 12:48:19 PM - ResourceClient: Request Url - http://api.bing.net/xml.aspx?AppId=EF79EAD2898E05B37E805F8547919DCA67154CBE&Version=2.2&Market=en-GB&Latitude=51.5561981201172&Longitude=-0.0771760940551758&Radius=10&Query=taxi&Sources=Phonebook&Web.Count=50
7/26/2011 12:48:19 PM - ResourceClient: Request header, name - 'Accept', value - 'application/xml'
7/26/2011 12:48:19 PM - ResourceClient: Request header, name - 'Range', value - 'bytes=0-100000'
7/26/2011 12:48:20 PM - ResourceClient: ProcessResponse, method -  GET
7/26/2011 12:48:20 PM - ResourceClient: Response header, name - 'Content-Type', value - 'text/xml; charset=utf-8'
7/26/2011 12:48:20 PM - ResourceClient: Response header, name - 'X-Akamai-TestID', value - 'cf880bf2295447e4a034ed3d4792a05f'
7/26/2011 12:48:20 PM - ResourceClient: Response header, name - 'Date', value - 'Tue, 26 Jul 2011 12:56:56 GMT'
7/26/2011 12:48:20 PM - ResourceClient: Response header, name - 'Content-Range', value - 'bytes 0-8339/8340'
7/26/2011 12:48:20 PM - ResourceClient: Response header, name - 'Content-Length', value - '8340'
7/26/2011 12:48:20 PM - ResourceClient: Response header, name - 'Connection', value - 'keep-alive'
7/26/2011 12:48:20 PM - ResourceClient: Xml content - 'taxi1000http://www.bing.com/local/default.aspx?what=taxi&where=&s_cid=ansPhBkYp01&mkt=en-gb&ac=false&FORM=SOAPGNGo to top local listings for taxi near G S Mini Cabhttp://www.b7/26/2011 12:48:20 PM - ResourceClient: Deserialization time - 32 ms
SUCCESSFUL!