Spring SOAP vs REST
Introduction
Web services are essential components of modern applications, allowing different systems to communicate over a network. In the Spring ecosystem, developers can implement two main types of web services: SOAP (Simple Object Access Protocol) and REST (Representational State Transfer). This guide explores both approaches, their differences, implementation details in Spring, and when to choose one over the other.
Understanding Web Services
Before diving into the specifics of SOAP and REST, let's understand what web services are:
Web services are standardized communication mechanisms that allow different applications to exchange data over a network, regardless of the programming languages or platforms used. They enable interoperability between systems running on various technologies.
SOAP Web Services
SOAP (Simple Object Access Protocol) is a protocol specification for exchanging structured information in web services using XML.
Key Characteristics of SOAP
- Protocol-based: SOAP is a protocol with specific rules and standards
- XML-based: All messages are formatted in XML
- Strongly typed: Uses WSDL (Web Services Description Language) to describe services
- Transport independent: Can use HTTP, SMTP, TCP, or JMS
- Stateless by default: But can be made stateful
- Built-in error handling: Has standardized fault messages
Spring SOAP Implementation
Spring provides the Spring Web Services (Spring-WS) module for implementing SOAP services.
Setting up a Spring SOAP Service
- First, add the required dependencies to your
pom.xml
:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>2.5.0</version>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<sources>
<source>${project.basedir}/src/main/resources/schema/students.xsd</source>
</sources>
</configuration>
</plugin>
</plugins>
</build>
- Define an XSD schema (
students.xsd
):
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://example.org/students"
targetNamespace="http://example.org/students"
elementFormDefault="qualified">
<xs:element name="getStudentRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="id" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="getStudentResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="student" type="tns:student"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="student">
<xs:sequence>
<xs:element name="id" type="xs:int"/>
<xs:element name="name" type="xs:string"/>
<xs:element name="grade" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
- Configure the web service endpoints:
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;
@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
@Bean
public ServletRegistrationBean<MessageDispatcherServlet> messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean<>(servlet, "/ws/*");
}
@Bean(name = "students")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema studentsSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("StudentsPort");
wsdl11Definition.setLocationUri("/ws");
wsdl11Definition.setTargetNamespace("http://example.org/students");
wsdl11Definition.setSchema(studentsSchema);
return wsdl11Definition;
}
@Bean
public XsdSchema studentsSchema() {
return new SimpleXsdSchema(new ClassPathResource("schema/students.xsd"));
}
}
- Create a service endpoint:
import org.example.students.GetStudentRequest;
import org.example.students.GetStudentResponse;
import org.example.students.Student;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
@Endpoint
public class StudentEndpoint {
private static final String NAMESPACE_URI = "http://example.org/students";
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "getStudentRequest")
@ResponsePayload
public GetStudentResponse getStudent(@RequestPayload GetStudentRequest request) {
GetStudentResponse response = new GetStudentResponse();
Student student = new Student();
student.setId(request.getId());
student.setName("John Doe");
student.setGrade(85);
response.setStudent(student);
return response;
}
}
- With this setup, your SOAP service is ready to process requests.
SOAP Request and Response Examples
Request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:stud="http://example.org/students">
<soapenv:Header/>
<soapenv:Body>
<stud:getStudentRequest>
<stud:id>123</stud:id>
</stud:getStudentRequest>
</soapenv:Body>
</soapenv:Envelope>
Response:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns2:getStudentResponse xmlns:ns2="http://example.org/students">
<ns2:student>
<ns2:id>123</ns2:id>
<ns2:name>John Doe</ns2:name>
<ns2:grade>85</ns2:grade>
</ns2:student>
</ns2:getStudentResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
REST Web Services
REST (Representational State Transfer) is an architectural style for designing networked applications, typically using HTTP methods explicitly.
Key Characteristics of REST
- Architecture style: Not a protocol but an architectural approach
- Format independent: Can use JSON, XML, HTML, plain text, etc. (JSON is most common)
- Uses standard HTTP methods: GET, POST, PUT, DELETE, PATCH
- Stateless: Each request is independent
- Resource-based: URLs represent resources
- Lightweight: Simpler than SOAP
Spring REST Implementation
Spring provides comprehensive support for REST via Spring MVC and Spring Boot.
Setting up a Spring REST Service
- Add the required dependencies to your
pom.xml
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- Create a model class:
public class Student {
private int id;
private String name;
private int grade;
// Getters and setters
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getGrade() { return grade; }
public void setGrade(int grade) { this.grade = grade; }
}
- Create a REST controller:
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/students")
public class StudentController {
@GetMapping("/{id}")
public Student getStudent(@PathVariable int id) {
// In a real app, you would fetch this from a database
Student student = new Student();
student.setId(id);
student.setName("John Doe");
student.setGrade(85);
return student;
}
@PostMapping
public Student createStudent(@RequestBody Student student) {
// In a real app, you would save to a database
// For this example, just return the student with an ID
student.setId(999);
return student;
}
@PutMapping("/{id}")
public Student updateStudent(@PathVariable int id, @RequestBody Student student) {
student.setId(id);
// In real app, update in database
return student;
}
@DeleteMapping("/{id}")
public void deleteStudent(@PathVariable int id) {
// Delete from database in real app
}
}
- Your REST API is ready to use with these endpoints.
REST Request and Response Examples
GET Request:
GET /api/students/123 HTTP/1.1
Host: localhost:8080
Accept: application/json
GET Response:
{
"id": 123,
"name": "John Doe",
"grade": 85
}
POST Request:
POST /api/students HTTP/1.1
Host: localhost:8080
Content-Type: application/json
{
"name": "Jane Smith",
"grade": 92
}
POST Response:
{
"id": 999,
"name": "Jane Smith",
"grade": 92
}
SOAP vs REST: Key Differences
Let's compare these approaches across several important dimensions:
Feature | SOAP | REST |
---|---|---|
Format | XML only | Multiple (JSON, XML, etc.) |
Protocol | Protocol-specific | Uses HTTP methods |
Bandwidth | Higher (XML verbose) | Lower (JSON compact) |
Service Definition | WSDL | No standard (OpenAPI/Swagger) |
Security | Built-in standards (WS-Security) | Relies on HTTP security mechanisms |
Ease of Use | Complex, steep learning curve | Simpler, easier to implement |
Flexibility | Less flexible, formal | More flexible |
Performance | More overhead | Lighter weight |
Caching | Difficult | Easy with HTTP caching |
State | Can be stateful | Stateless |
When to Choose SOAP vs REST
Choose SOAP when:
- You need formal contracts between service and client
- You require standardized security, transactions, and reliability
- You need stateful operations
- You're working with enterprise systems that require SOAP
- You need ACID-compliant transactions
Choose REST when:
- You need a lightweight, efficient solution
- You're building public APIs
- You have limited bandwidth or resources
- You want to maximize caching capabilities
- You're developing mobile applications
- You need scalability
Real-World Example: Building a Student Management System
Let's design a student management system using both approaches for comparison.
SOAP Implementation for Student Records
This example would build on our previous SOAP example, expanding it to handle multiple operations:
-
Add operations to the XSD:
- Add student
- Update student
- Delete student
- Get all students
-
Implement the endpoint methods for each operation
-
Generate a comprehensive WSDL that documents all operations
This approach would be ideal for an enterprise system where multiple internal applications need to interact with student records in a standardized way.
REST Implementation for Student Records
Our REST API would expose multiple endpoints:
@RestController
@RequestMapping("/api/students")
public class StudentRestController {
private final StudentService studentService;
public StudentRestController(StudentService studentService) {
this.studentService = studentService;
}
@GetMapping
public List<Student> getAllStudents() {
return studentService.findAllStudents();
}
@GetMapping("/{id}")
public ResponseEntity<Student> getStudentById(@PathVariable int id) {
return studentService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping
public ResponseEntity<Student> createStudent(@RequestBody Student student) {
Student saved = studentService.saveStudent(student);
URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(saved.getId())
.toUri();
return ResponseEntity.created(location).body(saved);
}
@PutMapping("/{id}")
public ResponseEntity<Student> updateStudent(@PathVariable int id, @RequestBody Student student) {
return studentService.findById(id)
.map(existingStudent -> {
student.setId(id);
Student updated = studentService.saveStudent(student);
return ResponseEntity.ok(updated);
})
.orElse(ResponseEntity.notFound().build());
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteStudent(@PathVariable int id) {
return studentService.findById(id)
.map(student -> {
studentService.deleteStudent(id);
return ResponseEntity.noContent().<Void>build();
})
.orElse(ResponseEntity.notFound().build());
}
}
This approach would be ideal for a modern web application where multiple clients (web, mobile) need to access student data, with emphasis on performance and ease of integration.
Best Practices
SOAP Best Practices
- Generate client code from WSDL
- Cache WSDL files locally
- Handle SOAP faults appropriately
- Use WS-Security for secure communications
- Follow naming conventions for operations
- Version your services
REST Best Practices
- Use proper HTTP methods for operations
- Return appropriate HTTP status codes
- Include hypermedia links (HATEOAS)
- Version your APIs
- Use consistent naming conventions
- Implement proper error handling
- Use pagination for large data sets
- Implement security with OAuth or JWT
Summary
Both SOAP and REST have their place in modern application development:
- SOAP provides a robust, standardized approach with built-in features for enterprise applications, but comes with overhead and complexity.
- REST offers a lightweight, flexible architecture that leverages HTTP and is easier to implement and consume, making it ideal for most public APIs and modern applications.
In the Spring ecosystem, you have excellent support for both:
- Spring WS for SOAP
- Spring MVC/Boot for REST
The choice between SOAP and REST should be based on your specific requirements, existing infrastructure, and the needs of your clients. Many organizations use both: REST for public-facing APIs and SOAP for internal enterprise integration.
Additional Resources
- Spring Web Services Documentation
- Spring REST Documentation
- Understanding SOAP vs REST W3C Standards
- REST API Design Best Practices
Exercises
- Create a SOAP web service with Spring that supports CRUD operations for a "Product" entity.
- Implement the same functionality using Spring REST controllers.
- Create a client application that consumes both services and compare the implementation complexity.
- Add security to both implementations (WS-Security for SOAP, OAuth2 for REST).
- Measure and compare the performance of both implementations under load.
By completing these exercises, you'll gain a deeper understanding of both approaches and their practical applications in real-world scenarios.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)