calisacagim.
Rest server ile client arasinda hizli ve basit iletisimi saglayan bir servistir. Servis yonelimli mimarilerde veri transferini
saglar. Http protocollerini kullanir ve minimum veriyi tasidigi icin digerlerine gore daha hizlidir. RestFul servisler ise
reste uygun yazilan servislerdir.
Rest stateless dir. Yani bilgi saklamaz. (All requests are standalone, no knowledge of previous requests)
Rest te Crud islemleri icin 4 http request tipi kullanilir.
Create icin POST, read icin GET, update icin PUT, delete icin DELETE request methodlar kullanilir.
Rest requestleri gondermek ve response almak icin Postman kullanacagiz. Postman, http request gondermemizi saglayan bir http
client tir.
Postman in uygun versiyonunu bilgisayariniza gore asagidaki linkten indirebilirsiniz.
https://www.getpostman.com/downloads/
Rest istegi atabilmek icin tek ihtiyacimiz olan URL dir. Oncelikle URL ile URI farkina bakalim.
Resimde de gordugumuz gibi adresin tamami URL oluyor. Bu url uygulamanin ilk 50 kullanicisini cekmek icindir.
GET te parametreler request e query param olarak eklenebilirken POST ta eklenemez. Request body icinde gider.
Traditional yontemlerde kullanici bilgisini cekmek icin
GET /getUserDetailsServlet?userId=1
seklinde bir istek atariz. User id 1 olan kullanicinin bilgilerini getirir. Geleneksel yontemlerde fiil kullanilir. get... seklinde
Rest ile ise :
GET /users/1
Burada sadece nereden cekecegini isim olarak veririz.
Traditional yontemlerde kullanici update etmek icin
POST /updateUserDetailsServlet{
"firstName" :"gamze",
"lastName" : "sen",
"userId" : "1"
}
seklinde bir istek atariz. User id 1 olan kullanicinin bilgilerini istekteki degerlerle gunceller.
Rest ile ise :
PUT /users/1 {
"firstName" :"gamze",
"lastName" : "sen",
}
seklinde yapariz.
HTTP METHODS
RestFul
POST : create user /users/1
GET : read user details /users/1
PUT : update user details /users/1
DELETE : delete user details /users/1
gordugumuz gibi 4 request te birbirine benziyor. O halde bunlari nasil ayirt ediyorsak dersek Http methoduna gore deriz.
Istekte hangi method oldugu da gonderilir.
Rest i java ile kullanmak icin Spring Tools 4 for Eclipse indirelim. http://spring.io/tools
pom.xml : Dependency leri eklemek icin kullanilan dosya. Ornegin mysql db kullanacaksak pom.xml e mysql dependency eklemeliyiz.
application.properties : db accessi burada configure edebiliriz.
Olusturulan classin uzerine @RestController annotion yazarak gelen Rest requestleri yakalamasini saglariz.
@RequestMapping: Http requestler belli bir url e gonderilir demistik. O url de ilgili islemler yapilir.
@RequestMapping("users") yazmamiz o request in url ni su hale getirir: http://localhost:8080/users
Anlamindan da cikarabiliriz. Gelen request i maplemis oluyoruz.
Get icin Ornek bir controller su sekilde olabilir.
@RestController
@RequestMapping("kullanicilar") //http://localhost:8080/kullanicilar
public class UserController {
@GetMapping
public String getUser() {
return "get user cagrildi";
}
}
Postman den resimdeki gibi GET requesti gonderdigimizde response umuzu aliriz.
Simdi gercekteki GET requestini dusunelim. Bir userId verilir ve o user a ait bilgiler cekilir.Yani request su sekilde
olmali.
http://localhost:8080/kullanicilar/1
Buradaki 1 userId dir ve Path e eklenmistir. path="/{userId}" demeliyiz. UserId parametre olarak gonderilmistir.O yuzden
PathVariable diyerek alabiliriz.
@GetMapping(path="/{userId}")
public String getUser(@PathVariable String userId) {
return "get user cagrildi userID: "+ userId;
}
Simdi ise suna bakalim : Path e id ekledigimizde o id li kaydi donuyor, eger bir sey eklemezsek tum kayitlari doner. Ama
eger cok fazla kayi varsa sorun olabilir. O yuzden belli bir limit koyalim. Page 1 icin 50 kayit donsun. Bu durumda
requestimiz su sekilde olmali,
http://localhost:8080/kullanicilar?page=1&limit=50
Parametreler ? den sonra eklenir ve aralarina & konur.
Bu durumda Get methodumuz su sekilde olmali.
@GetMapping
public String getUser(@RequestParam(value="page") int page,
@RequestParam(value="limit") int limit) {
return "get user cagrildi page: "+ page+ " limit: "+ limit;
}
Path de bir deger gelmedigi icin path i kaldirdik. RequestParam olarak gelen parametreleri yakaladik.
Peki bu degerlerden biri gonderilmezse nasil bir response donecegiz? Kullandigimiz degerler bos geldigi icin islemlerimizi
yapamayacagiz ve 400 Bad Request donecegiz. Bu gibi durumlara dusmemek icin her bir parametreye default bir deger atayalim ve eger
degerler bos gelirse o default degeri kullansin.
@GetMapping
public String getUser(@RequestParam(value="page", defaultValue="8") int page,
@RequestParam(value="limit", defaultValue="6") int limit) {
return "get user cagrildi page: "+ page+ " limit: "+ limit;
}
Diger bir yontem ise parametreyi optional yapmaktir(required=false) eklemek. Ancak bu yontem primitive data type lar icin sorun yaratir, operationda
o degeri kullanmaya calisirsak null pointer alabilir ve 500 Internal Server Error verebiliriz.
Get ile yapilan islem normalde sudur. Cekilmesi gereken kullanicinin userId si input olarak gecilir ve o id li user in
bilgisi cekilir. O zaman bu metodun return tipi User tipinde bir obje olmalidir. Yani User modeline ihtiyacimiz var.
@GetMapping(path="/{userId}")
public User getUser(@PathVariable String userId){
User u = new User();
u.setFirstName("gamze");
u.setEmail("gamzesen@gmail.com");
u.setLastName("sen");
u.setUserId(userId);
return u;
}
Bize donen response u json olarak yakaladik. XML e donusturmeye calisip hata alabiliriz. Cunku datanin tipi uygun olmayabilir.
Response un hangi tiplerde donecegini methoda produces parametresi ekleyerek ayarlayabiliriz. Ornegin hem json hem xml
tipinde response uretmesi icin su sekilde olmali:
@GetMapping(path="/{userId}", produces = {MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE})
public User getUser(@PathVariable String userId){
User u = new User();
u.setFirstName("gamze");
u.setEmail("gamzesen@gmail.com");
u.setLastName("sen");
u.setUserId(userId);
return u;
}
Sadece belli tipteki datalari request olarak kabul etmesini saglabiliriz. Onu da consumes yazarak saglayabiliriz.
@GetMapping(path="/{userId}", produces = {MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE}, consumes = {MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE})
Ancak response umuz hala XML tipine uygun olmayabilir. Bunun icin projemiz xml desteklemesi icin pom.xml e xml dependency
eklemeliyiz. Bunun icin https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-xml/2.9.8
bu linkten jackson data formatini aciyoruz. En son surumu secip Maven tabindan ilgili kodu pom.xml e ekliyoruz. Artik
response umuz XML tipinde donebilir.
Put ile update islemleri yapiyoruz. Hangi kullaniciyi update edecegimiz bilgisini parametre olarak user id ile gonderiyoruz.
Ayrica update edilecek bilgileri de requestin body sinde gondermeliyiz. Bu yuzden RequestBody tipinde bir parametre daha
ekleyecegiz. Girilen bilgilerin validation ini yapacagiz. O yuzden basina @Valid annotation ekleyelim.
RequestBody olarak kullanacagimiz model icinse yeni bir model olusturalim.
Simdi ise Post isteginebakalim. Post requesti create icin kullanilir. Request te yeni bir kullanici olusturacagiz. O kullanicinin
bilgilerini request body de gondeririz. Oncelikle bu request body de gelen kullanici bilgilerini modele setleriz. Daha sonra da
olusturdugumuz kullaniciyi donelim. O yuzden methodumuzun return tipi ResponseEntity<Model> seklinde olmali.
Map<String, User> users;
@PostMapping(produces = {MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE}, consumes = {MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE})
public ResponseEntity<User> createUser(@RequestBody UserRequest userReq){
User u = new User();
u.setFirstName(userReq.getFirstName());
u.setLastName(userReq.getLastName());
u.setEmail(userReq.getEmail());
u.setUserId(UUID.randomUUID().toString());
if(users == null) {
users = new HashMap<>();
users.put(u.getUserId(), u);
}
return new ResponseEntity<User>(u, HttpStatus.OK);
Simdi de DELETE requestine bakalim. Silme islemi icin silinecek kullanicinin id sini gonderecegiz. Herhangi bir response body
gondermedigimiz veya sonuc beklemedigimiz icin Accept ve Content-Type header kullanmamiza gerek yok. Methodumuz su sekilde
olacak.
@DeleteMapping(path="/{userId}")
public ResponseEntity<Void> deleteUser(@PathVariable String userId){
users.remove(userId);
return ResponseEntity.noContent().build();
}
Bu sekilde users listesinden ilgili user silinecek ve no content response muz donecek.
Bunun icin once post requestiyle bir user olusturalim. Sonra get ile o user in id sini alalim. Ve delete requestde o user i
silelim. Islemimiz bu kadar basit.
Simdi ise Exception Handling isine bakalim. Eger uygulamamizda herhangi bir exception cikarsa zaten bunu otomatik handle eder.
500 internal server hatasini dondurur, ama biz bu isi custom olarak yapalim.
Ornegin getUser methodumuzda String firtname= null; yapip int length = firstName.length();
dersek null pointer exception aliriz. Bu durumu AppExeptionHandler adinda bir class yazarak biz handle edelim.
Eger yazdigimiz classin ustune @ControllerAdvice annotation eklersek uygulamanin herhangi bir yerinde olusan hatayi yakalamamizi
saglar.
methodumuzun uzerine ekleyecegimiz @ExceptionHandler annotation ise icine ne tarz exceptionlari yakalamak istiyorsak onu yazariz.
Tum exceptionlari yakalamak icin sadece Exception yazacagiz.
@ControllerAdvice
public class AppExeptionHandler extends ResponseEntityExceptionHandler{
@ExceptionHandler(value ={Exception.class})
public ResponseEntity<Object> handleAnyException(Exception ex, WebRequest req){ //Object yazdik cunku hata responseunu biz olusturacagiz.
return ResponseEntity<>(ex , new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
get request calistirip null pointer exception alacagiz ve bu methodumuz calisacak.
Dependency Injection : Uygulama gelistirirken bir class icinde diger classdaki ozelliklere erismemiz gerekebilir. Bu gibi durumlarda
nesne olustururuz. Classlar arasi bagimliliklari en aza indirmek ve bu bagimliliklari yonetmek amaciyla kullanilir. Belli bir
islevi yapan bir uygulama yazildiginda daha sonra baska bir sekilde degistirilmesi istenirse her yerde degisiklik yapmak yerine
en az degisiklikle yeni hale getirmek icin Dependency Injection kullanilir. new ile class a ait nesne olusturmak (direct instance)
daha zorludur. Bunun yerine bir interface yazilir. Daha sonra bir implementation yazilir. Direkt olarak classi degistirmektense
interface leri degistirmek daha az maliyetlidir. Ornegin bir yapay zekamiz olsun. Bulunulan dosya icinde arama yapma ozelligi
olsun.
public class YapayZeka{
public String aramaYap(){
return "Dosya icinde arama yapildi";
}
}
public class Uygulama{
public static void main(String[] args){
YapayZeka yapayZeka = new YapayZeka();
yapayZeka.aramaYap();
}
}
Uygulamamiz bu sekilde sorunsuz calisir ancak ilerde aramanin sadece dosya icinde degilde tum dosyalarda aramasi gerektigi
soylenirse bu YapayZeka classini degistirmek anlamina gelir. Ancak dependency injection kullanirsak bu degisikligi en aza
indiririz.
public interface YapayZeka{
public String aramaYap();
}
2 tane implementation yazalim.
public class DosyaAramasi implements YapayZeka{
@Override
public String aramaYap(){
return "Dosya icinde arama yapildi";
}
}
public class GenisArama implements YapayZeka{
@Override
public String aramaYap(){
return "Tum dosyalar icinde arama yapildi";
}
}
Uygulama ise su sekilde olmali.
public class Uygulama{
public static void main(String[] args){
DosyaAramasi dosyaAra = new DosyaAramasi();
dosyaAra.aramaYap();
GenisArama genisAra = new GenisArama();
genisAra.aramaYap();
}
}
Dependency Injection ile java class larimiz birbirinden bagimsiz olmali. Bu sayede hem bu classlari baska uygulamalarda
kullanabiliriz hem de test etmemiz daha kolaylasir.
Bu Dependency Injection meselesini bizim uygulamamiz acisindan inceleyelim. createUser methodunun business logic kismini baska bir
class icinde yazarsak ilerde bu islem database kullanmamiz geretiginde veya baska islemler yapmamiz gerektiginde bu isler
daha kolaylasir.
UserService adinda bir interface olsuturalim.
public interface UserService {
UserRest createUser(UserDetailsRequestModel userDetails);
}
Simdi de implementation olsuturalim. Ve createUser icindeki buradaki implementation a kopyalalim.
public class UserServiceImpl implements UserService {
Map<String, UserRest> users;
@Override
public UserRest createUser(UserDetailsRequestModel userDetails) {
UserRest user = new UserRest();
user.setEmail(userDetails.geteMail());
user.setFirstName(userDetails.getFirstName());
user.setLastName(userDetails.getLastName());
String userId = UUID.randomUUID().toString();
user.setUserId(userId);
if(users == null) {
users = new HashMap<>();
users.put(userId, user);
}
return user;
}
}
Simdide bunu createUser methodunda su sekilde cagirabiliriz.
UserRest user = new UserServiceImpl().createUser();
Bu sekilde kullanmak direct dependency olur. Ve bu sekilde cok verimli olmaz. Dependency fazla olur. Bunun yerine UserService i
inject edecegiz. En ustte
@Autowired
UserService userService;
Autowired annotation ile Spring framework bizim adimiza o classin otomatik olarak instance ini olusturur.
Artik createUser icinde
UserRest user = userService.createUser(); seklinde kullanabiliriz. Ama daha bitmedi. Framework un UserServiceImpl i gormesi
ve autowired etmesi icin UserServiceImpl i Service olarak tanimlamaliyiz. Bunun icin en ustune @Service annotation eklemeliyiz.
Framework tum projeyi scan eder ve Service annotation gorurse ancak userService den instance olusturur.
Post request atip yazdiklarimizi deneyelim.
Simdi de constructor based Dependency Injection a bakalim:
UserServiceImpl in userId uretmek icin Utils adinda bir classa dependent oldugunu dusunelim. Bu Util i de inject etmek icin
constructor kullanalim. Bunun icin UserServiceImpl icin 2 constructor olusturalim. Ve Utils olan constructor i Autowired ile
annotate edelim ki Utils icin otomatik olarak instance olsutursun.
Utils utils;
public ServiceImpl(){}
@Autowired
public UserServiceImpl(Utils utils){
this.utils = utils;
}
Bunun icin Utils classimizi olusturalim. Ve class imizi @Service annotation ile isaretleyelim.
@Service
public class Utils{
public String generateUserId(){
return UUID.randomUUID().toString();
}
}
ServiceImpl icinde createUser da userId kismini Utils den alalim.
String userId = utils.generateUserId(); seklinde.
Yaptiklarimizi post req atarak deneyelim.
Hiç yorum yok:
Yorum Gönder