| Typ/Viewer: RSS/ RSS-Reader | Aktualisiert: 10.02.2012 | Aufrufe: 758 |
Kategorie: Computer, Technik > Programmierung & Programmiersprachen
NET Software Entwicklung
DotNetNuke Spezialist
DNN
E-Commerce
Business-Develoment
Online-Marketing
Nachrichten aus dem RSS-Feed: NET - Software & DotNetNuke (DNN) Blog
DotNetNuke speichern von Zugangsdaten der Benutzer(Tue, 22 Nov 2011 17:29:45 GMT) DotNetNuke bietet drei Möglichkeiten die Passwörter von Benutzern zu speichern. Diese können im Klartext, als verschlüsselte Zeichenfolge oder als Hashwert in der Datenbank gespeichert werden. Das die erste Variante höchtens für Test- und Entwicklungsinstallationen in Frage kommt, muss wohl nicht weiter diskutiert werden. Konfiguiert wird die Behandlung von Passwörtern in der web.config direkt bei den Einstellungen des MembershipProvider:
<add
name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="SiteSqlServer" enablePasswordRetrieval="true" enablePasswordReset="true" requiresQuestionAndAnswer="false" minRequiredPasswordLength="7" minRequiredNonalphanumericCharacters="0" requiresUniqueEmail="false" passwordFormat="Encrypted" applicationName="DotNetNuke" />
Worin besteht aber der genau Unterschied zwischen veschlüsselt und hashed?
Verschlüsselte Zeichenfolgen
Bei diesem Modus werden die Passwörter durch einen Verschlüsselungsalgorithmus unleserlich
gemacht, so das bei einem Auslesen der Tabelle, die Passwörter nicht im Klartext zur
Verfügung stehen. DotNetNuke bzw. jedes DNN-Module ist in der Lage die Passwörter
ohne Probleme zu lesen.
Hashwerte
Wenn man als Modus "Hashed" wählt, dann wird nur ein Hashwert vom Passwort abgespeichert
und nicht das eigentliche Passwort. Aus dem Hashwert kann man das Passwort nicht mehr
in Klartext umwandeln und somit hat auch kein Module mehr Zugriff auf die Passwörter.
Das bedeutet aber auch, dass die "Passwort zusenden"-Funktion nicht mehr das Passwort
dem Benutzer zur Verfügung stellen kann. Ob das eine Anwendung überhaupt machen sollte
ist eine ganz andere Diskussion. Die Funktion "Passwort vergessen" funktioniert aber
natürlich trotzdem, in diesem Modus wird von DotNetNuke einfach ein neues Zufallspasswort
erzeugt.
Welche Variante
Es gibt keine klare Empfehlung für eine der verfügbaren Methode. Für die Auswahl sollten
im Vorfeld Überlegungen angestellt werden, ob man das Passwort wirklich jemals im
Klartext benötigt. Aus der Perspektive eines Datenschützers wäre eigentlich nur die
Möglichkeit "als Hashwert" akzeptable.
DotNetNuke Performance Optimierung(Thu, 17 Nov 2011 11:11:25 GMT) Die Performance einer Webseite ist heute aus vielen Gründen - die hier nicht weiter aufgeführt werden müssen - wichtig. DotNetNuke selber bietet eine Vielzahl von Einstellungen, die direkten Einfluss auf die Performance der Webseite haben können. Es gibt auf Codeplex ein Projekt, dass durch ein paar SQL Scripts die Einstellungen einer DotNetNuke Installation so modifiziert, das eine maximale Performance möglich ist.
Natürlich muss man im Einzelfall über manche Einstellung extra entscheiden aber als
Startpunkt für eine Optimierung finde ich das sehr gelungen.
Das Projekt findet man unter http://dnnperformance.codeplex.com
Die web.config bei der Installation eines Modules verändern(Thu, 27 Oct 2011 17:04:00 GMT) Je nach Modul gibt es schon mal die Anforderung, dass neue Einträge in die web.config geschrieben werden müssen. Dieses kann man entweder im SourceCode erledigen oder aber die Änderungen in dem DNN Modul Manifest / Definitionsdatei (meinmodule.dnn) definieren. Verfügbar ist das ab der Version 5 von DotNetNuke. Um das zu nutzen, muss man in der Manifest-Datei folgendes hinzufügen:
<component
type="Config">
<config> <configFile>web.config</configFile> <install> <configuration>
<nodes> .....
</nodes> </configuration> </install> <uninstall> <configuration>
<nodes /> </configuration> </uninstall> </config> </component>Wie
man sieht gibt es zwei Bereiche. Der Bereich <install> wird während der Installation
und der Bereich <uninstall> wird be der Deinstallation von dem DNN Modul ausgeführt.
Innerhalb des Tags <nodes> können dann die entsprechenden Einträge hinzugefügt
werden. Hier ein Beispiel womit ein HttpHandler hinzugefügt wird:
<node
path="/configuration/system.web/httpHandlers" action="update"
key="path" collision="overwrite">
<add verb="*" path="myhandler.axd" validate="false"
type="DNN.Module.Shop.MyHandler,
DNN.Module.Shop" /> </node>
Weiter Informationen findet man im Wiki auf www.dotnetnuke.com
Conditional Stylesheets oder CSS hacks(Wed, 19 Oct 2011 13:43:52 GMT) Jeder der sich schon mal mit dem Thema Webdesign beschäftigt hat, kennt die Probleme der unterschiedlichen Browser. Besonders der Internet Explorer ist ein Kandidat, bei dem man sehr schnell graue Haare bekommt kann.
Meistens werden Hacks verwendet, die nur von bestimmten Versionen / Browsern erkannt
bzw. akzeptiert werden und von den anderen als Fehler ignoriert.
Das Problem von solchen Hacks ist unter anderem dafür sorgen das eine CSS nicht mehr
validiert werden kann. Um dieses Problem zu umgehen verwenden viele Conditional Stylesheets,
die dann vom jeweiligen Browser erkannt und geladen werden. Das sieht dann so aus:
<link rel="stylesheet" type="text/css" media="screen" href="skin/css/style.css" />
<!--[if IE
7]><link rel="stylesheet" type="text/css" media="screen" href="skin/css/ie7.css" /><
![endif]--> <!--[if IE
6]><link rel="stylesheet" type="text/css" media="screen" href="skin/css/ie6.css" /><
![endif]-->Nun kann man für die verschiedenen Browser(versionen) die
entsprecheden CSS-Definitionen sauber überschreiben. Ist doch super oder?
Diese Variante ist schon deutlich besser als die Verwendung von Hacks im eigentlichen
CSS aber hat den großen Nachteil das noch mehr Dateien vom Server geladen werden müssen.
Eine deutlich besser Lösung sieht so aus:
<!--[if lt
IE 7 ]> <html class="ie6">
<![endif]--> <!--[if IE
7 ]> <html class="ie7">
<![endif]--> <!--[if IE
8 ]> <html class="ie8">
<![endif]--> <!--[if IE
9 ]> <html class="ie9">
<![endif]--> <!--[if (gt
IE 9)|!(IE)]><!-->
<html class="">
<!--<![endif]-->Hier wird nun in Abhängigkeit von der
Browserversion das Tag HTML mit einer CSS-Klasse versehen und man kann nun ganz sauber
und ohne Hacks für die div. Versionen CSS-Definitionen erstellen, dass dann z.B. so
aussieht::
div.contentpane { width: 510px; }
.ie6 div.contentpane { width: 500px; }
Diese Technik wird z.B. auch vom bekannten http://html5boilerplate.com/ verwendet.
URI schema constant (Thu, 04 Aug 2011 08:38:59 GMT) 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 wird
Details
gibt es hier.
ASP 0131 Unzulaessiger Pfad zum uebergeordneten Verzeichnis(Sun, 31 Jul 2011 12:12:56 GMT) Bei dem Versuch eine alte ASP (classic asp) Anwendung auf einem Windows 2008 R2 System und damit auf einem IIS 7.5 funktionsfähig zu bekommen bestand das Problem das die Seite immer nur einen HTTP Errorcode 500 zurück gibt. Einen Blick in die Logs von IIS ergab die Fehlermeldung:
"|23|ASP_0131|Unzulässiger_Pfad_zum_übergeordneten_Verzeichnis"
Ursache dieses Problems ist das Standardmäßig der übergeordneten ASP-Pfade für eine
Website oder Anwendung bei gleichzeitiger Verwendung relativer übergeordneter Pfade
in einer Include-Anweisung nicht erlaubt / deaktiviert ist.
Empfohl ist die Einbindungen von
<!--#include file="../dbconn.inc"-->
auf
<!--#include virtual="/<virtual path>/dbconn.inc"-->
zu verändern.
Alternativ kann man aber auch den Internet Information Server so einstellen, dass
die übergeordneten ASP-Pfade erlaubt sind.
Dafür muss man beim IIS auf das entsprechende Web klicken und in der rechten Seite
unter ASP die Konfiguration öffnen. Dort gibt es dann die Option "Übergeordnete
Pfade aktivieren" der Wert muss von False auf True umgestellt werden.
Microsoft SQL Server Error: 15138 drop user account(Wed, 22 Jun 2011 22:16:40 GMT) Gerade habe ich eine ältere Sicherung in den Mircosoft SQL Express 2008 eingespielt. Dabei wollte ich ein wenig aufräumen und die nicht benötigten Benutzerkonten löschen. Jedoch habe ich bei dem Versuch immer die Fehlermeldung
"The database principal owns a schema in the database, and cannot be dropped. (Microsoft
SQL Server, Error: 15138)."
bekommen. Die Lösung für dieses Problem ist recht einfach: Wenn für den Benutzeraccount
ein Schema angelegt wurde, muss zunächst das Schema aus der SQL Server Datenbank entfernt
werden. Um zu überprüfen, ob es ein Schema für den Benutzer gibt, kann man mit dem
SQL Server Manager bei der entsprechenden Datenbank unter Sicherheit => Schema
nachschauen. Wenn kein Schema mehr von diesem Benutzer existiert, lässt sich auch
das Benutzerkonto ohne Fehlermeldung löschen.
API REST Html-Helppage display Json requestsample (Tue, 15 Mar 2011 10:13:49 GMT)
Für mein aktuelles API Projekt erstelle ich eigene Helppages auf denen ich
auch Requestbeispiele für Xml und Json anzeige. Die Erstellung eines Request-Beispiel
wird durch Xsd erstellt und das resultierende Xml wird dann durch Json.NET in
Json konvertiert.
Der Aufruf dazu sah wie folgt aus:
var
jsonSample = JsonConvert.SerializeXNode(xmlSampelRequest);
Als Ergebnis wurde auch Json ausgebeben, leider aber ohne Zeilenumbrüche, was die
Darstellung und Lesbarkeit doch stark reduziert. Damit der Json-String auch "vernüftig"
formatiert wird, kann man bei der Serializierung noch einen Parameter setzen und damit
die Formatierung erzwingen: Formatting.Indented
Der Aufruf sieht dann wie folgt aus:
var
jsonSample = JsonConvert.SerializeXNode(xmlSampelRequest,
Newtonsoft.Json.Formatting.Indented);
Automapper custom TypeConverter Exceptionhandling (Fri, 11 Mar 2011 12:44:49 GMT) In einem Projekt verwende ich aktuell die Komponente AutoMapper (automapper.codeplex.com),
im zwischen den externen Datacontracts und den internen Entities zu mappen. Teilweise
habe ich dafür auch eigenen TypeConverter entwickelt. Wenn man in diesem TypeConverter eine
eigene Exception auslöst, dann erhält man beim aufrufenden Code immer eine AutoMapperMappingException.
Da ich in dem Projekt ein globales ErrorHandling habe (WCF bzw. Implementierung
vom IErrorHandler), möchte ich aber die konkrete Exception gerne aus dem TypeConverter
in mein Errorhandling weitergeben.
Damit das funktioniert baut man sich am besten einen Wrapper für den Aufruf vom Automapper,
um dort zu entscheiden, welche Exception man weitergeben möchte. Die eigene Exception
wird als InnerException vom AutoMapperMappingException mitgeliefiert.
Durch überprüfung der InnerException kann man als feststellen, wie die aktuelle Exception
zu bahandeln ist.
Hier ein Beispiel:
public class AutoMapperWrapper
{ public static U
Map<T, U>( T source) { try { return Mapper.Map<T,
U>(source); } catch (Exception
e) { while (e is AutoMapperMappingException)
e = e.InnerException; if (e
== null) throw; else throw e;
} } }
WCF OperationContract nicht optionale Prameter im Wsdl (Wed, 02 Mar 2011 13:53:16 GMT) 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' (Wed, 19 Jan 2011 10:44:55 GMT) 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 = user
LOG: DisplayName = System.ServiceModel.Activation
(Partial)
WRN: Partial binding information was supplied for an assembly:
WRN: Assembly Name: System.ServiceModel.Activation | Domain ID: 2
WRN: 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\bin
Calling assembly : (Unknown).
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: xxxx\web.config
LOG: Using host configuration file: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet.config
LOG: 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: 15
Das 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(Fri, 07 Jan 2011 13:59:38 GMT) 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(Fri, 07 Jan 2011 08:44:20 GMT) 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.
HTTP Error 404.11 IIS7 verarbeitet keine URLs mit + (Wed, 05 Jan 2011 17:17:50 GMT) Bei dem IIS 7.5 hatte ich gerade das Problem, dass eine Url die Whitespaces als
"+" codiert (auch wenn es nichht optimal ist aber leider nicht zu ändern aktuell)
immer mit dem Fehler http Statuscode 404.11 und der Nachricht The
request filtering module is configured to deny a request that contains a double escape
sequence. beendet wurde.
Der Grund für die Ablenhung der Url ist, dass "+" als ein gefährliches Zeichen eingestuft
wird. Hierbei handelt es sich also um eine Sicherheitseinstellung.
Wenn man nun solche Urls aber weiter verarbeiten möchte, dann kann man diesen Requestfilter
einfach deaktivieren über folgenden Aufruf:
%windir%System32\inetsrv>appcmd set config -section:system.webServer/security/requestfiltering
-allowDoubleEscaping:true
Weitere Informationen zum RequestFiltering gibt es auch hier IIS
Security RequestFiltering.
Install ASP.NET 1.1 with IIS7 on Windows 2008 (Tue, 04 Jan 2011 12:54:47 GMT) In einer Evaluierungsphase habe ich gerade mal probiert eine alte ASP.Net 1.1 Anwendung
auf einem Windows Server 2008 zu installieren. Per Default wird allerdings ASP.NET
1.1 nicht mehr auf einem Windows 2008 Server unterstützt, so das ein wenig manuelle
Arbeit notwendig ist.
Als erstes muss man die "IIS Metabase Compatibility" installieren und das geht
durch die Schritte: "Start" -> "Server Manager" -> "Manage Roles" ->
"Web Server (IIS)" -> "Add Role Services".
Der zweite Schritt ist die Installation vom .Net Framework 1.1. Hier die Downloadlinks:
.NET
Framework Version 1.1 Redistributable Package
.NET
Framework Version 1.1 Service Pack 1
ASP.NET
Security Update for .NET Framework 1.1 SP1
Wenn man das Setup ausführt, dann bekommt man vom Windows Server 2008 den Hinweis
das es möglicherweise Kompatibilitätsprobleme geben könnte. Diese Meldung einfach
mit "Ausführen" / "run programm" überspringen.
Anschließend muss das alte .Net Framework noch im IIS registriert werden. Dafür kann
man folgenden Behfel ausführen:
%windir%\Microsoft.NET\Framework\v1.1.4322\aspnet_regiis -enable
Ein Änderung in der machine.config (unter %windir%\Microsoft.NET\Framework\v1.1.4322\config\machine.config)
muss noch gemacht werden. Vor dem schließenden Tag </configSection> muss folgendes
hinzhugefügt werden:
<section name="system.webServer" type="System.Configuration.IgnoreSectionHandler,
System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
/>
Einen neue ApplicationPool - der unter .Net 1.1 läuft - kann man nun mit folgendem
Befehl anlegen:
%windir%\system32\inetsrv\appcmd add apppool /name:"Pool1-1" /managedRuntimeVersion:"v1.1"
Bei einem 64bit Betriebsystem muss man nun noch ein Verzeichnis erstellen:
md %windir%\Microsoft.net\Framework64\v1.1.4322\config
und
copy %windir%\Microsoft.net\Framework\v1.1.4322\Config\machine.config %windir%\Microsoft.net\Framework64\v1.1.4322\config\machine.config
IIS Manager Error: The process cannot access the file because it is being used by another process. (Exception from HRESULT: 0×80070020)(Thu, 30 Dec 2010 10:31:54 GMT) Heute habe ich versucht bei mir lokal ein SSL Zertifikat zu installieren. So etwas habe ich nicht zum ersten mal gemacht aber heute wollte es einfach nicht funktionieren. Immer wenn ich den IIS entsprechend konfiguriert hatte (Zertifikat importiert, Binding bzw. Hostheader erstellt, etc.) kam beim Versuch den IIS zu starten immer die Meldung:
IIS Manager Error: The process cannot access the file because it is being used
by another process. (Exception from HRESULT: 0×80070020)
Nach der Analyse habe ich dann festgestellt, dass ein Prozess bereits den Port 443 nutzt
und daher der IIS mit einem https-Binding natürlich nicht mehr richtig starten
konnte. Herausfinden welcher Port aktuell genutzt wird kann man im übrigen über "netstart
-ano" im Cmd-Prompt. Der Befehl "Tasklist" hat mir dann den notwendigen Hinweis gegeben. Skype ist
der "Bösewicht" der den Port 443 nutzt. Dieses kann man aber per Konfiguration
ausschalten.
Danach funktioniert auch der Start vom IIS mit einem htts-Binding
WCF URL Rewriting entfernen der Endung "svc"(Tue, 26 Oct 2010 12:21:09 GMT) Bei dem Standardverhalten, wenn ein WCF Service über den IIS veröffentlicht
wird ist, dass der Endpunkt die Datei von Service ist mit der Dateiendung ".svc".
Dieses sieht aber - gerade im Bezug auf ein REST-basiertes System nicht besonders
elegant aus.
Bei dem Einsatz von IIS 7.0 ist es sehr einfach die Dateiendung zu entfernen,
ohne dabei viel Code zu schreiben. Für das URL Rewriting gibt eine Erweiterung,
die man dem IIS 7.0 hinzufügen kann. Dadurch ist es sehr schön möglich z.B. über Regular
Expressions ein URL Rewriting durchzuführen.
Nach der Installation dieser Erweiterung muss
nur noch folgende Regel definiert werden:
1. Name: Entfernen der Dateiendung svc
2. Request URL: Matches the pattern
3. Using: Regular Expressions
4. Pattern: ^([0-9a-zA-Z\-]+)/([0-9a-zA-Z\-\.\/\(\)]+)
5. Ignore Cases: true
6. Action Type: Rewrite
7. Rewrite URL: {R:1}.svc/{R:2}
8. Append Querystring: true
Fertig ;-)
Per Eintrag in der web.config sieht das dann alternativ so aus:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<rewrite>
<rules>
<rule name="Remove Svc Extension">
<match url="^([0-9a-zA-Z\-]+)/([0-9a-zA-Z\-\.\/\(\)]+)" />
<action type="Rewrite" url="{R:1}.svc/{R:2}" />
</rule>
</rules>
</rewrite>
</system.webServer>
WCF WSDL replace http://tempuri.org (Tue, 26 Oct 2010 09:48:45 GMT) Jeder Webservice sollte einen eindeutigen Namespace verwenden, um im Web auf jeden Fall eindeutig zu sein. Als Standardnamespace wird von ASP.NET Webservices (und auch von der WCF) folgender Namespace verwendet: http://tempuri.org
Dieses sollte als erstes modifiziert werden, damit man erst gar nicht mit einem solchen
Namespace online geht.
Das Problem bei der WCF ist nun leider, dass der Namespace an drei verschiedenen Stellen
angegeben werden muss, damit auch im WSDL der gewünschte Namespace durchgängig verwendet
wird.
Als erstes muss man dem Namespace beim der Servicebeschreibung dem [ServiceContract]
angeben:
[ServiceContract(Namespace = "http://dotnetnukeblog.de")]
public interface IPostService
Die zweite Stelle ist bei der Serviceimplementierung:
[ServiceBehavior(Namespace = "http://dotnetnukeblog.de")]
class PostService
: IPostService
Die letzte Stelle ist bei der Konfiguration des Endpoints mit dem Attribute bindingNamespace:
<endpoint binding="basicHttpBinding"
bindingNamespace="http://dotnetnukeblog.de"....
Erst wenn an allen drei Stellen der Namespace gesetzt wurde, wird dieser auch durchgängig
bei der WSDL Erzeugung genutzt.
WCF, IIS and 404.3 Errors(Wed, 20 Oct 2010 13:21:55 GMT) Nachdem ich an einem jungfräulichen Rechner sitze und gerade einen WCF Service debuggen wolte, bekam ich ständig vom IIS den Fehlercode 404.3.
Schnell habe ich herausgefunden das die verwendete Endung ".svc" nicht einem
Handler zugeordnet ist. Um dieses aber nicht manuel machen zu müssen gibt es bei der
WCF ein Tool, dass die Registrierung übernimmt.
Das Tool findet man unter %Windows%\Microsoft.Net\Framework\v3.0\Windows Communication
Foundation\ und heißt ServiceModelReg.exe. Das muss mit dem Parameter "-i"
aufgerufen werden und anschließend funktioniert auch das Hosting im IIS.
asp.net viewstate analyzer(Tue, 28 Sep 2010 12:53:12 GMT) Es gibt immer wieder mal Fälle, wo man sich gerne den von ASP.NET erzeugten Viewstate anschauen möchte, um zu sehen, welche Kompontenten dort etwas reinschreiben, zur Optimierung, etc.
Für das Pflichttool (zumindest für Webentwickler) Fiddler gibt es genau für diese
Aufgabe eine sehr schöne Erweiterung: Den ASP.NET
ViewState Helper
Damit kann man sich den decodierten Viewstate innerhalb vom Fiddler anschauen und
sehr schön erkennen, was dort zum Client übertragen wurde.
Diese Erweiterung ist kostefrei und sehr einfach zu installieren. Dafür muss man lediglich
die Software runterladen und anschließend die Datei in das Unterverzeichnis "Inspectors"
kopieren. Schon hat man nach dem nächsten Start von Fiddler einen neuen Tab-Reiter
mit dem decodierten Viewstate.
Internet Explorer zeigt eine leere Seite / IE blank page(Thu, 23 Sep 2010 18:05:00 GMT) Beim Testen eines Redesign hatte ich das Problem das die neue Seite in
allen Browsern angezeigt wurde, nur nicht im Internet-Explorer (auf
jeden Fall im IE 7 und 8). Im IE sah ich eine leere
Seite / blank page.
Durch den Einsatz vom vom HTTP Debugging Tool Fiddler konnte ich aber sehen das der
komplette Inhalt zum Browser übertragen wurde, habe ich mir den Quellcode anzeigen
lassen, war auch alles in Ordnung. Allerdings zeigten mir die Entwicklertools - genau
wie der Browser selber - auch kein Ergebnis an.
Nachdem ich mir dann den Quellcode etwas näher angeschaut habe, musste ich festestellen,
dass der Code unsauber war. Im Head-Bereich habe ich das Standard-Tag title verwendet
aber leider sah das so aus:
<title>Meine tolle Seite<title>
Das Tag wurde leider nicht geschlossen. Das scheint dem IE überhaupt nicht
zu schmecken und verursacht, dass die Seite komplett nicht angezeigt wird.
Nachdem ich den Fehler behoben habe und das title-Tag ordnungsgemäß geschlossen hatte,
wurde auch die Seite endlich im IE angezeigt.
jQuery autocomplete und aufruf eines asmx webservice per get(Wed, 22 Sep 2010 17:42:49 GMT) Beschäftige mich aktuell intensiver mit dem System Umbraco. Dabei versuch ich einen Datentyp für die Administration zu entwicklen, der per AJAX Daten aus einer Datenbank lädt. Ein Datentyp in Umbraco ist z.B. ein Textfeld, Checkbox oder ähnliches um im Adminbereich das System mit Inhalten zu befüllen.
Bei meiner Suche bin ich dann auf ein jQuery-Plugin namens autocomplete aufmerksam
geworden. Damit kann man eine Textbox via JavaScript mit einer autocomplete-Funktion
erweitern. Da ich die Daten aber nicht per JSON direkt mit zum Client übertragen wollte,
habe ich dafür einen asmx-Webservice geschrieben.
Die Einbindung via jQuery ist denkbar einfach und sieht in C# z.B. so aus:
private void _BuildAutoCompletedScript()
{ StringBuilder clientScript = new StringBuilder();
clientScript.Append("
<script type='text/javascript'>"); clientScript.Append("
$(document).ready(function() { "); clientScript.AppendFormat("
$(\"#{0}\").autocomplete( ",
this.autoCompleteTextbox.ClientID);
clientScript.Append("
\"/umbraco/webservices/api/mywebserive.asmx/mymethod\", "); clientScript.Append("
{ delay:10, minChars:2,
matchContains:1,
cacheLength:10, autoFill:true } "); clientScript.Append("
); "); clientScript.Append("
}); "); clientScript.Append("
</script>"); this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), this.ClientID +
"_autocompleteData",
clientScript.ToString()); this.Page.ClientScript.RegisterClientScriptInclude("jquery.autocomplete",
"/umbraco_client/Application/JQuery/jquery.autocomplete.js");Auf
der Serverseite wird dann eine Web-Servicemethode definiert:
[WebService(Namespace = "uri:umbraco-irgendwas")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService] public class mywebservice
: System.Web.Services.WebService { [WebMethod] public string mymethod(string q, int limit)
{ return "Hello
World" + q;
} }Die Parameter q und limit wird vom Plugin automatisch
beim Aufruf als Parameter übergeben. Dabei ist q der Inhalt aus dem Textfeld
und limit ist die maximale Anzahl an Datensätzen, die zurück geliefert werden
sollen.
So weit so einfach, doch leider habe ich beim Aufruf immer einen http-Fehlercode 500
bekommen. Die Lösung habe ich noch einiger Recherche dann gefunden.
Die web.config muss um folgendes ergänzt werden:
<webServices>
<protocols>
<add name="HttpGet"/>
<add name="HttpPost"/>
<add name="HttpSoap"/>
</protocols>
</webServices>
sonst kann der Web-Service nicht via GET aufgerufen werden.
Hinweis: Der Feed "NET - Software & DotNetNuke (DNN) Blog" und dessen hier dargestellten RSS-Inhalte liegen urheberrechtlich beim Autor der Betreiber-URL (siehe RSS-Link). Auf den Inhalt von "NET - Software & DotNetNuke (DNN) Blog" hat RSS-Nachrichten.de keinen Einfluss. (23905-4-165-1 - 0)