Emre Göçmen Blog

SAP OData Servislerini Tüketme ve Entegre Etme

5 dk. okuma
1304 görüntülenme
0 yorum

Emre Göçmen

Yazar

SAP OData Servislerini Tüketme ve Entegre Etme

SAP OData Servislerini Tüketme Rehberi

SAP OData servisleri, kurumsal verilere standart bir HTTP tabanlı protokol üzerinden erişim sağlayan güçlü bir API teknolojisidir. Bu kapsamlı rehber, farklı uygulama türleri için OData servislerini nasıl verimli ve güvenli bir şekilde tüketebileceğinizi açıklamaktadır.


OData Servislerini Tüketme Yöntemleri

1. HTTP İstekleri ile Doğrudan Tüketim

En temel düzeyde, OData servisleri standart HTTP istekleri kullanılarak tüketilebilir:

GET: Veri okuma işlemleri için

// Örnek GET isteği - Malzeme verilerini almak için
GET https://sap-server.example.com/sap/opu/odata/SAP/ZMATERIAL_SRV/MaterialSet
Accept: application/json
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

POST: Yeni veri oluşturmak için

// Örnek POST isteği - Yeni malzeme oluşturmak için
POST https://sap-server.example.com/sap/opu/odata/SAP/ZMATERIAL_SRV/MaterialSet
Content-Type: application/json
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

{
  "Matnr": "1000001",
  "Maktx": "Test Malzemesi",
  "Meins": "ST",
  "Mtart": "FERT",
  "Matkl": "001"
}

PUT/PATCH: Mevcut veriyi güncellemek için

// Örnek PUT isteği - Malzeme güncellemek için
PUT https://sap-server.example.com/sap/opu/odata/SAP/ZMATERIAL_SRV/MaterialSet('1000001')
Content-Type: application/json
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

{
  "Maktx": "Güncellenmiş Malzeme Tanımı",
  "Matkl": "002"
}

DELETE: Veri silmek için

// Örnek DELETE isteği - Malzeme silmek için
DELETE https://sap-server.example.com/sap/opu/odata/SAP/ZMATERIAL_SRV/MaterialSet('1000001')
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

2. OData İstemci Kütüphaneleri ile Tüketim

Çeşitli programlama dilleri için özel OData istemci kütüphaneleri mevcuttur:

JavaScript - OData.js: Web uygulamaları için popüler bir OData istemcisi

// OData.js ile müşteri verilerini sorgulama örneği
var odataUrl = "https://sap-server.example.com/sap/opu/odata/SAP/ZCUSTOMER_SRV/";

// OData istemcisini başlat
var oDataClient = new OData.Service({
  url: odataUrl,
  user: "username",
  password: "password"
});

// Müşteri verilerini sorgula
oDataClient.CustomerSet
  .filter("PostalCode eq '34000'") // İstanbul'daki müşteriler
  .expand("Orders")                // Siparişleri dahil et
  .select("CustomerID,CompanyName,ContactName,Orders/OrderID,Orders/OrderDate")
  .orderBy("CompanyName")
  .take(10)
  .execute()
  .then(function(customers) {
    // Müşteri verilerini işle
    customers.forEach(function(customer) {
      console.log(customer.CompanyName + ": " + customer.Orders.length + " sipariş");
    });
  })
  .catch(function(error) {
    console.error("Veri sorgulama hatası:", error);
  });

Java - Olingo: Java tabanlı uygulamalar için Apache Olingo

// Apache Olingo ile OData servisine erişim örneği
import org.apache.olingo.client.api.ODataClient;
import org.apache.olingo.client.api.domain.ClientEntity;
import org.apache.olingo.client.api.domain.ClientEntitySet;
import org.apache.olingo.client.core.ODataClientFactory;

// OData istemcisini oluştur
ODataClient client = ODataClientFactory.getClient();

// Kimlik doğrulama başlıklarını hazırla
String basicAuth = "Basic " + Base64.getEncoder().encodeToString("username:password".getBytes());

// Müşteri verilerini getir
URI uri = client.newURIBuilder("https://sap-server.example.com/sap/opu/odata/SAP/ZCUSTOMER_SRV")
                .appendEntitySetSegment("CustomerSet")
                .filter("City eq 'İstanbul'")
                .orderBy("CompanyName")
                .top(20)
                .build();

// İsteği gönder
ClientEntitySet customers = client.getRetrieveRequestFactory()
                                  .getEntitySetRequest(uri)
                                  .addCustomHeader("Authorization", basicAuth)
                                  .execute()
                                  .getBody();

// Sonuçları işle
for (ClientEntity customer : customers.getEntities()) {
    String companyName = customer.getProperty("CompanyName").getValue().toString();
    String contactName = customer.getProperty("ContactName").getValue().toString();
    System.out.println(companyName + " - " + contactName);
}

