Cliente de servicio web generado automáticamente por .NET: ¿Cómo evito solicitar esquemas desde w3.org?

StackOverflow https://stackoverflow.com/questions/217841

Pregunta

Tengo un cliente de servicio web .NET que se ha generado automáticamente a partir de un archivo wsdl utilizando la herramienta wsdl.exe.

Cuando instalo por primera vez la clase generada, comienza a solicitar un montón de documentos de w3.org y otros. El primero es http://www.w3.org/2001/XMLSchema.dtd

Además de no querer causar tráfico innecesario a w3.org, necesito poder ejecutar la aplicación sin una conexión a Internet (el servicio web es un " servicio Intra-web ").

¿Alguien sabe la solución?

Si ayuda, aquí está el stacktrace que obtengo cuando no tengo Internet:

"An error has occurred while opening external DTD 'http://www.w3.org/2001/XMLSchema.dtd': The remote name could not be resolved: 'www.w3.org'"

   at System.Net.HttpWebRequest.GetResponse()
   at System.Xml.XmlDownloadManager.GetNonFileStream(Uri uri, ICredentials credentials)
   at System.Xml.XmlDownloadManager.GetStream(Uri uri, ICredentials credentials)
   at System.Xml.XmlUrlResolver.GetEntity(Uri absoluteUri, String role, Type ofObjectToReturn)
   at System.Xml.XmlTextReaderImpl.OpenStream(Uri uri)
   at System.Xml.XmlTextReaderImpl.DtdParserProxy_PushExternalSubset(String systemId, String publicId)

   at System.Xml.XmlTextReaderImpl.Throw(Exception e)
   at System.Xml.XmlTextReaderImpl.DtdParserProxy_PushExternalSubset(String systemId, String publicId)
   at System.Xml.XmlTextReaderImpl.DtdParserProxy.System.Xml.IDtdParserAdapter.PushExternalSubset(String systemId, String publicId)
   at System.Xml.DtdParser.ParseExternalSubset()
   at System.Xml.DtdParser.ParseInDocumentDtd(Boolean saveInternalSubset)
   at System.Xml.DtdParser.Parse(Boolean saveInternalSubset)
   at System.Xml.XmlTextReaderImpl.DtdParserProxy.Parse(Boolean saveInternalSubset)
   at System.Xml.XmlTextReaderImpl.ParseDoctypeDecl()
   at System.Xml.XmlTextReaderImpl.ParseDocumentContent()
   at System.Xml.XmlTextReaderImpl.Read()
   at System.Xml.Schema.Parser.StartParsing(XmlReader reader, String targetNamespace)
   at System.Xml.Schema.Parser.Parse(XmlReader reader, String targetNamespace)
   at System.Xml.Schema.XmlSchemaSet.ParseSchema(String targetNamespace, XmlReader reader)
   at System.Xml.Schema.XmlSchemaSet.Add(String targetNamespace, XmlReader schemaDocument)
   at [...]WebServiceClientType..cctor() in [...]
¿Fue útil?

Solución 2

Necesitaba XmlResolver, por lo que la solución de tamberg no funcionó del todo. Lo resolví implementando mi propio XmlResolver que lee los esquemas necesarios de los recursos incrustados en lugar de descargarlos.

El problema no tuvo nada que ver con el código generado automáticamente, por cierto.

El servicio web-cliente tenía otro archivo de implementación que contenía algo como esto:

