`
k1121
  • 浏览: 181093 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

使用Handler来增强Web服务的功能

 
阅读更多

转自:

J2EE Web服务开发系列之六: 使用Handler来增强Web服务的功能

 

http://www.ibm.com/developerworks/cn/webservices/ws-handler/#author

 

阅读本文前您需要以下的知识和工具:

  • Apache axis1.1,并且会初步使用;
  • Tomcat 4.0以上, 并且会初步使用;
  • SOAP消息(SOAP Message)编程知识;
  • JAX-RPC编程基础知识;
  • Java安全编程基础知识。


本文的参考资料见 参考资料。 
本文的全部代码在这里 下载

Handler的基本概念


J2EE Web 服务中的Handler技术特点非常像Servlet技术中的Filter。我们知道,在Servlet中,当一个HTTP到达服务端时,往往要经过多个Filter对请求进行过滤,然后才到达提供服务的Servlet,这些Filter的功能往往是对请求进行统一编码,对用户进行认证,把用户的访问写入系统日志等。相应的,Web服务中的Handler通常也提供一下的功能:

  • 对客户端进行认证、授权;
  • 把用户的访问写入系统日志;
  • 对请求的SOAP消息进行加密,解密;
  • 为Web Services对象做缓存。

SOAP消息Handler能够访问代表RPC请求或者响应的SOAP消息。在JAX-RPC技术中,SOAP消息Handler可以部署在服务端,也可以在客户端使用。

下面我们来看一个典型的SOAP消息Handler处理顺序: 
某个在线支付服务需要防止非授权的用户访问或者撰改服务端和客户端传输的信息,从而使用消息摘要(Message Digest)的方法对请求和响应的SOAP消息进行加密。当客户端发送SOAP消⑹保突Ф说腍andler把请求消息中的某些敏感的信息(如信用卡密码)进行加密,然后把加密后的SOAP消息传输到服务端;服务端的SOAP消息Handler截取客户端的请求,把请求的SOAP 消息进行解密,然后把解密后的SOAP消息派发到目标的Web服务端点。

Apache axis是我们当前开发Web服务的较好的选择,使用axisWeb服务开发工具,可以使用Handler来对服务端的请求和响应进行处理。典型的情况下,请求传递如图1所示。

图1 SOAP消息的传递顺序

图1 SOAP消息的传递顺序

在图中,轴心点(pivot point)是Apache与提供程序功能相当的部分,通过它来和目标的Web服务进行交互,它通常称为Provider。axis中常用的Provider有Java:RPC,java:MSG,java:EJB。一个Web服务可以部署一个或者多个Handler。

Apache axis中的Handler体系结构和JAX-RPC 1.0(JSR101)中的体系结构稍有不同,需要声明的是,本文的代码在axis中开发,故需要在axis环境下运行。

在axis环境下,SOAP消息Handler必须实现org.apache.axis.Handler接口(在JAX-RPC 1.0规范中,SOAP消息Handler必须实现javax.xml.rpc.handler.Handler接口),org.apache.axis.Handler接口的部分代码如下:

例程1 org.apache.axis.Handle的部分代码
public interface Handler extends Serializable {
    public void init();  
    public void cleanup();
    public void invoke(MessageContext msgContext) throws AxisFault ;
    public void onFault(MessageContext msgContext);
    public void setOption(String name, Object value);    
    public Object getOption(String name);
   
    public void setName(String name);   
    public String getName();     
    public Element getDeploymentData(Document doc);
    public void generateWSDL(MessageContext msgContext) throws AxisFault;
   …
}

为了提供开发的方便,在编写Handler时,只要继承org.apache.axis.handlers. BasicHandler即可,BasicHandler是Handler的一个模板,我们看它的部分代码:

例程2 BasicHandler的部分代码
public abstract class BasicHandler implements Handler {
    protected static Log log =
        LogFactory.getLog(BasicHandler.class.getName());
    protected Hashtable options;
    protected String name;
    //这个方法必须在Handler中实现。
public abstract void invoke(MessageContext msgContext) throws AxisFault;
public void setOption(String name, Object value) {
        if ( options == null ) initHashtable();
        options.put( name, value );
    }
…
}

BasicHandler中的(MessageContext msgContext)方法是Handler实现类必须实现的方法,它通过MessageContext来获得请求或者响应的SOAPMessage对象,然后对SOAPMessage进行处理。

在介绍Handler的开发之前,我们先来看一下目标Web服务的端点实现类的代码,如例程3所示。

例程3 目标Web服务的端点实现类
package com.hellking.webservice;
public class HandleredService 
{
 //一个简单的Web服务
 public String publicMethod(String name)
 {
  return "Hello!"+name;
 }
}
//另一个Web服务端点:
package com.hellking.webservice;
public class OrderService 
{
       //web服务方法:获得客户端的订单信息,并且对订单信息进行对应的处理,
通常情况是把订单的信息写入数据库,然后可客户端返回确认信息。
 public String orderProduct(String name,String address,String item,int quantity,Card card)
 {
  String cardId=card.getCardId();
  String cardType=card.getCardType();
  String password=card.getPassword();
  String rderInfo="name="+name+",address="+address+",item="+item+",quantity="+quantity+"
,cardId="+cardId+",cardType="+cardType+",password="+password;
  System.out.println("这里是客户端发送来的信息:");
  System.out.println(orderInfo);  
  return orderInfo;
 } 
}

下面我们分不同情况讨论Handler的使用实例。

 

使用Handler为系统做日志


Handler为系统做日志是一种比较常见而且简单的使用方式。和Servlet中的Filter一样,我们可以使用Handler来把用户的访问写入系统日志。下面我们来看日志Handler的具体代码,如例程4所示。

例程4 LogHandler的代码
package com.hellking.webservice;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.Date;
import org.apache.axis.AxisFault;
import org.apache.axis.Handler;
import org.apache.axis.MessageContext;
import org.apache.axis.handlers.BasicHandler;
public class LogHandler extends BasicHandler {
  
   /**invoke,每一个handler都必须实现的方法。
  */
    public void invoke(MessageContext msgContext) throws AxisFault
    {
       //每当web服务被调用,都记录到log中。
        try {
            Handler handler = msgContext.getService();
            String filename = (String)getOption("filename");
            if ((filename == null) || (filename.equals("")))
                throw new AxisFault("Server.NoLogFile",
                                 "No log file configured for the LogHandler!",
                                    null, null);
            FileOutputStream fos = new FileOutputStream(filename, true);            
            PrintWriter writer = new PrintWriter(fos);            
            Integer counter = (Integer)handler.getOption("accesses");
            if (counter == null)
                counter = new Integer(0);
            
            counter = new Integer(counter.intValue() + 1);            
            Date date = new Date();
            msgContext.getMessage().writeTo(System.out);
           
            String result = "在"+date + ": Web 服务 " +
                            msgContext.getTargetService() +
                            " 被调用,现在已经共调用了 " + counter + " 次.";
            handler.setOption("accesses", counter);            
            writer.println(result);            
            writer.close();
        } catch (Exception e) {
            throw AxisFault.makeFault(e);
        }
    }
}

前面我们说过,Handler实现类必须实现invoke方法,invoke方法是Handler处理其业务的入口点。LogHandler的主要功能是把客户端访问的Web服务的名称和访问时间、访问的次数记录到一个日志文件中。

下面部署这个前面开发的Web服务对像,然后为Web服务指定Handler。编辑Axis_Home/WEB-INF/ server-config.wsdd文件,在其中加入以下的内容:

<service name="HandleredService" provider="java:RPC">
  <parameter name="allowedMethods" value="*"/>
  <parameter name="className" value="com.hellking.webservice.HandleredService"/>
  <parameter name="allowedRoles" value="chen"/>
  <beanMapping languageSpecificType="java:com.hellking.webservice.Card"
 qname="card:card" xmlns:card="card"/>
  <requestFlow>
<handler name="logging" type="java:com.hellking.webservice.LogHandler">
  <parameter name="filename" value="c:\\MyService.log"/>
 </handler>
  </requestFlow>
 </service>
…
</globalConfiguration>
…
  <handler name="logging" type="java:com.hellking.webservice.LogHandler">
  <parameter name="filename" value="c:\\MyService.log"/>
 </handler>
…
<service name="HandleredService" provider="java:RPC">
…
  <requestFlow>
  <handler type="logging"/>
   …<!--在这里可以指定多个Handler-->
  </requestFlow>
 </service>
http://127.0.0.1:8080/handler/services/HandleredService?wsdl&method=publicMethod&name=chen

注意:这个URL需要根据具体情况改变。

在Sun Jul 06 22:42:03 CST 2003: Web 服务 HandleredService 被调用,现在已经共调用了 1 次.
在Sun Jul 06 22:42:06 CST 2003: Web 服务 HandleredService 被调用,现在已经共调用了 2 次.
在Sun Jul 06 22:42:13 CST 2003: Web 服务 HandleredService 被调用,现在已经共调用了 3 次.
 

使用Handler对用户的访问认证


使用Handler为用户访问认证也是它的典型使用,通过它,可以减少在Web服务端代码中认证的麻烦,同时可以在部署描述符中灵活改变用户的访问权限。

对用户认证的Handler代码如下:

例程5 认证的Handler
package com.hellking.webservice;
import….
//此handler的目的是对用户认证,只有认证的用户才能访问目标服务。
public class AuthenticationHandler extends BasicHandler
{
 /**invoke,每一个handler都必须实现的方法。
  */
 public void invoke(MessageContext msgContext)throws AxisFault
 {  
        SecurityProvider provider = (SecurityProvider)msgContext.getProperty("securityProvider");
  if(provider==null)
  {
   provider= new SimpleSecurityProvider();
             msgContext.setProperty("securityProvider", provider);
         }
        if(provider!=null)
        {         
         String userId=msgContext.getUsername();
         String password=msgContext.getPassword();
         
         //对用户进行认证,如果authUser==null,表示没有通过认证,
抛出Server.Unauthenticated异常。
            org.apache.axis.security.AuthenticatedUser authUser 
= provider.authenticate(msgContext);
            if(authUser==null)
              throw new AxisFault("Server.Unauthenticated", 
Messages.getMessage("cantAuth01", userId), null,null);
            //用户通过认证,把用户的设置成认证了的用户。
            msgContext.setProperty("authenticatedUser", authUser);
        } 
    }
}

在AuthenticationHandler代码里,它从MessageContext中获得用户信息,然后进行认证,如果认证成功,那么就使用msgContext.setProperty("authenticatedUser", authUser)方法把用户设置成认证了的用户,如果认证不成功,那么就抛出Server.Unauthenticated异常。

部署这个Handler,同样,在server-config里加入以下的内容:

<handler name="authen" type="java:com.hellking.webservice.AuthenticationHandler"/>
…
<service name="HandleredService" provider="java:RPC">
<parameter name="allowedRoles" value="chen"/>
…
</service>

WEB-INF/users.lst文件中加入以下用户:

hellking hellking
chen chen
http://127.0.0.1:8080/handler/services/HandleredService?wsdl&method=publicMethod&name=chen

将会提示输入用户名和密码,如图2所示。

图2 访问web服务时的验证

图2 访问web服务时的验证

如果客户端是应用程序,那么可以这样在客户端设置用户名和密码:

例程6 在客户端设置用户名和密码
http://127.0.0.1:808
String endpointURL = "http://127.0.0.1:8080/handler/services/HandleredService?wsdl";            
            Service  service = new Service();
            Call     call    = (Call) service.createCall();
            call.setTargetEndpointAddress( new java.net.URL(endpointURL) );
            call.setOperationName( new
 QName("HandleredService", "orderProduct") );//设置操作的名称。
            //由于需要认证,故需要设置调用的用户名和密码。
            call.getMessageContext().setUsername("chen");
            call.getMessageContext().setPassword("chen");
 

使用Handler对用户的访问授权


对于已经认证了的用户,有时在他们操作某个特定的服务时,还需要进行授权,只有授权的用户才能继续进行操作。我们看对用户进行授权的Handler的代码。

例程7 对用户进行授权的代码
package com.hellking.webservice;
import…
//此handler的目的是对认证的用户授权,只有授权的用户才能访问目标服务。
public class AuthorizationHandler extends BasicHandler
{
 /**invoke,每一个handler都必须实现的方法。
  */
 public void invoke(MessageContext msgContext)
        throws AxisFault
    {
      
        AuthenticatedUser user = (AuthenticatedUser)msgContext.getProperty("authenticatedUser");
        if(user == null)
            throw new AxisFault("Server.NoUser", Messages.getMessage("needUser00"), null, null);
        String userId = user.getName();
        Handler serviceHandler = msgContext.getService();
        if(serviceHandler == null)
            throw new AxisFault(Messages.getMessage("needService00"));
        String serviceName = serviceHandler.getName();
        String allowedRoles = (String)serviceHandler.getOption("allowedRoles");
        if(allowedRoles == null)
        {          
            return;
        }
        SecurityProvider provider = (SecurityProvider)msgContext.getProperty("securityProvider");
        if(provider == null)
            throw new AxisFault(Messages.getMessage("noSecurity00"));
        for(StringTokenizer st = new StringTokenizer(allowedRoles, ","); st.hasMoreTokens();)
        {
            String thisRole = st.nextToken();
            if(provider.userMatches(user, thisRole))
            {
                return;//访问授权通过。
            }
        }
        //没有通过授权,不能访问目标服务,抛出Server.Unauthorized异常。
        throw new AxisFault("Server.Unauthorized", 
Messages.getMessage("cantAuth02", userId, serviceName), null, null);
    }     
}

在service-config.wsdd文件中,我们为Web服务指定了以下的用户:

<parameter name="allowedRoles" value="chen,hellking"/>

provider.userMatches(user, thisRole)将匹配允许访问Web服务的用户,如果匹配成功,那么授权通过,如果没有授权成功,那么抛出Server.Unauthorized异常。

 

使用Handler对SOAP消息进行加密、解密


由于SOAP消息在HTTP协议中传输,而HTTP协议的安全度是比较低的,怎么保证信息安全到达对方而不泄漏或中途被撰改,将是Web服务必须解决的问题。围绕Web服务的安全,有很多相关的技术,比如WS-Security,WS-Trace等,另外,还有以下相关技术:

  • XML Digital Signature(XML数字签名)
  • XML Encryption (XML加密)
  • XKMS (XML Key Management Specification)
  • XACML (eXtensible Access Control Markup Language)
  • SAML (Secure Assertion Markup Language)
  • ebXML Message Service Security
  • Identity Management & Liberty Project

不管使用什么技术,要使信息安全到达对方,必须把它进行加密,然后在对方收到信息后解密。为了提供开发的方便,可以使用Handler技术,在客户端发送信息前,使用客户端的Handler对SOAP消息中的关键信息进行加密;在服务端接收到消息后,有相应的Handler把消息进行解密,然后才把SOAP消息派发到目标服务。

下面我们来看一个具体的例子。加入使用SOAP消息发送订单的信息,订单的信息如下:

例程8 要发送的订单SOAP消息

点击查看代码清单

上面的黑体字是传输的敏感信息,故需要加密。我们可以使用Message Digest之类的方法进行加密。加密之后的信息结构如下:

例程9 把SOAP消息某些部分加密
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope …
<soapenv:Body>
  <ns1:orderProduct …>
   …
   <arg4 href="#id0"/>
  </ns1:orderProduct>
  <multiRef …>
   <ns3:EncryptedData xmlns:ns3="http://www.w3.org/2000/11/temp-xmlenc">
    <ns3:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
    <ns3:DigestValue>rO0ABXQAkyA8Y2FyZ…….
</ns3:DigestValue>
   </ns3:EncryptedData>
  </multiRef>
 </soapenv:Body>
</soapenv:Envelope>

图3是使用Handler对SOAP消息进行加密、解密后,SOAP消息在传递过程中结构的改变。

图3 SOAP消息的加密和解密

图3 SOAP消息的加密和解密

从上图可以看出,通过使用加密、解密的Handler,可以确保消息的安全传递。进一步说,如果把这种Handler做成通用的组件,那么就可以灵活地部署到不同的服务端和客户端。

客户端的Handler的功能是把SOAP消息使用一定的规则加密,假如使用Message Digest加密方式,那么可以这样对敏感的信息加密:

例程10 对SOAP消息的敏感部分加密

点击查看代码清单

在客户端发送出SOAP消息时,客户端的Handler拦截发送的SOAP消息,然后对它们进行加密,最后把加密的信息传送到服务端。

服务端接收到加密的信息后,解密的Handler会把对应的加密信息解密。服务端Handler代码如例程11所示。

例程11 服务端解密Handler
package com.hellking.webservice;
import…
//此handler的目的是把加密的SOAP消息解密成目标服务可以使用的SOAP消息。
public class MessageDigestHandler extends BasicHandler
{
 /**invoke,每一个handler都必须实现的方法。
  */
 public void invoke(MessageContext msgContext)throws AxisFault
 {
  try
  {   
   //从messageContext例取得SOAPMessage对象。
   SOAPMessage msg=msgContext.getMessage();
   SOAPEnvelope env=msg.getSOAPPart().getEnvelope();
   Iterator it=env.getBody().getChildElements();   
   SOAPElement multi=null;
   while(it.hasNext())
    {
     multi=(SOAPElement)it.next();//multi是soapbody的最后一个child。
    }
   String value="";//value表示加密后的值。
   SOAPElement digestValue=null;
   Iterator it2=multi.getChildElements();
   while(it2.hasNext())
   {
    SOAPElement temp=(SOAPElement)it2.next();
    Iterator it3=temp.getChildElements(env.createName("DigestValue",
"ns3","http://www.w3.org/2000/11/temp-xmlenc"));
    if(it3.hasNext())
    value=((SOAPElement)it3.next()).getValue();//获得加密的值    
   }   
    //把加密的SOAPMessage解密成目标服务可以调用的SOAP消息。
    SOAPMessage   msg2=convertMessage(msg,this.decrypte(value));
    msgContext.setMessage(msg2);        
      }
      catch(Exception e)
      {
       e.printStackTrace();
      }      
 } 
 //这个方法是把加密的数据进行解密,返回明文。
 public String decrypte(String value)
 {
  String data=null;
  try
  {
   ByteArrayInputStream fis = new 
ByteArrayInputStream(new sun.misc.BASE64Decoder().decodeBuffer(value));
   ObjectInputStream ois = new ObjectInputStream(fis);
   Object o = ois.readObject();
   if (!(o instanceof String)) {
    System.out.println("Unexpected data in string");
    System.exit(-1);
   }
   data = (String) o;
   System.out.println("解密后的值:" + data);
   o = ois.readObject();
   if (!(o instanceof byte[])) {
    System.out.println("Unexpected data in string");
    System.exit(-1);
   }   
   byte origDigest[] = (byte []) o;
   MessageDigest md = MessageDigest.getInstance("SHA");
   md.update(data.getBytes());
  }
         …
  return data;
  }
    //把解密后的信息重新组装成服务端能够使用的SOAP消息。
 public SOAPMessage convertMessage(SOAPMessage msg,String data)
 {    
   ….
 }
}

可以看出,服务端解密的Handler和客户端加密的Handler的操作是相反的过程。

 

总结


通过以上的讨论,相信大家已经掌握了Handler的基本使用技巧。可以看出,通过使用Handler,可以给Web服务提供一些额外的功能。在实际的开发中,我们可以开发出一些通用的Handler,然后通过不同的搭配方式把它们部署到不同的Web服务中。

另外,在XML & Web services专区还有一篇关于使用Handler来为Web服务提供缓存功能的文章,您可以参考它, 《用高速缓存加速您的 Web 服务》

分享到:
评论

相关推荐

    JWS Handler 示例

    Java Web Services (JWS) Handler 是Java平台中用于处理Web服务消息的一个关键组件。它提供了一种机制,允许...通过深入理解和实践这些示例,开发者能够更好地理解如何利用JWS Handler来提升Web服务的安全性和功能性。

    soap中Handler的处理

    本文将深入探讨SOAP中的Handler处理,以及如何利用它来增强Web服务的功能。 首先,我们需要了解SOAP消息的基本结构。一个典型的SOAP消息由三部分组成:Header、Body和Envelope。Header通常用于传递与消息处理相关的...

    使用Ajax调用SOAP Web服务2:扩展Web服务客户机

    在实际应用中,这些增强功能使得JavaScript库不仅能够简单地调用SOAP服务,还能处理更复杂的服务交互,如服务发现、故障恢复和资源管理。这种能力对于那些希望在Web应用程序中集成远程服务的开发者来说是极其宝贵的...

    ASP.NET源码——利用handler生成静态页面的完整解决方案.zip

    ASP.NET 是微软开发的一种用于构建Web应用程序的框架,它提供了丰富的功能和强大的工具来帮助开发者创建动态、数据驱动的网站。在这个"ASP.NET源码——利用handler生成静态页面的完整解决方案.zip"压缩包中,我们...

    使用XFire+Spring构建Web Service

    【使用XFire+Spring构建Web Service】是一种在Java平台上开发Web服务的高效方法。XFire作为新一代的Web服务框架,与Axis2并列,因其简洁的API和对Web服务标准的良好支持而受到开发者欢迎。XFire特别强调与Spring框架...

    Webservice之Axis高级编程

    在本文中,我们将探索Axis的高级编程技巧,涵盖如何发布Web服务、使用Handler增强功能、建立安全服务、处理JavaBean传递及监控SOAP消息等方面。 1. **AXIS第一课:最简单的发布AXIS的Web Service** 在这一课中,...

    Go-Handler:AlightexpressiveGoHTTPhandler

    在Go语言中,HTTP处理程序(Handler)是构建Web服务的核心组件,它们负责接收HTTP请求并生成响应。"Go-Handler: Alightexpressive Go HTTP handler"是一个专注于提供轻量级且表达性强的HTTP处理程序库,使得Go开发者...

    Laravel开发-rest-handler

    总之,`Laravel开发-rest-handler`是提升REST API质量的重要一环,它使得异常处理更加规范、人性化,增强了API的稳定性和可维护性。通过理解和掌握上述知识点,你可以创建一个高效且易于使用的API异常处理系统。

    Laravel开发-laravel-api-handler

    在本文中,我们将深入探讨“Laravel开发-laravel-api-handler”这一主题,这是一个专为Laravel框架设计的扩展包,旨在简化RESTful API的开发工作,通过提供一系列实用的帮助函数来增强API处理能力。 首先,Laravel...

    .Net WebApi消息拦截器之MessageHandler的示例

    消息拦截器是.NET WebApi中强大的功能,它使得开发者能够在请求处理的各个阶段注入自定义逻辑,从而增强应用程序的功能性和安全性。上述示例展示了如何使用消息拦截器来实现请求处理、HTTPS重定向等常见需求。开发者...

    Node.js-使用ReduxBoot创建的Web服务器

    2. **中间件支持**: 类似于Redux中的中间件机制,ReduxBoot允许开发者插入自定义的中间件来处理请求,增强功能。 3. **简洁的API**: ReduxBoot提供了一套简洁的API,使得开发者可以快速上手并构建复杂的Web应用。 4....

    webservices

    Apache CXF是一个开源框架,专门用于构建和消费Web服务,它提供了强大的功能来简化开发过程。 Apache CXF 2.7.10是这个版本的框架,包含了工具和库,使得开发者能够轻松地实现SOAP(简单对象访问协议)和RESTful...

    Web服务器软件Apache模块初步研究总结.doc

    以上是对Apache模块的初步研究总结,深入理解和实践Apache模块API可以极大地增强Web服务器的功能,实现高度定制化的Web服务解决方案。通过不断研究和实践,可以进一步优化和扩展这些模块,以满足不断变化的Web应用...

    A-small-web-server.rar_C 实现web

    在IT领域,构建一个Web服务器是一项基础且重要的任务,它涉及到网络编程、HTTP协议和多线程等核心技术...通过分析和重构这个项目,开发者可以增强自己的编程技能,并为构建更复杂、功能更全面的Web服务打下坚实的基础。

    JFinal框架在Java Web开发中的应用.pdf

    此外,由于JFinal是轻量级框架,某些复杂场景下可能需要额外的插件或工具来增强其功能性。尽管如此,JFinal依然是Java Web开发领域中一个值得推荐的开发框架,特别是在追求快速开发和简单配置的项目中具有很大优势。...

    Axis高级编程

    #### 三、AXIS高级应用,使用Handler来增强Web服务的功能 **知识点概述:** 本节介绍如何使用Handler来扩展Axis的Web服务功能,增强其灵活性和安全性。 **知识点详述:** 1. **Handler的定义** - Handler是在...

    【go web基础】02 web开发

    在本节【go web基础】02 web开发的学习笔记中,我们将深入探讨使用Go语言进行Web开发的基础知识。Go语言,又称Golang,是Google推出的一种静态类型、编译型的编程语言,以其简洁的语法、高效的性能以及内置的并发...

Global site tag (gtag.js) - Google Analytics