URI schema constant

Immer wiedermal muss man in seinem Code überprüfen welches Schema die URI (URL) besitzt. Daher sieht man oft solche Codezeilen:if (httpRequest.Uri.Scheme == "https")Ich bin absolut kein Freund von solchen Abfragen, die auf einen String-Wert vergleichen, den man selber schreiben muss. Das ist einfach viel zu Fehleranfällig und verleitet auch durch Copy & Paste und zu schnelles Tippen einfach Fehler zu machen... die Abfrage ist ja auch wirklich zu einfach um länger drüber nachzudenken.Dabei kann man sich das Leben auch einfacher machen und zumindest in meinen Augen auch den Code richtig schreiben durch die Verwendung einer Konstanten aus dem .NET Framework. Die Klasse URI besitzt nämlich schon bereits diese Konstanten!Der Code von oben sieht dann plötzlich wie folgt aus:if (httpRequest.Uri.Scheme == Uri.UriSchemeHttps)Hier einen Auszug der vordefinierten Werte:UriSchemeFtp: URI für  FTP (File Transfer Protocol).UriSchemeHttp: URI für HTTP (Hypertext Transfer Protocol) UriSchemeHttps: URI für HTTPS (Secure Hypertext Transfer Protocol).UriSchemeMailto: Gibt an, dass der URI eine E-Mail-Adresse ist und der Zugriff über SMTP (Simple Mail Transport Protocol) erfolgt.UriSchemeNetPipe: Gibt an, dass auf den URI über das von Windows Communication Foundation (WCF) verwendete NetPipe-Schema zugegriffen wird.UriSchemeNetTcp: Gibt an, dass auf den URI über das von Windows Communication Foundation (WCF) verwendete NetTcp-Schema zugegriffen wird.UriSchemeNntp: URI für eine Internetnewsgroup, auf die über NNTP (Network News Transport Protocol) zugegriffen wirdDetails gibt es hier.

WCF OperationContract nicht optionale Prameter im Wsdl

