杰瑞科技汇

soapheader java

Of course! Let's dive deep into SOAPHeader in Java, covering its purpose, how to create and use it with both the standard JAX-WS API and the more modern and flexible Jakarta EE API.

What is a SOAP Header?

In the SOAP (Simple Object Access Protocol) messaging standard, a SOAP message consists of two main parts:

  1. SOAP Body: Contains the actual message payload or the data being exchanged (e.g., the request parameters or response results).
  2. SOAP Header: An optional, but powerful, part of the message that contains metadata about the message itself. It's not the data being processed by the application logic, but rather information for the processing infrastructure.

Think of it like an envelope for a letter:

  • Letter (SOAP Body): The actual content you want to read.
  • Envelope's Stamp/Postmark (SOAP Header): Information for the postal service (the infrastructure), like the destination, routing information, or "Priority Mail" status.

Common uses for the SOAP Header include:

  • Authentication/Authorization: Passing security tokens, usernames, passwords, or session IDs.
  • Routing: Specifying the path a message should take through a network of services.
  • Message-Level Quality of Service (QoS): Indicating requirements like must-understand, relay, or transaction context.
  • WS-Security: Carrying encrypted or signed security information.

Key Concepts in SOAPHeader

Before we look at the code, it's important to understand the building blocks:

  • SOAPHeader: The root element of the header block. It's a container for all header entries.
  • SOAPHeaderElement: An individual entry or block within the SOAPHeader. Each element can have its own purpose and attributes.
  • MustUnderstand Attribute: A crucial boolean attribute (often mustUnderstand="1"). If a SOAP receiver (e.g., a web service) doesn't understand a header block marked with mustUnderstand, it must reject the message with a MustUnderstand SOAP fault. This is for critical, non-optional metadata.
  • Relay Attribute: A boolean attribute (often relay="1"). It instructs an intermediate node to pass the header block to the next node in the message path, even if it doesn't understand it. This is useful for routing information.

How to Use SOAP Headers in Java

There are two primary ways to work with SOAP headers in Java:

  1. JAX-WS (Java API for XML Web Services): The older, standard API for Java EE.
  2. Jakarta EE: The modern, open-source continuation of Java EE. The API is almost identical, but the package names have changed.

We'll focus on the Jakarta EE API as it's the current standard, but I'll provide the JAX-WS equivalents.

Prerequisites

To run these examples, you'll need the Jakarta EE APIs. If you're using a build tool like Maven, add these dependencies:

<!-- For the client and server to process SOAP messages -->
<dependency>
    <groupId>jakarta.xml.ws</groupId>
    <artifactId>jakarta.xml.ws-api</artifactId>
    <version>4.0.0</version>
</dependency>
<dependency>
    <groupId>com.sun.xml.ws</groupId>
    <artifactId>jaxws-rt</artifactId>
    <version>4.0.0</version>
</dependency>

Scenario: Adding a Security Token to a SOAP Header

Let's create a practical example where a client adds a securityToken to the SOAP header before calling a web service operation.

Step 1: Create the Web Service Endpoint (SEI - Service Endpoint Interface)

This is the Java interface that defines the web service operations.

HelloService.java

package com.example.soap;
import jakarta.jws.WebMethod;
import jakarta.jws.WebService;
import jakarta.jws.soap.SOAPBinding;
// @WebService marks this as a Web Service interface
@WebService
// @SOAPBinding specifies the SOAP style (DOCUMENT), use (LITERAL), and parameter style (WRAPPED)
@SOAPBinding(style = SOAPBinding.Style.DOCUMENT, use = SOAPBinding.Use.LITERAL, parameterStyle = SOAPBinding.ParameterStyle.WRAPPED)
public interface HelloService {
    @WebMethod
    String sayHello(String name);
}

Step 2: Implement the Web Service

This is the concrete implementation of the SEI.

HelloServiceImpl.java

package com.example.soap;
import jakarta.jws.WebService;
@WebService(endpointInterface = "com.example.soap.HelloService")
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        System.out.println("Received request to say hello to: " + name);
        return "Hello, " + name + "!";
    }
}

Step 3: Create a Client to Add a SOAP Header

This is the core of our example. We'll create a JAX-WS Dispatch object, which gives us low-level control over the SOAP message. We'll use a SOAPHandler to programmatically add the header.

SoapHeaderClient.java