C# - Simple.OData.Client: .NET uygulamaları için OData istemcisi

// C# ve Simple.OData.Client ile OData tüketimi
using Simple.OData.Client;
using System.Collections.Generic;
using System.Threading.Tasks;

// OData istemcisini yapılandır
var clientSettings = new ODataClientSettings
{
    BaseUri = new Uri("https://sap-server.example.com/sap/opu/odata/SAP/ZMATERIAL_SRV/"),
    Credentials = new NetworkCredential("username", "password")
};

var client = new ODataClient(clientSettings);

// Malzemeleri asenkron olarak sorgula
async Task GetMaterialsAsync()
{
    try
    {
        // Filtrelenmiş malzeme listesini getir
        var materials = await client
            .For("MaterialSet")
            .Filter("Matkl eq '001'") // Belirli bir malzeme grubunu filtrele
            .Select("Matnr,Maktx,Meins,Mtart")
            .OrderBy("Maktx")
            .Top(50)
            .FindEntriesAsync();

        // Sonuçları işle
        foreach (var material in materials)
        {
            Console.WriteLine($"Malzeme: {material["Matnr"]} - {material["Maktx"]}");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Hata: {ex.Message}");
    }
}

3. SAP UI5 ile OData Tüketimi

SAP UI5, OData servislerini tüketmek için özel olarak tasarlanmış model ve bağlayıcılar sunar:

// SAP UI5 ile OData Model oluşturma
sap.ui.define([
  "sap/ui/core/mvc/Controller",
  "sap/ui/model/odata/v2/ODataModel"
], function(Controller, ODataModel) {
  "use strict";
  
  return Controller.extend("app.controller.MaterialList", {
    onInit: function() {
      // OData modelini oluştur
      var oDataModel = new ODataModel({
        serviceUrl: "/sap/opu/odata/SAP/ZMATERIAL_SRV/",
        useBatch: false,
        defaultCountMode: "Inline"
      });
      
      // Modeli view'a bağla
      this.getView().setModel(oDataModel);
      
      // Verileri yükle
      this.loadMaterials();
    },
    
    loadMaterials: function() {
      var oTable = this.byId("materialsTable");
      var oBinding = oTable.getBinding("items");
      
      // Filtreleri ayarla
      var aFilters = [];
      aFilters.push(new sap.ui.model.Filter("Mtart", "EQ", "FERT"));
      
      // Sıralama ayarla
      var aSorters = [];
      aSorters.push(new sap.ui.model.Sorter("Maktx", false));
      
      // Verileri filtrele ve sırala
      oBinding.filter(aFilters);
      oBinding.sort(aSorters);
    },
    
    onMaterialSelected: function(oEvent) {
      var oContext = oEvent.getSource().getBindingContext();
      var sPath = oContext.getPath();
      var oMaterial = oContext.getObject();
      
      // Seçilen malzeme ile işlem yap
      MessageToast.show("Seçilen malzeme: " + oMaterial.Maktx);
    }
  });
});

SAP UI5 XML View örneği:

<mvc:View
  controllerName="app.controller.MaterialList"
  xmlns="sap.m"
  xmlns:mvc="sap.ui.core.mvc">
  
  <Page title="Malzeme Listesi">
    <Table id="materialsTable"
           items="{
             path: '/MaterialSet',
             parameters: {
               $select: 'Matnr,Maktx,Meins,Mtart,Matkl',
               $top: 50
             }
           }">
      <columns>
        <Column><Text text="Malzeme No"/></Column>
        <Column><Text text="Tanım"/></Column>
        <Column><Text text="Birim"/></Column>
        <Column><Text text="Tür"/></Column>
      </columns>
      <items>
        <ColumnListItem type="Navigation" press=".onMaterialSelected">
          <cells>
            <Text text="{Matnr}" />
            <Text text="{Maktx}" />
            <Text text="{Meins}" />
            <Text text="{Mtart}" />
          </cells>
        </ColumnListItem>
      </items>
    </Table>
  </Page>
</mvc:View>

Farklı Uygulama Tiplerine Entegrasyon

1. Web Uygulamaları için OData Entegrasyonu

Modern web uygulamaları (React, Angular, Vue.js vb.) OData servislerini kolayca tüketebilir:

React.js Entegrasyonu:

// React.js ile OData Servisi Entegrasyonu
import React, { useState, useEffect } from 'react';
import axios from 'axios';

