Hands-On Network Programming with C# and .NET Core
上QQ阅读APP看书,第一时间看更新

State management methods

I'd encourage you to try to sort the methods I've listed into the two categories I'm about to describe for you. And in the future, I'd encourage you to try to categorize interfaces and public class definitions in this way. Doing so will improve your ability to read and internalize new software features quickly, and leverage them efficiently, instead of copying code snippets from StackOverflow.com until you find something that works. That said, let's take a look at the state management functions.

First, we have the Create methods. Each of these methods will return a usable instance of a concrete WebRequest sub-class. They're all static, and so can be invoked from the class definition without first needing to create an instance of it (for obvious reasons; why would you need to create an instance of a class to then create an instance of a class?). Depending on the specific method used, this sets up an instance of the default sub-class for the given scheme specified in the URI supplied to the method. So, if we wanted instances of WebRequest for accessing data from a RESTful HTTP service, collecting files from a designated FTP server, and reading data from a remote file system, we could do all of this with a simple call to Create(uriString):

var httpRequest = WebRequest.Create("http://test-domain.com");
var ftpRequest = WebRequest.Create("ftp://ftp.test-domain.com");
var fileRequest = WebRequest.Create("file://files.test-domain.com");

You may recognize this code from the SubmitRealEstateRequest sample method we wrote in the Class properties section. I didn't explain it until now, but because the class is so clearly and simply defined, I expect you were able to infer its use just fine from my code without this explanation. But in case you were wondering why it seemed like I was creating an instance of an abstract class (a compile-time error in C#), that's why. I was actually requesting an instance of an appropriate sub-class from the abstract base-classes, static definition.

Those three use cases in the preceding code block cover just about everything you can do with Create() out of the box, but that certainly doesn't mean those are the only use cases Create() can apply to. The functionality uses common protocol prefixes for URIs to determine default sub-classes to instantiate. So, simply passing http://test-domain.com to the method is all the default implementation needs to then return an instance of the HttpWebRequest class. The same logic that allows the Uri class to parse the preceding string is used to tell WebRequest which protocol it should be creating a sub-class for.

As I said, though, the default behavior is only defined for a limited set of use cases out of the box. There are four specific protocols whose concrete sub-classes are preregistered with the WebRequest class at runtime; they are as follows:

  • http://
  • https://
  • ftp://
  • file://

So, any URI string given to the Create method with any of these four prefixes as the first characters in the string will be reliably handled by the WebRequest base class. And since the base class provides a sufficient interface for executing the core operations of its sub-classes, you don't even have to know specifically what sub-class was returned. Thanks to type inheritance, you can just declare your instance as being of type WebRequest, and use it accordingly, just like I did in the sample method from earlier.

But what if you don't want to work with one of these four preregistered types? What if you wrote your own custom WebRequest sub-class specifically for working with a WebSocket (WS) protocol, and you'd like to get the same support from WebRequest just by passing in a URI with the WebSocket prefix of ws://? Well, that exact use case leads us to another state management method: RegisterPrefix(string, IWebRequestCreate).

RegisterPrefix is a powerful new tool that supports what's known as pluggable protocols. It's basically a way for you to incorporate custom implementations and sub-classes of the WebRequest and WebResponse base classes into the runtime of your application. When done properly, your custom code can be treated as a first-class citizen in the System.Net namespace, being appropriately delegated to by system types and methods, and having full access to the network stack, just like the native library classes you'll be learning about next.

The scope and depth of fully implementing a custom protocol handler are beyond this chapter, and will be explored in more detail later in this book. For now though, just know that once the work of writing a custom protocol handler is completed, wiring it in is as simple as calling RegisterPrefix. That's why this falls under the domain of state management methods; because it's about configuring the working conditions of WebRequest for the duration of your application's runtime.

The method returns a bool to indicate the success or failure of your attempt to register your custom protocol, and throw or process exceptions accordingly. So, while the process of setting up a pluggable protocol is outside the scope of this chapter, for now just trust that, once the work is done, configuring it as part of the valid state of the WebRequest class is a straightforward affair:

if(!WebRequest.RegisterPrefix("cpf://", new CustomRequestCreator())) {
throw new WebException("Failure to register custom prefix protocol handler.");
}

And with that, we have every tool we need to properly configure and initialize network requests. State management is complete, and all that's left is to begin submitting requests and processing responses.