← Back to Home

Module 4: HTTP and RESTful Services

Module Overview

Master HTTP methods and RESTful API concepts to build and interact with web services.

Learning Objectives

Making a Request to a RESTful API

HTTP Methods and RESTful API Requests

HTTP methods define the type of operation being performed on a resource in a RESTful API. Each method has specific semantics and use cases:

GET

Used to retrieve resources without modifying them. GET requests are:

  • Read-only - They should never modify data
  • Idempotent - Making the same request multiple times produces the same result
  • Cacheable - Responses can be cached for performance

Example of a GET request using Java's HttpClient:

HttpClient client = HttpClient.newHttpClient();

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/songs/imagine-dragons/radioactive"))
    .header("Accept", "application/json")
    .GET() // This is the default, so it could be omitted
    .build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

System.out.println("Status code: " + response.statusCode());
System.out.println("Response body: " + response.body());

POST

Used to create new resources. POST requests are:

  • Not idempotent - Multiple identical requests may create multiple resources
  • Not cacheable - Generally, responses should not be cached
  • Includes a request body - Contains the data for the new resource

Example of a POST request:

String jsonBody = """ { "artist": "Imagine Dragons", "songTitle": "Believer", "albumTitle": "Evolve", "yearReleased": 2017, "genres": ["Alternative Rock", "Pop Rock"] } """;

HttpRequest postRequest = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/songs"))
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

HttpResponse<String> postResponse = client.send(postRequest, HttpResponse.BodyHandlers.ofString());

// Expect a 201 Created status code for successful creation
System.out.println("Status code: " + postResponse.statusCode());
System.out.println("Response body: " + postResponse.body());

PUT

Used to update existing resources or create them if they don't exist. PUT requests are:

  • Idempotent - Multiple identical requests have the same effect as a single request
  • Complete replacement - The entire resource is replaced with the request payload
  • Requires the complete resource - All fields should be provided

Example of a PUT request:

String updateJson = """ { "artist": "Imagine Dragons", "songTitle": "Believer", "albumTitle": "Evolve", "yearReleased": 2017, "genres": ["Alternative Rock", "Pop Rock", "Electronic"] } """;

HttpRequest putRequest = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/songs/imagine-dragons/believer"))
    .header("Content-Type", "application/json")
    .PUT(HttpRequest.BodyPublishers.ofString(updateJson))
    .build();

HttpResponse<String> putResponse = client.send(putRequest, HttpResponse.BodyHandlers.ofString());

// Expect a 200 OK or 204 No Content for successful update
System.out.println("Status code: " + putResponse.statusCode());

DELETE

Used to remove resources. DELETE requests are:

  • Idempotent - Multiple identical requests have the same effect (resource remains deleted)
  • Typically don't include a request body - The resource to delete is identified by the URL

Example of a DELETE request:

HttpRequest deleteRequest = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/songs/imagine-dragons/believer"))
    .DELETE()
    .build();

HttpResponse<String> deleteResponse = client.send(deleteRequest, HttpResponse.BodyHandlers.ofString());

// Expect a 204 No Content for successful deletion
System.out.println("Status code: " + deleteResponse.statusCode());

HTTP Status Codes

HTTP status codes indicate the result of the request:

  • 2xx (Success) - The request was successfully received, understood, and accepted
    • 200 OK - Standard success response
    • 201 Created - Resource successfully created
    • 204 No Content - Success with no response body
  • 4xx (Client Error) - The request contains errors or cannot be fulfilled
    • 400 Bad Request - Invalid syntax or parameters
    • 401 Unauthorized - Authentication required
    • 403 Forbidden - Server understands but refuses the request
    • 404 Not Found - Resource doesn't exist
    • 409 Conflict - Request conflicts with server state
  • 5xx (Server Error) - The server failed to fulfill a valid request
    • 500 Internal Server Error - Generic server error
    • 502 Bad Gateway - Invalid response from upstream server
    • 503 Service Unavailable - Server temporarily unavailable

Consuming a RESTful API

Processing API Responses

When consuming RESTful APIs, you need to properly handle and process the responses:

Response Formats

RESTful APIs typically return data in JSON or XML format. JSON is the most common format due to its simplicity and compatibility with JavaScript:

// Example JSON response for a GET request to /songs/imagine-dragons/radioactive
{
  "artist": "Imagine Dragons",
  "songTitle": "Radioactive",
  "albumTitle": "Night Visions",
  "yearReleased": 2012,
  "genres": ["Alternative Rock", "Indie Rock"],
  "playCount": 10452687
}

Parsing JSON Responses

In Java, you can parse JSON responses using libraries like Jackson or Gson:

// Using Jackson for JSON processing
import com.fasterxml.jackson.databind.ObjectMapper;

// Send the GET request
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