Wenn man mit der WCF einen Service definiert und dabei Nachrichtenbasiert kommunizieren möchte, sieht kann die Definition z.B. so aussehen:[ServiceContract(Namespace = APICommon.DefaultSOAPNameSpace)]public interface ICartSoapService{ [OperationContract] ProcessCartResponse ProcessCart(ProcessCartRequest request);}[DataContract]public class ProcessCartRequest{ [DataMember(IsRequired = true)] public Cart Cart { get; set; }}[DataContract]public class ProcessCartResponse : BaseResponseMessage{ [DataMember(IsRequired = true)] public ProcessResultType ProcessResult { get; set; }}Die WCF erzeugt auch brav eine passendes Wsdl Datei für diese Beschreibung. Allerdings hat die "Standardausgabe" der WCF datei den Nachteil, das der Parameter "request" der Methode ProcessCart immer optionaler Parameter ist bzw. in der Wsdl Datei wird das Element mit den Attribute minOccurs="0" gekennezichnet. Da ohne den Parameter die Methode aber nicht vernüftig abgearbeitet werden kann, müsste im Wsdl eigentlich ein minOccurs="1" stehen ...also kein optionaler Parameter. Leider gibt es bei den Standardattributen der WCF keine Möglichkeit, diese Verhalten oder viel mehr die Wsdl-Generierung zu beinflussen. Trotzdem kann man durch ein eigenes Attribute das gewünschte Verhalten sehr schnell der WCF beibringen.Dafür muss man lediglich ein Attribute anlegen und die Interfaces IContractBehavior + IWsdlExportExtension mit hinzufügen. Die vollständige Implementierung sieht so aus: [AttributeUsage(AttributeTargets.Interface)] public class OperationsParametersAreRequiredAttribute : Attribute, IContractBehavior, IWsdlExportExtension { private List<RequiredOperationParameter> _requiredOperationParameters; public void AddBindingParameters( ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior( ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime) { } public void ApplyDispatchBehavior( ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.DispatchRuntime dispatchRuntime) { } public void ExportContract( WsdlExporter exporter, WsdlContractConversionContext context) { if (_requiredOperationParameters == null) _requiredOperationParameters = new List<RequiredOperationParameter>(); foreach (var operation in context.Contract.Operations) { var requestMessage = operation.Messages.Where(m => m.Direction == MessageDirection.Input).FirstOrDefault(); var parameters = operation.SyncMethod.GetParameters(); Debug.Assert(parameters.Length == requestMessage.Body.Parts.Count); for (var iLoop = 0; iLoop < parameters.Length; iLoop++) { var attributes = parameters[iLoop].GetCustomAttributes( typeof(OperationParameterIsOptionalAttribute), false); if (attributes.Length == 0) { _requiredOperationParameters.Add(new RequiredOperationParameter { Namespace = requestMessage.Body.Parts[iLoop].Namespace, Name = requestMessage.Body.Parts[iLoop].Name, Message = operation.Name }); } } } } public void ExportEndpoint( WsdlExporter exporter, WsdlEndpointConversionContext context) { foreach (var requiredParamter in _requiredOperationParameters) { var schemas = exporter.GeneratedXmlSchemas.Schemas(requiredParamter.Namespace); foreach (XmlSchema schema in schemas) { var message = schema.Elements[requiredParamter.XmlName] as XmlSchemaElement; var complexType = message.ElementSchemaType as XmlSchemaComplexType; var sequence = complexType.Particle as XmlSchemaSequence; foreach (XmlSchemaElement schemaElement in sequence.Items) { if (schemaElement.Name == requiredParamter.Name) { schemaElement.MinOccurs = 1; schemaElement.MinOccursString = "1"; break; } } } } _requiredOperationParameters.Clear(); } public void Validate( ContractDescription contractDescription, ServiceEndpoint endpoint) { } internal class RequiredOperationParameter { public string Message { get; set; } public string Name { get; set; } public string Namespace { get;set;} public XmlQualifiedName XmlName { get { return new XmlQualifiedName(Message, Namespace); } } } }

WCF REST Could not load file or assembly 'System.ServiceModel.Activation'

Bei dem Versuch HTTP Basic Auth für einen REST Dienst zu implementieren (der wiederrum durch Konfiguration per serviceActivations in der .config und eigenere factory gestartet wird) bekam ich die Fehlermeldung bei der Umstellung vom Attribute aspNetCompatibilityEnabled von "false" auf "true"  das die Assembly 'System.ServiceModel.Activation' nicht gefunden werden konnte. Hier die genaue Fehlermeldung: System.IO.FileNotFoundException: Could not load file or assembly 'System.ServiceModel.Activation' or one of its dependencies. The system cannot find the file specified.File name: 'System.ServiceModel.Activation'   at System.RuntimeTypeHandle.GetTypeByName(String name, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMarkHandle stackMark, Boolean loadTypeFromPartialName, ObjectHandleOnStack type)   at System.RuntimeTypeHandle.GetTypeByName(String name, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMark& stackMark, Boolean loadTypeFromPartialName)   at System.Type.GetType(String typeName, Boolean throwOnError, Boolean ignoreCase)   at System.Web.Compilation.BuildManager.GetType(String typeName, Boolean throwOnError, Boolean ignoreCase)   at System.Web.Configuration.HandlerFactoryCache.GetTypeWithAssert(String type)   at System.Web.Configuration.HandlerFactoryCache.GetHandlerType(String type)   at System.Web.Configuration.HandlerFactoryCache..ctor(String type)   at System.Web.HttpApplication.GetFactory(String type)   at System.Web.HttpApplication.MaterializeHandlerExecutionStep.System.Web.                        HttpApplication.IExecutionStep.Execute()   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)=== Pre-bind state information ===LOG: User = userLOG: DisplayName = System.ServiceModel.Activation (Partial)WRN: Partial binding information was supplied for an assembly:WRN: Assembly Name: System.ServiceModel.Activation | Domain ID: 2WRN: A partial bind occurs when only part of the assembly display name is provided.WRN: This might result in the binder loading an incorrect assembly.WRN: It is recommended to provide a fully specified textual identity for the assembly,WRN: that consists of the simple name, version, culture, and public key token.WRN: See whitepaper http://go.microsoft.com/fwlink/?LinkId=109270 for more information and common solutions to this issue.LOG: Appbase = file:///xxxxx/LOG: Initial PrivatePath xxxx\binCalling assembly : (Unknown).===LOG: This bind starts in default load context.LOG: Using application configuration file: xxxx\web.configLOG: Using host configuration file: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet.configLOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).LOG: Attempting download of new URL file:///C:/Windows/Microsoft.NET/Framework64/v4.0.30319/Temporary ASP.NET Files/root/3d7bd35f/452e5631/System.ServiceModel.Activation.DLL.LOG: Attempting download of new URL file:///C:/Windows/Microsoft.NET/Framework64/v4.0.30319/Temporary ASP.NET Files/root/3d7bd35f/452e5631/System.ServiceModel.Activation/System.ServiceModel.Activation.DLL.LOG: Attempting download of new URL file:///xxxx/bin/System.ServiceModel.Activation.DLL.LOG: Attempting download of new URL file:///Dxxxx/bin/System.ServiceModel.Activation/System.ServiceModel.Activation.DLL.LOG: Attempting download of new URL file:///C:/Windows/Microsoft.NET/Framework64/v4.0.30319/Temporary ASP.NET Files/root/3d7bd35f/452e5631/System.ServiceModel.Activation.EXE.LOG: Attempting download of new URL file:///C:/Windows/Microsoft.NET/Framework64/v4.0.30319/Temporary ASP.NET Files/root/3d7bd35f/452e5631/System.ServiceModel.Activation/System.ServiceModel.Activation.EXE.LOG: Attempting download of new URL file:///xxxx/bin/System.ServiceModel.Activation.EXE.LOG: Attempting download of new URL file:///xxxxx/bin/System.ServiceModel.Activation/System.ServiceModel.Activation.EXE. - Thread: 15Das Problem liegt an einem Eintrag in der web.config, denn dort hatte ich unter system.webserver -> handlers folgenden Eintrag hinzugefügt:<add name="svc" path="*.svc" verb="*" type="System.ServiceModel.Activation.ServiceHttpHandlerFactory, System.ServiceModel.Activation"/>Nach dem Ändern bzw. Ergänzen diesers Eintrages: <add name="svc" path="*.svc" verb="*" type="System.ServiceModel.Activation.ServiceHttpHandlerFactory,System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="integratedMode,runtimeVersionv4.0" />Funktioniert auch wieder die Aktivierung vom WCF basierten REST Service.