package com.example.soap;
import jakarta.xml.soap.*;
import jakarta.xml.ws.Dispatch;
import jakarta.xml.ws.Service;
import javax.xml.namespace.QName;
import java.net.URL;
public class SoapHeaderClient {
    public static void main(String[] args) throws SOAPException {
        // 1. Create a Service instance from the WSDL location
        // In a real app, you'd use the WSDL URL of your deployed service.
        // For this example, we'll create a mock service for demonstration.
        URL wsdlUrl = SoapHeaderClient.class.getResource("/hello_service.wsdl"); // Assume this WSDL exists
        if (wsdlUrl == null) {
            System.out.println("WSDL not found. Using a mock service for demonstration.");
            // In a real scenario, you would have a WSDL. For this client-side focus,
            // we'll proceed by creating a Dispatch object directly.
        }
        QName qname = new QName("http://example.com/soap", "HelloService");
        Service service = Service.create(qname);
        // 2. Create a Dispatch instance for dynamic message sending
        // We use SOAPMessage for full control over the message.
        Dispatch<SOAPMessage> dispatch = service.createDispatch(qname, SOAPMessage.class, Service.Mode.MESSAGE);
        // 3. Create the SOAP Message
        MessageFactory factory = MessageFactory.newInstance();
        SOAPMessage soapMessage = factory.createMessage();
        SOAPPart soapPart = soapMessage.getSOAPPart();
        SOAPEnvelope envelope = soapPart.getEnvelope();
        // 4. Create the SOAPHeader and add a header element
        SOAPHeader header = envelope.getHeader();
        if (header == null) {
            header = envelope.addHeader(); // Add header if it doesn't exist
        }
        // Create the header element
        // QName defines the name and namespace of the element
        QName securityHeaderName = new QName("http://example.com/soap/security", "securityToken");
        SOAPHeaderElement securityToken = header.addHeaderElement(securityHeaderName);
        // Set the content of the header element
        securityToken.addTextNode("abc-123-security-token-xyz");
        // Optional: Add MustUnderstand attribute
        securityToken.setMustUnderstand(true);
        // 5. Create the SOAP Body and add the request payload
        SOAPBody body = envelope.getBody();
        QName bodyElementName = new QName("http://example.com/soap", "sayHello");
        SOAPBodyElement bodyElement = body.addBodyElement(bodyElementName);
        // Add the argument to the body element
        bodyElement.addTextNode("ClientUser");
        // 6. Save the changes and send the message
        soapMessage.saveChanges();
        System.out.println("Sending SOAP Message with Header:");
        soapMessage.writeTo(System.out); // Print the message to console
        System.out.println("\n------------------------------------");
        // 7. Receive the response
        SOAPMessage response = dispatch.invoke(soapMessage);
        System.out.println("Received SOAP Response:");
        response.writeTo(System.out);
    }
}

Step 4: Handling SOAP Headers on the Server Side

To process the securityToken header on the server, you would typically use a SOAPHandler. This is a more advanced topic, but here's the concept:

  1. Create a SOAPHandler<SOAPMessageContext>: This class will inspect the incoming SOAP message.
  2. Implement handleMessage: In this method, you get the SOAPHeader from the message context.
  3. Find and Process the Header: You iterate through the header elements to find your securityToken and validate it.
  4. Attach Handler to Endpoint: You configure your web service endpoint to use this handler, usually via a HandlerResolver.

Here's a simplified SOAPHandler for the server:

// Server-side Handler
public class SecurityHeaderHandler implements SOAPHandler<SOAPMessageContext> {
    @Override
    public boolean handleMessage(SOAPMessageContext context) {
        Boolean isRequest = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
        if (!isRequest) { // This is an inbound request
            try {
                SOAPMessage soapMessage = context.getMessage();
                SOAPHeader header = soapMessage.getSOAPHeader();
                if (header != null) {
                    // Find the securityToken element
                    Iterator<SOAPHeaderElement> iterator = header.examineAllHeaderElements();
                    while (iterator.hasNext()) {
                        SOAPHeaderElement element = iterator.next();
                        if ("securityToken".equals(element.getLocalName())) {
                            String token = element.getValue();
                            System.out.println("Server received security token: " + token);
                            // TODO: Add your validation logic here
                            if (!"abc-123-security-token-xyz".equals(token)) {
                                // If token is invalid, create a fault and abort processing
                                SOAPBody body = soapMessage.getSOAPBody();
                                SOAPFault fault = body.addFault();
                                fault.setFaultString("Invalid security token");
                                throw new SOAPException("Authentication failed");
                            }
                            return true; // Token is valid
                        }
                    }
                }
                // If we get here, the header was not found and it was not MustUnderstand
                // The spec says we can just proceed.
                System.out.println("No security token found, but it was not mandatory.");
                return true;
            } catch (SOAPException e) {
                System.err.println("Error processing SOAP header: " + e.getMessage());
                // The exception will cause the framework to send the fault
                return false;
            }
        }
        return true; // Continue processing for outbound messages
    }
    // ... other required methods (getHeaders, close) ...
}

JAX-WS vs. Jakarta EE Package Names

The only difference between the two is the package name. If you are working on an older Java EE project, you would use javax.xml.* instead of jakarta.xml.*.

Jakarta EE (Current) JAX-WS (Legacy)
jakarta.xml.soap.* javax.xml.soap.*
jakarta.xml.ws.* javax.xml.ws.*
jakarta.jws.* javax.jws.*

For example, the HelloService interface in JAX-WS would be:

// JAX-WS version
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
// ... rest of the code is identical

Summary

Feature Description
Purpose To carry metadata about a SOAP message, separate from the application data in the body.
Java API Use Jakarta EE (jakarta.xml.soap, jakarta.xml.ws) for new projects. Use JAX-WS (javax.xml.*) for legacy projects.
Client-Side Use a Dispatch<SOAPMessage> object for fine-grained control. Create a SOAPHeader, add SOAPHeaderElements to it, and attach it to the SOAPEnvelope before sending.
Server-Side Use a SOAPHandler<SOAPMessageContext> to inspect the SOAPHeader of incoming requests. Perform validation logic in the handler.
Key Attributes mustUnderstand (critical) and relay (for routing) are standard SOAP attributes for header elements.
分享:
扫描分享到社交APP
上一篇
下一篇