SAP OData Servislerini Tüketme ve Entegre Etme
Emre Göçmen
Yazar

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
Henüz yorum bulunmamaktadır.
İlk yorumu yapan siz olun.