WCF Http API REST XML Custom ErrorHandler

Im Augenblick entwickel ich eine Architektur für eine WEB-API basierend auf der WCF. Als Grundlage habe ich das WCF Http Projekt (das auf Codeplex zu finde ist) genommen. Mir gefällt dort insbesondere der Ansatz der MediaTypeProcessor, womit sich die Request- und Response-Formate sehr schön beeinflussen lassen.Ein wichtier Aspekt bei jeder Architektur ist das Thema Exceptionhandling. Leider gibt es für das oben genannte Projekt noch keinen eigenen Exceptionhandler, der auch die MediaTypeProcessors nutzt, um eine Exception im angeforderten Format zurück zu geben. Das kann z.B. XML, Json aber theoretisch auch ein Bild oder Wav Dateien sein. Daher habe ich heute mal einen Exceptionhandler geschrieben, der mit dem Projekt zusammenarbeitet. Als Basis wird hierbei natürlich das Interface der WCF IErrorHandler genutzt und eine generelle Basisimplementierung aus dem Projekt.Ich bin noch nicht 100% glücklich mit der Lösung aber im Augenblick funktioniert das so ganz gut. Werde die Implementierung auch auf Codeplex posten und hoffe dort vielleicht weitern Input zu finden. Aber auch per E-Mail freue ich mich über konstruktive Beiträge!Here we go: public class RESTMessageErrorHandler : HttpMessageErrorHandler, IErrorHandler { // Public Methods #region HandleError public override bool HandleError( Exception error) { Logging.Error("API Exception", error); return true; } #endregion // Protected Methods #region ProvideResponse protected override void ProvideResponse( Exception exception, Microsoft.Http.HttpResponseMessage response) { APIBaseException apiException = null; if (exception is APIBaseException) apiException = exception as APIBaseException; else apiException = new APIBaseException(System.Net.HttpStatusCode.InternalServerError,  "An error has occured processing your request."); var supportedMediaTypes = new List<string>(); var httpMessageProperty = OperationContext.Current. IncomingMessageProperties[HttpMessageProperty.Name] as HttpMessageProperty; var httpRequest = httpMessageProperty.Request as HttpRequestMessage; var contentType = httpRequest.Headers.ContentType; var uriMatch = httpRequest.Properties.First( p => p.GetType() == typeof(UriTemplateMatch)) as UriTemplateMatch; var endpoint = OperationContext.Current.Host.Description.Endpoints.Find( OperationContext.Current.EndpointDispatcher.EndpointAddress.Uri); var dispatchOperation = OperationContext.Current.EndpointDispatcher.DispatchRuntime. Operations.Where(op => op.Name == uriMatch.Data).First(); var operationDescription = endpoint.Contract.Operations.Find(dispatchOperation.Name); //get the contenttype of the request var httpBehavoir = endpoint.Behaviors.Find<HttpEndpointBehavior>(); var processors = httpBehavoir.GetResponseProcessors( operationDescription.ToHttpOperationDescription()).ToList<Processor>(); //Fallback for empty contenttype if (string.IsNullOrEmpty(contentType)) { foreach (var processor in processors) { var mediaTypeProcessor = processor as MediaTypeProcessor; if (mediaTypeProcessor == null) continue; supportedMediaTypes.AddRange(mediaTypeProcessor.SupportedMediaTypes); } contentType = ContentNegotiationHelper.GetBestMatch(httpRequest.Headers.Accept.ToString(),  supportedMediaTypes).MediaType; if (string.IsNullOrEmpty(contentType)) contentType = "text/plain"; } //set http-header and status code response.Headers.ContentType = contentType; response.StatusCode = apiException.Status; //search processor for the output-serialization foreach (var processor in processors) { var mediaTypeProcessor = processor as MediaTypeProcessor; if (mediaTypeProcessor == null) continue; if (mediaTypeProcessor.SupportedMediaTypes.Contains<string>(contentType)) { response.Content = HttpContent.Create(s => mediaTypeProcessor. WriteToStream(new APIExceptionContract(apiException), s, httpRequest)); break; } } //if no processor found use plain text if (response.Content == null) response.Content = HttpContent.Create(apiException.Description); } #endregion }

HttpRequestValidationException 0x80004005 A potentially dangerous Request.Form value was detected from the client

Bei der Umstellung eines ASP.NET Projektes auf das Framework 4.0 wurde bei bestimmten Eingabedaten immer der Fehler geworfen:System.Web.HttpRequestValidationException (0x80004005): A potentially dangerous Request.Form value was detected from the client (_dataTextBox="...bitkarte (<print template="pay...").   at System.Web.HttpRequest.ValidateString(String value, String collectionKey, RequestValidationSource requestCollection)   at System.Web.HttpRequest.ValidateNameValueCollection(NameValueCollection nvc, RequestValidationSource requestCollection)   at System.Web.HttpRequest.get_Form()Dieses konnte man unter ASP.NET 2.0 durch ein @pagedirektive unterdrücken bzw. die Filterung ausschalten. Bei ASP.NET 4.0 ist dieses per @pagedirektive aber nicht mehr per Default möglich, es gibt aber die Möglichkeit die RequestValidierung per Eintrag in die web.config wieder in den Modus "ASP.NET 2.0" zu versetzen, damit das Verhalten gleich bleibt. Dafür ergänzt man die web.config wie folgt:<httpRuntime requestValidationMode="2.0" /> Weiter Informationen gibt es in der MSDN.