// Check for successful response
if (response.statusCode() == 200) {
    // Parse JSON to Java object
    ObjectMapper mapper = new ObjectMapper();
    MusicItem song = mapper.readValue(response.body(), MusicItem.class);
    
    System.out.println("Loaded song: " + song.getSongTitle());
    System.out.println("Album: " + song.getAlbumTitle());
    System.out.println("Year: " + song.getYearReleased());
} else {
    System.out.println("Error: " + response.statusCode());
    // Handle error based on status code
}

Error Handling

Proper error handling is crucial when working with APIs:

try {
    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    
    switch (response.statusCode() / 100) {
        case 2: // 2xx Success
            // Process successful response
            break;
        case 4: // 4xx Client Error
            if (response.statusCode() == 404) {
                System.out.println("Resource not found");
            } else if (response.statusCode() == 401 || response.statusCode() == 403) {
                System.out.println("Authentication or authorization error");
            } else {
                System.out.println("Client error: " + response.statusCode());
            }
            break;
        case 5: // 5xx Server Error
            System.out.println("Server error: " + response.statusCode());
            // Implement retry logic for 5xx errors
            break;
    }
} catch (IOException e) {
    System.out.println("Network error: " + e.getMessage());
} catch (InterruptedException e) {
    System.out.println("Request interrupted: " + e.getMessage());
    Thread.currentThread().interrupt();
}

Testing with cURL

cURL is a command-line tool for making HTTP requests, useful for testing APIs:

# GET request
curl -X GET https://api.example.com/songs/imagine-dragons/radioactive

# POST request with JSON body
curl -X POST https://api.example.com/songs \
-H "Content-Type: application/json" \
-d '{"artist":"Imagine Dragons","songTitle":"Believer","albumTitle":"Evolve","yearReleased":2017}'

# PUT request
curl -X PUT https://api.example.com/songs/imagine-dragons/believer \
-H "Content-Type: application/json" \
-d '{"artist":"Imagine Dragons","songTitle":"Believer","albumTitle":"Evolve","yearReleased":2017,"genres":["Alternative Rock","Pop Rock"]}'

# DELETE request
curl -X DELETE https://api.example.com/songs/imagine-dragons/believer

HTTP and RESTful Services Overview

RESTful API Design Best Practices

When designing or working with RESTful APIs, keep these best practices in mind:

1. Resource Naming

  • Use nouns, not verbs - Resources should be named as nouns, not actions
  • Use plurals - Collections should be plural (e.g., /songs, not /song)
  • Use lowercase - All URLs should be lowercase
  • Use hyphens for readability - Use hyphens (-) instead of underscores (_)

2. Resource Hierarchy

# Good examples of RESTful URL patterns
/artists # List all artists
/artists/123 # Get a specific artist
/artists/123/albums # List all albums by artist 123
/artists/123/albums/456 # Get a specific album by artist 123
/artists/123/albums/456/songs # List all songs in album 456 by artist 123

3. Use HTTP Methods Correctly

  • GET for reading (never for modifying)
  • POST for creating
  • PUT for complete updates
  • PATCH for partial updates
  • DELETE for removing

4. Always Use HTTPS

Secure your API with HTTPS to protect data in transit. This is not optional in production environments.

5. Versioning

Include API versioning to make changes without breaking existing clients:

# Versioning in URL path
/api/v1/songs

# Versioning in header
curl -H "Accept: application/vnd.example.v1+json" https://api.example.com/songs

6. Use Proper Status Codes

Return appropriate HTTP status codes for different scenarios:

  • 200 OK for successful GET, PUT, or PATCH
  • 201 Created for successful POST
  • 204 No Content for successful DELETE
  • 400 Bad Request for validation errors
  • 401 Unauthorized for missing authentication
  • 403 Forbidden for insufficient permissions
  • 404 Not Found for non-existent resources
  • 500 Internal Server Error for server-side issues

7. Pagination, Filtering, and Sorting

For collections, support pagination, filtering, and sorting via query parameters:

# Pagination
/songs?page=2&limit=10

# Filtering
/songs?genre=rock&year=2020

# Sorting
/songs?sort=yearReleased:desc

8. Error Handling

Return descriptive error responses with appropriate status codes:

# Example error response
{
  "status": 400,
  "error": "Bad Request",
  "message": "Validation failed",
  "errors": [
    {
      "field": "yearReleased",
      "message": "Year must be between 1900 and current year"
    }
  ],
  "timestamp": "2023-06-01T12:34:56Z",
  "path": "/songs"
}

9. Idempotency

Understand and respect idempotency:

  • Idempotent operations produce the same result regardless of how many times they're called
  • GET, PUT, DELETE are idempotent
  • POST is not idempotent

This is especially important for error handling and retries. If a request fails due to a network issue, idempotent operations can be safely retried without side effects.

Resources