function CustomerList() {
  const [customers, setCustomers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    // Axios ile OData servisini çağır
    const fetchCustomers = async () => {
      try {
        // Basic Auth için Base64 kodlu kimlik bilgileri
        const authHeader = 'Basic ' + btoa('username:password');
        
        const response = await axios.get(
          'https://sap-server.example.com/sap/opu/odata/SAP/ZCUSTOMER_SRV/CustomerSet',
          {
            params: {
              $format: 'json',
              $select: 'CustomerID,CompanyName,ContactName,City',
              $orderby: 'CompanyName',
              $filter: "City eq 'İstanbul'",
              $top: 20
            },
            headers: {
              'Authorization': authHeader,
              'Accept': 'application/json'
            }
          }
        );
        
        // Verileri state'e kaydet
        setCustomers(response.data.d.results);
        setLoading(false);
      } catch (err) {
        setError(err.message);
        setLoading(false);
      }
    };

    fetchCustomers();
  }, []);

  if (loading) return <p>Yükleniyor...</p>;
  if (error) return <p>Hata: {error}</p>;

  return (
    <div>
      <h2>Müşteri Listesi</h2>
      <table>
        <thead>
          <tr>
            <th>Müşteri ID</th>
            <th>Şirket Adı</th>
            <th>İlgili Kişi</th>
            <th>Şehir</th>
          </tr>
        </thead>
        <tbody>
          {customers.map(customer => (
            <tr key={customer.CustomerID}>
              <td>{customer.CustomerID}</td>
              <td>{customer.CompanyName}</td>
              <td>{customer.ContactName}</td>
              <td>{customer.City}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

Angular Entegrasyonu:

// Angular ile OData Servis Entegrasyonu
// customer.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class CustomerService {
  private apiUrl = 'https://sap-server.example.com/sap/opu/odata/SAP/ZCUSTOMER_SRV';
  
  constructor(private http: HttpClient) { }
  
  // Temel kimlik doğrulama için HTTP başlıkları oluştur
  private getHeaders(): HttpHeaders {
    const credentials = btoa('username:password');
    return new HttpHeaders({
      'Authorization': 'Basic ' + credentials,
      'Content-Type': 'application/json',
      'Accept': 'application/json'
    });
  }
  
  // Müşterileri getir
  getCustomers(city?: string, limit: number = 20): Observable {
    let url = `${this.apiUrl}/CustomerSet`;
    let params: any = {
      $format: 'json',
      $select: 'CustomerID,CompanyName,ContactName,City',
      $orderby: 'CompanyName',
      $top: limit
    };
    
    // Şehir filtresi ekle
    if (city) {
      params.$filter = `City eq '${city}'`;
    }
    
    return this.http.get(url, {
      headers: this.getHeaders(),
      params: params
    }).pipe(
      map((response: any) => response.d.results)
    );
  }
  
  // Müşteri detayını getir
  getCustomerDetail(customerId: string): Observable {
    const url = `${this.apiUrl}/CustomerSet('${customerId}')`;
    
    return this.http.get(url, {
      headers: this.getHeaders(),
      params: {
        $format: 'json',
        $expand: 'Orders' // Siparişleri de getir
      }
    }).pipe(
      map((response: any) => response.d)
    );
  }
  
  // Yeni müşteri oluştur
  createCustomer(customer: any): Observable {
    const url = `${this.apiUrl}/CustomerSet`;
    
    return this.http.post(url, customer, {
      headers: this.getHeaders()
    }).pipe(
      map((response: any) => response.d)
    );
  }
}

// customer-list.component.ts
import { Component, OnInit } from '@angular/core';
import { CustomerService } from './customer.service';

@Component({
  selector: 'app-customer-list',
  templateUrl: './customer-list.component.html'
})
export class CustomerListComponent implements OnInit {
  customers: any[] = [];
  loading = true;
  error: string | null = null;
  
  constructor(private customerService: CustomerService) { }
  
  ngOnInit(): void {
    this.loadCustomers();
  }
  
  loadCustomers(city?: string): void {
    this.loading = true;
    this.customerService.getCustomers(city)
      .subscribe(
        data => {
          this.customers = data;
          this.loading = false;
        },
        err => {
          this.error = err.message;
          this.loading = false;
        }
      );
  }
}

2. Mobil Uygulamalar için OData Entegrasyonu

Mobil uygulamalar da benzer şekilde OData servislerini tüketebilir:

React Native Entegrasyonu:

// React Native ile OData Entegrasyonu
import React, { useState, useEffect } from 'react';
import { View, Text, FlatList, StyleSheet, ActivityIndicator } from 'react-native';
import axios from 'axios';

const MaterialScreen = () => {
  const [materials, setMaterials] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetchMaterials();
  }, []);

  const fetchMaterials = async () => {
    try {
      // Basic Auth kimlik bilgileri
      const authHeader = 'Basic ' + btoa('username:password');
      
      const response = await axios.get(
        'https://sap-server.example.com/sap/opu/odata/SAP/ZMATERIAL_SRV/MaterialSet',
        {
          params: {
            $format: 'json',
            $select: 'Matnr,Maktx,Meins',
            $filter: "Mtart eq 'FERT'",
            $top: 30
          },
          headers: {
            'Authorization': authHeader,
            'Accept': 'application/json'
          }
        }
      );
      
      setMaterials(response.data.d.results);
      setLoading(false);
    } catch (err) {
      console.error(err);
      setError('Veri alınamadı. Lütfen bağlantınızı kontrol edin.');
      setLoading(false);
    }
  };

  if (loading) {
    return (
      <View style={styles.centered}>
        <ActivityIndicator size="large" color="#0000ff" />
      </View>
    );
  }

  if (error) {
    return (
      <View style={styles.centered}>
        <Text style={styles.errorText}>{error}</Text>
      </View>
    );
  }

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Malzeme Listesi</Text>
      <FlatList
        data={materials}
        keyExtractor={item => item.Matnr}
        renderItem={({ item }) => (
          <View style={styles.itemContainer}>
            <Text style={styles.materialNumber}>{item.Matnr}</Text>
            <Text style={styles.materialName}>{item.Maktx}</Text>
            <Text style={styles.materialUnit}>Birim: {item.Meins}</Text>
          </View>
        )}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
    backgroundColor: '#f5f5f5'
  },
  centered: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  },
  title: {
    fontSize: 22,
    fontWeight: 'bold',
    marginBottom: 16
  },
  itemContainer: {
    backgroundColor: 'white',
    padding: 16,
    marginBottom: 8,
    borderRadius: 4,
    elevation: 2
  },
  materialNumber: {
    fontSize: 14,
    color: '#555'
  },
  materialName: {
    fontSize: 18,
    fontWeight: '500',
    marginVertical: 4
  },
  materialUnit: {
    fontSize: 14,
    color: '#666'
  },
  errorText: {
    color: 'red',
    fontSize: 16
  }
});