public partial class [...]WebServiceClientType
  {
    private static readonly XmlSchemaSet _schema;

    static KeyImportFileType()
    {
      _schema = new XmlSchemaSet();
      _schema.Add(null, XmlResourceResolver.GetXmlReader("http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd"));
      _schema.Add(null, XmlResourceResolver.GetXmlReader("http://www.w3.org/TR/2002/REC-xmlenc-core-20021210/xenc-schema.xsd"));
      _schema.Compile();
    }

y fue este constructor de clase el que falló.

Otros consejos

si tiene acceso a XmlReader (o XmlTextReader) puede hacer lo siguiente:

XmlReader r = ...
r.XmlResolver = null; // prevent xsd or dtd parsing

Saludos, tamberg

Aquí está mi solución. Espero que evite que alguien tenga que depurar a través del marco .NET como tuve que trabajar los fundamentos de XmlUrlResolver. Se cargará desde un recurso local (archivo de texto resx), caché o utilizará el comportamiento predeterminado de XmlUrlResolver:

using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Net;
using System.Net.Cache;
using System.IO;
using System.Resources;

namespace AxureExport {

    //
    // redirect URL resolution to local resource (or cache)
    public class XmlCustomResolver : XmlUrlResolver {

        ICredentials _credentials;
        ResourceManager _resourceManager;

        public enum ResolverType { useDefault, useCache, useResource };
        ResolverType _resolverType;

        public XmlCustomResolver(ResolverType rt, ResourceManager rm = null) {
            _resourceManager = rm != null ? rm : AxureExport.Properties.Resources.ResourceManager;
            _resolverType = rt;
        }

        public override ICredentials Credentials {
            set {
                _credentials = value;
                base.Credentials = value;
            }
        }

        public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn) {
            object response = null;

            if (absoluteUri == null)
                throw new ArgumentNullException(@"absoluteUri");

            switch (_resolverType) {
                default:
                case ResolverType.useDefault:                   // use the default behavior of the XmlUrlResolver
                    response = defaultResponse(absoluteUri, role, ofObjectToReturn);
                    break;

                case ResolverType.useCache:                     // resolve resources thru cache
                    if (!isExternalRequest(absoluteUri, ofObjectToReturn)) {
                        response = defaultResponse(absoluteUri, role, ofObjectToReturn);
                        break;
                    }

                    WebRequest webReq = WebRequest.Create(absoluteUri);
                    webReq.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.Default);
                    if (_credentials != null)
                        webReq.Credentials = _credentials;

                    WebResponse wr = webReq.GetResponse();
                    response = wr.GetResponseStream();
                    break;

                case ResolverType.useResource:                  // get resource from internal resource
                    if (!isExternalRequest(absoluteUri, ofObjectToReturn)) {
                        response = defaultResponse(absoluteUri, role, ofObjectToReturn);    // not an external request
                        break;
                    }

                    string resourceName = uriToResourceKey(absoluteUri);
                    object resource = _resourceManager.GetObject(resourceName);
                    if (resource == null)
                        throw new ArgumentException(@"Resource not found.  Uri=" + absoluteUri + @" Local resourceName=" + resourceName);

                    if (resource.GetType() != typeof(System.String))
                        throw new ArgumentException(resourceName + @" is an unexpected resource type.  (Are you setting resource FileType=Text?)");

                    response = ObjectToUTF8Stream(resource);
                    break;
            }

            return response;
        }

        //
        // convert object to stream
        private static object ObjectToUTF8Stream(object o) {
            MemoryStream stream = new MemoryStream();

            StreamWriter writer = new StreamWriter(stream, Encoding.UTF8);
            writer.Write(o);
            writer.Flush();
            stream.Position = 0;

            return stream;
        }

        //
        // default response is to call tbe base resolver
        private object defaultResponse(Uri absoluteUri, string role, Type ofObjectToReturn) {
            return base.GetEntity(absoluteUri, role, ofObjectToReturn);
        }

        //
        // determine whether this is an external request
        private static bool isExternalRequest(Uri absoluteUri, Type ofObjectToReturn) {
            return absoluteUri.Scheme == @"http" && (ofObjectToReturn == null || ofObjectToReturn == typeof(Stream));
        }

        //
        // translate uri to format compatible with reource manager key naming rules
        // see: System.Resources.Tools.StronglyTypedResourceBuilder.VerifyResourceName Method
        //   from http://msdn.microsoft.com/en-us/library/ms145952.aspx:
        private static string uriToResourceKey(Uri absoluteUri) {
            const string repl = @"[ \xA0\.\,\;\|\~\@\#\%\^\&\*\+\-\/\\\<\>\?\[\]\(\)\{\}\" + "\"" + @"\'\:\!]+";
            return Regex.Replace(Path.GetFileNameWithoutExtension(absoluteUri.LocalPath), repl, @"_");
        }
    }
}

Gracias, Tamberg, me ahorró mucho tiempo con su respuesta sucinta y correcta. No me di cuenta de que la resolución predeterminada iría a la web. La comprobación de MSDN es estados -

  

XmlResolver es el resolutor predeterminado para todas las clases en el espacio de nombres System.Xml. También puede crear su propio resolutor ...

He implementado tu respuesta, estableciendo el resolutor en NULL que soluciona el problema y reduce la sobrecarga de la red.

XmlReader r = ...r.XmlResolver = null; // prevent xsd or dtd parsing

Gracias de nuevo, Andy

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top