export default MaterialScreen;

Flutter Entegrasyonu:

// Flutter ile OData Entegrasyonu
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

class Material {
  final String id;
  final String name;
  final String unit;

  Material({required this.id, required this.name, required this.unit});

  factory Material.fromJson(Map<String, dynamic> json) {
    return Material(
      id: json['Matnr'],
      name: json['Maktx'],
      unit: json['Meins'],
    );
  }
}

class MaterialListScreen extends StatefulWidget {
  @override
  _MaterialListScreenState createState() => _MaterialListScreenState();
}

class _MaterialListScreenState extends State<MaterialListScreen> {
  List<Material> _materials = [];
  bool _isLoading = true;
  String _errorMessage = '';

  @override
  void initState() {
    super.initState();
    _fetchMaterials();
  }

  Future<void> _fetchMaterials() async {
    try {
      // Basic Auth kimlik bilgileri
      String credentials = base64Encode(utf8.encode('username:password'));
      
      // OData isteği
      final response = await http.get(
        Uri.parse('https://sap-server.example.com/sap/opu/odata/SAP/ZMATERIAL_SRV/MaterialSet'
          '?\$format=json'
          '&\$select=Matnr,Maktx,Meins'
          '&\$filter=Mtart%20eq%20%27FERT%27'
          '&\$top=30'),
        headers: {
          'Authorization': 'Basic $credentials',
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
      );

      if (response.statusCode == 200) {
        final Map<String, dynamic> responseData = json.decode(response.body);
        final List<dynamic> materialsJson = responseData['d']['results'];
        
        setState(() {
          _materials = materialsJson
              .map((json) => Material.fromJson(json))
              .toList();
          _isLoading = false;
        });
      } else {
        throw Exception('HTTP Hatası: ${response.statusCode}');
      }
    } catch (e) {
      setState(() {
        _errorMessage = e.toString();
        _isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Malzeme Listesi'),
      ),
      body: _isLoading
          ? Center(child: CircularProgressIndicator())
          : _errorMessage.isNotEmpty
              ? Center(
                  child: Text(
                    'Hata: $_errorMessage',
                    style: TextStyle(color: Colors.red),
                  ),
                )
              : ListView.builder(
                  itemCount: _materials.length,
                  itemBuilder: (context, index) {
                    return Card(
                      margin: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
                      child: ListTile(
                        title: Text(_materials[index].name),
                        subtitle: Text('No: ${_materials[index].id}'),
                        trailing: Text('${_materials[index].unit}'),
                        onTap: () {
                          // Malzeme detayı için işlemler
                        },
                      ),
                    );
                  },
                ),
    );
  }
}

3. Masaüstü Uygulamaları için OData Entegrasyonu

C# WPF Uygulaması:

// C# WPF ile OData Entegrasyonu
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace SapOdataWpfApp
{
    public class Customer : INotifyPropertyChanged
    {
        private string _customerId;
        private string _companyName;
        private string _contactName;
        private string _city;

        public string CustomerId {
            get => _customerId;
            set {
                _customerId = value;
                OnPropertyChanged(nameof(CustomerId));
            }
        }

        public string CompanyName {
            get => _companyName;
            set {
                _companyName = value;
                OnPropertyChanged(nameof(CompanyName));
            }
        }

        public string ContactName {
            get => _contactName;
            set {
                _contactName = value;
                OnPropertyChanged(nameof(ContactName));
            }
        }

        public string City {
            get => _city;
            set {
                _city = value;
                OnPropertyChanged(nameof(City));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class MainWindowViewModel : INotifyPropertyChanged
    {
        private ObservableCollection<Customer> _customers;
        private bool _isLoading;
        private string _errorMessage;
        private string _selectedCity;

        public ObservableCollection<Customer> Customers {
            get => _customers;
            set {
                _customers = value;
                OnPropertyChanged(nameof(Customers));
            }
        }

        public bool IsLoading {
            get => _isLoading;
            set {
                _isLoading = value;
                OnPropertyChanged(nameof(IsLoading));
            }
        }

        public string ErrorMessage {
            get => _errorMessage;
            set {
                _errorMessage = value;
                OnPropertyChanged(nameof(ErrorMessage));
                OnPropertyChanged(nameof(HasError));
            }
        }

        public bool HasError => !string.IsNullOrEmpty(ErrorMessage);

        public string SelectedCity {
            get => _selectedCity;
            set {
                _selectedCity = value;
                OnPropertyChanged(nameof(SelectedCity));
                LoadCustomersAsync(_selectedCity);
            }
        }

        public List<string> Cities { get; } = new List<string> {
            "İstanbul", "Ankara", "İzmir", "Bursa", "Adana"
        };

        public MainWindowViewModel()
        {
            Customers = new ObservableCollection<Customer>();
            LoadCustomersAsync();
        }

        public async Task LoadCustomersAsync(string city = null)
        {
            IsLoading = true;
            ErrorMessage = string.Empty;

            try
            {
                using (var client = new HttpClient())
                {
                    // Basic Auth için kimlik doğrulama ekle
                    var authToken = Convert.ToBase64String(Encoding.ASCII.GetBytes("username:password"));
                    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authToken);
                    
                    // OData URL oluştur
                    var baseUrl = "https://sap-server.example.com/sap/opu/odata/SAP/ZCUSTOMER_SRV/CustomerSet";
                    var queryParams = new List<string> {
                        "$format=json",
                        "$select=CustomerID,CompanyName,ContactName,City",
                        "$orderby=CompanyName"
                    };
                    
                    // Şehir filtresi ekle
                    if (!string.IsNullOrEmpty(city))
                    {
                        queryParams.Add($"$filter=City eq '{city}'");
                    }
                    
                    var url = $"{baseUrl}?{string.Join("&", queryParams)}";
                    
                    // API çağrısı
                    var response = await client.GetAsync(url);
                    response.EnsureSuccessStatusCode();
                    
                    var json = await response.Content.ReadAsStringAsync();
                    var jObject = JObject.Parse(json);
                    var results = jObject["d"]["results"] as JArray;
                    
                    // Sonuçları modele dönüştür
                    Application.Current.Dispatcher.Invoke(() => {
                        Customers.Clear();
                        foreach (var item in results)
                        {
                            Customers.Add(new Customer
                            {
                                CustomerId = item["CustomerID"].ToString(),
                                CompanyName = item["CompanyName"].ToString(),
                                ContactName = item["ContactName"].ToString(),
                                City = item["City"].ToString()
                            });
                        }
                    });
                }
            }
            catch (Exception ex)
            {
                ErrorMessage = $"Veri yüklenirken hata oluştu: {ex.Message}";
            }
            finally
            {
                IsLoading = false;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Performans ve Güvenlik İlkeleri

1. Performans Optimizasyonu

OData servislerini tüketirken performansı optimize etmek için çeşitli teknikler:

Veri Miktarını Optimize Etme: Sadece gerekli alanları seçin ($select) ve gerekli kayıtları filtreleyerek yükü azaltın ($filter).

// Sadece gerekli alanları seçme örneği
GET https://sap-server.example.com/sap/opu/odata/SAP/ZMATERIAL_SRV/MaterialSet?$select=Matnr,Maktx,Meins&$top=50

İstemci Tarafında Önbellekleme: Sık kullanılan referans verileri önbelleğe alarak ağ trafiğini azaltın.

// React uygulamasında yerel depolama ile önbellekleme örneği
const fetchMaterials = async () => {
  // Önbellekte veri var mı kontrol et
  const cachedData = localStorage.getItem('materials');
  const cacheTimestamp = localStorage.getItem('materialsTimestamp');
  const cacheAge = cacheTimestamp ? (Date.now() - parseInt(cacheTimestamp)) : 0;
  
  // Önbellek 30 dakikadan yeni ise kullan
  if (cachedData && cacheAge < 30 * 60 * 1000) {
    return JSON.parse(cachedData);
  }
  
  // Değilse yeni veri getir
  try {
    const response = await axios.get('/sap/opu/odata/SAP/ZMATERIAL_SRV/MaterialSet', {
      params: {
        $select: 'Matnr,Maktx,Meins',
        $top: 100
      }
    });
    
    const materials = response.data.d.results;
    
    // Önbelleğe al
    localStorage.setItem('materials', JSON.stringify(materials));
    localStorage.setItem('materialsTimestamp', Date.now().toString());
    
    return materials;
  } catch (error) {
    console.error('Veri getirme hatası:', error);
    
    // Hata durumunda önbellekteki son veriyi kullan
    if (cachedData) {
      return JSON.parse(cachedData);
    }
    throw error;
  }
};

Batch İşlemler: Birden çok istek göndermek yerine toplu (batch) işlemleri kullanın.

// SAP UI5 ile OData batch işlemleri
var oModel = this.getView().getModel();
oModel.setUseBatch(true);

// Birden çok işlemi batch olarak gönder
oModel.setDeferredGroups(["changes"]);

// Yeni kayıt oluştur
oModel.create("/MaterialSet", {
  Matnr: "1000001",
  Maktx: "Yeni Malzeme 1",
  Meins: "ST"
}, { groupId: "changes" });

// Mevcut kaydı güncelle
oModel.update("/MaterialSet('1000002')", {
  Maktx: "Güncellenmiş Malzeme 2"
}, { groupId: "changes" });

// Batch işlemini gönder
oModel.submitChanges({
  groupId: "changes",
  success: function(oData) {
    MessageToast.show("İşlemler başarıyla tamamlandı");
  },
  error: function(oError) {
    MessageBox.error("İşlemler sırasında hata oluştu");
  }
});

2. Güvenlik İlkeleri

OData servislerini güvenli bir şekilde tüketmek için önemli güvenlik ilkeleri:

OAuth 2.0 / SAML Kimlik Doğrulama: Daha güvenli kimlik doğrulama yöntemleri kullanın.

// OAuth 2.0 kullanarak OData servisine erişim örneği (JavaScript)
const getOAuthToken = async () => {
  try {
    const response = await fetch('https://sap-auth.example.com/oauth/token', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      body: new URLSearchParams({
        'grant_type': 'client_credentials',
        'client_id': 'YOUR_CLIENT_ID',
        'client_secret': 'YOUR_CLIENT_SECRET'
      })
    });
    
    if (!response.ok) {
      throw new Error('Token alınamadı');
    }
    
    const data = await response.json();
    return data.access_token;
  } catch (error) {
    console.error('OAuth token hatası:', error);
    throw error;
  }
};

const fetchMaterialsWithOAuth = async () => {
  try {
    // OAuth token al
    const token = await getOAuthToken();
    
    // OData isteği yap
    const response = await fetch(
      'https://sap-server.example.com/sap/opu/odata/SAP/ZMATERIAL_SRV/MaterialSet?$format=json',
      {
        headers: {
          'Authorization': `Bearer ${token}`,
          'Accept': 'application/json'
        }
      }
    );
    
    if (!response.ok) {
      throw new Error(`HTTP hata: ${response.status}`);
    }
    
    const data = await response.json();
    return data.d.results;
  } catch (error) {
    console.error('Veri getirme hatası:', error);
    throw error;
  }
};

CSRF Token Koruması: Create, Update ve Delete işlemleri için CSRF token kullanın.

// CSRF token alıp, POST isteği gönderme örneği (JavaScript)
const performWriteOperation = async () => {
  try {
    // CSRF token al
    const tokenResponse = await fetch(
      'https://sap-server.example.com/sap/opu/odata/SAP/ZMATERIAL_SRV',
      {
        method: 'GET',
        headers: {
          'X-CSRF-Token': 'Fetch',
          'Authorization': 'Basic ' + btoa('username:password')
        }
      }
    );
    
    if (!tokenResponse.ok) {
      throw new Error('CSRF token alınamadı');
    }
    
    // CSRF token'ı başlıktan çıkar
    const csrfToken = tokenResponse.headers.get('x-csrf-token');
    
    // POST isteği için veriyi hazırla
    const newMaterial = {
      Matnr: '1000001',
      Maktx: 'Test Malzeme',
      Meins: 'ST'
    };
    
    // POST isteği gönder
    const createResponse = await fetch(
      'https://sap-server.example.com/sap/opu/odata/SAP/ZMATERIAL_SRV/MaterialSet',
      {
        method: 'POST',
        headers: {
          'X-CSRF-Token': csrfToken,
          'Authorization': 'Basic ' + btoa('username:password'),
          'Content-Type': 'application/json',
          'Accept': 'application/json'
        },
        body: JSON.stringify(newMaterial)
      }
    );
    
    if (!createResponse.ok) {
      throw new Error(`HTTP hata: ${createResponse.status}`);
    }
    
    const result = await createResponse.json();
    console.log('Oluşturulan malzeme:', result.d);
    return result.d;
  } catch (error) {
    console.error('İşlem hatası:', error);
    throw error;
  }
};

Girdi Doğrulama: Kullanıcı girdilerini OData servislerine göndermeden önce doğrulayın.

// React uygulamasında form doğrulama örneği
const MaterialForm = () => {
  const [formData, setFormData] = useState({
    matnr: '',
    maktx: '',
    meins: 'ST'
  });
  
  const [errors, setErrors] = useState({});
  const [isSubmitting, setIsSubmitting] = useState(false);
  
  const validateForm = () => {
    let formErrors = {};
    
    // Malzeme numarası kontrolü
    if (!formData.matnr) {
      formErrors.matnr = 'Malzeme numarası zorunludur';
    } else if (!/^\d{7}$/.test(formData.matnr)) {
      formErrors.matnr = 'Malzeme numarası 7 haneli olmalıdır';
    }
    
    // Malzeme tanımı kontrolü
    if (!formData.maktx) {
      formErrors.maktx = 'Malzeme tanımı zorunludur';
    } else if (formData.maktx.length < 3 || formData.maktx.length > 40) {
      formErrors.maktx = 'Malzeme tanımı 3-40 karakter arasında olmalıdır';
    }
    
    // Ölçü birimi kontrolü
    if (!formData.meins) {
      formErrors.meins = 'Ölçü birimi zorunludur';
    }
    
    setErrors(formErrors);
    return Object.keys(formErrors).length === 0;
  };
  
  const handleSubmit = async (e) => {
    e.preventDefault();
    
    if (!validateForm()) {
      return;
    }
    
    setIsSubmitting(true);
    
    try {
      // CSRF token al ve POST isteği gönder
      // ... (önceki örnekteki token alma ve POST isteği kodu)
      
      // Başarılı işlem sonrası
      alert('Malzeme başarıyla oluşturuldu');
      setFormData({ matnr: '', maktx: '', meins: 'ST' });
    } catch (error) {
      alert(`Hata: ${error.message}`);
    } finally {
      setIsSubmitting(false);
    }
  };
  
  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData(prev => ({ ...prev, [name]: value }));
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>Malzeme No:</label>
        <input
          type="text"
          name="matnr"
          value={formData.matnr}
          onChange={handleChange}
        />
        {errors.matnr && <span className="error">{errors.matnr}</span>}
      </div>
      
      <div>
        <label>Malzeme Tanımı:</label>
        <input
          type="text"
          name="maktx"
          value={formData.maktx}
          onChange={handleChange}
        />
        {errors.maktx && <span className="error">{errors.maktx}</span>}
      </div>
      
      <div>
        <label>Ölçü Birimi:</label>
        <select
          name="meins"
          value={formData.meins}
          onChange={handleChange}
        >
          <option value="ST">Adet (ST)</option>
          <option value="KG">Kilogram (KG)</option>
          <option value="LT">Litre (LT)</option>
          <option value="M">Metre (M)</option>
        </select>
        {errors.meins && <span className="error">{errors.meins}</span>}
      </div>
      
      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'İşleniyor...' : 'Kaydet'}
      </button>
    </form>
  );
};

Hata Yönetimi

1. HTTP Durum Kodları

OData servisleri çeşitli HTTP durum kodları döndürebilir. Bunları doğru şekilde işlemek önemlidir:

// Hata işleme örneği
function handleODataError(error) {
  if (error.response) {
    // Sunucudan dönen hata yanıtı
    const status = error.response.status;
    
    switch (status) {
      case 400:
        return 'Geçersiz istek. Parametrelerinizi kontrol edin.';
      case 401:
        return 'Kimlik doğrulama başarısız. Lütfen giriş yapın.';
      case 403:
        return 'Bu işlem için yetkiniz bulunmuyor.';
      case 404:
        return 'İstenen kaynak bulunamadı.';
      case 500:
        return 'Sunucuda bir hata oluştu. Lütfen daha sonra tekrar deneyin.';
      default:
        return `HTTP Hatası: ${status}`;
    }
  } else if (error.request) {
    // İstek gönderildi ancak yanıt alınamadı
    return 'Sunucuya bağlanılamıyor. Ağ bağlantınızı kontrol edin.';
  } else {
    // İstek oluşturulurken bir şey oldu
    return `Bilinmeyen hata: ${error.message}`;
  }
}

2. OData Hata Yanıtları

OData servisleri genellikle yapılandırılmış hata mesajları döndürür. Bunları ayrıştırabilirsiniz:

// OData hata mesajlarını ayrıştırma örneği
function parseODataError(error) {
  try {
    // OData hata yanıtını ayrıştır
    if (error.response && error.response.data) {
      const errorData = error.response.data;
      
      // SAP OData hata format kontrolü
      if (errorData.error && errorData.error.message) {
        return {
          message: errorData.error.message.value || 'Bilinmeyen hata',
          code: errorData.error.code || '',
          details: errorData.error.innererror ? 
                   (errorData.error.innererror.errordetails || []) : []
        };
      }
      
      // Alternatif OData hata format kontrolü
      if (errorData.odata && errorData.odata.error) {
        return {
          message: errorData.odata.error.message || 'Bilinmeyen hata',
          code: errorData.odata.error.code || '',
          details: []
        };
      }
    }
    
    // Genel hata durumu
    return {
      message: error.message || 'Bilinmeyen hata',
      code: '',
      details: []
    };
  } catch (e) {
    console.error('Hata ayrıştırma hatası:', e);
    return {
      message: 'Hata detayları ayrıştırılamadı',
      code: '',
      details: []
    };
  }
}

3. Kullanıcı Dostu Hata Mesajları

Teknik hata mesajlarını kullanıcı dostu mesajlara dönüştürün:

// Kullanıcı dostu hata mesajları örneği
const errorMessages = {
  'ZMATERIAL_NOT_FOUND': 'Malzeme bulunamadı. Lütfen malzeme numarasını kontrol edin.',
  'ZMATERIAL_DUPLICATE': 'Bu malzeme numarası zaten kullanımda. Lütfen farklı bir numara deneyin.',
  'ZMATERIAL_LOCKED': 'Malzeme başka bir kullanıcı tarafından kilitlenmiş. Lütfen daha sonra tekrar deneyin.',
  'AUTHORIZATION_FAILURE': 'Bu işlem için yetkiniz bulunmuyor. Lütfen yöneticinizle iletişime geçin.',
  'INTERNAL_ERROR': 'Sistem hatası oluştu. Lütfen SAP desteğinizle iletişime geçin.',
  'DEFAULT': 'İşlem sırasında bir hata oluştu. Lütfen tekrar deneyin.'
};

function getUserFriendlyErrorMessage(errorCode) {
  return errorMessages[errorCode] || errorMessages['DEFAULT'];
}

// Kullanımı
try {
  await createMaterial();
} catch (error) {
  const parsedError = parseODataError(error);
  const userMessage = getUserFriendlyErrorMessage(parsedError.code);
  showErrorToUser(userMessage);
  
  // Detaylı hata günlüğü (sadece geliştirme veya sistem günlüğü için)
  console.error('Teknik hata detayları:', parsedError);
}

Sonuç

SAP OData servislerini tüketmek, modern uygulamalar ile SAP sistemleri arasında güçlü bir entegrasyon sağlar. Bu rehberde, farklı uygulama türleri için OData tüketim yöntemlerini, güvenlik ve performans ilkelerini ve hata yönetimini inceledik.

Doğru yaklaşımları uygulayarak, SAP OData servislerinizle etkileşime giren güvenli, verimli ve sağlam uygulamalar geliştirebilirsiniz. Günümüzün dijital dönüşüm çağında, bu tür sistem entegrasyonları kuruluşlar için kritik öneme sahiptir.

OData servislerini tüketirken, geliştirdiğiniz uygulamanın gereksinimlerini, güvenlik ihtiyaçlarını ve performans hedeflerini göz önünde bulundurun. İyi tasarlanmış bir istemci uygulaması, kullanıcılarınıza sorunsuz deneyim sağlarken, SAP sisteminizin gücünden tam anlamıyla yararlanmanıza olanak tanır.

Yorumlar

0

Yorum yapmak için giriş yapmalısınız.

Henüz yorum bulunmamaktadır.

İlk yorumu yapan siz olun.

Emre Göçmen

Yazar & Geliştirici

SAP ABAP & Full Stack geliştirici olarak deneyimlerim, becerilerim ve kariyer yolculuğum hakkında blog yazılarım.

Kategori

SAP

SAP

Yazılardan Haberdar Olun

Yeni yazılardan ilk siz haberdar olmak için e-posta bültenime abone olun.