İçerikler :

@Consumes Annotation @DELETE Annotation @GET Annotation @HEAD Annotation @Path Annotation @POST Annotation @Produces Annotation @PUT Annotation com.sun.jersey.api.container.ContainerExcepti.. DropWizard Gömülü Jetty ve Jersey İle Rest Servisi HEAD ve GET ile Veri Güncellemeyiniz Java'da Jersey API'si ile RESTful Servisi (Ka.. JAX-RS: Java API for RESTful Web Services API.. Jersey Jersey'de Client'da Date Tipinin Kullanımı Jersey'de Servis Adlarını ve Parametreleri Ek.. Jersey API Jersey ile BASIC HTTP Athentication Örneği Jersey İle Bir JSON Döndüren RESTful Web Serv.. Jersey İle Rest Servisi Çağrılması JSR 311, Java Specification Requests - JAX-RS.. Maven Shade Plugin ve MessageBodyReader Not F.. org.jboss.resteasy.spi.NotAcceptableException.. Request Method Designator - İstek Metodu İşar.. Resource Class - Kaynak Sınıfı Resource - Kaynak Resource Method - Kaynak Metodu Rest Api Excepition Yakalayan Bir Örnek RESTEasy RESTful ile Hello World Sınıf Örneği Restfull Servislerinde UTF-8 RESTFul Servislerinde Alt Kaynak Bulucusu (Su.. RESTFul Servislerinde Kaynak , Metod, Alt Kay.. RESTFul Servislerinde Kaynak ve Alt Kaynak Me.. RESTful Web Servisleri Rest Mimarisindeki HTTP Metodları REST - Representational State Transfer Root Resource Class - Kök Kaynak Sınıfı Root Resource - Kök Kaynak Subresource - Alt Kaynak Subresource Locator - Alt Kaynak Bulucusu Subresource Metod - Alt Kaynak Metodu

Bu Sayfayı Paylaş:

Kavram

REST - Representational State Transfer

Tanım: Web ve HTTP protokolü (GET,PUT,POST, DELETE gibi HTTP metodları desteklemektedir) standartlarını kullarak web üzerinden hizmet verebilen (web servis) uygulamalar geliştirmek için kullanılan teknoloji veya mimari

Kavram

RESTful Web Servisleri

Tanım: REST teknolojilerine (GET,PUT,POST, DELETE gibi HTTP metodları kullanılmaktadır) dayanarak geliştirilen web servisleri (web üzerinden hizmet veren uygulama veya fonksiyonlar)

Veri

JAX-RS: Java API for RESTful Web Services API'sinin Geliştirilmesinin Temel Amaçları

JAX-RS: Java API for RESTful Web Services API'sinin geliştirilmesini temel amaçları olarak aşağıdakiler söylenmektedir : 

  • POJO (Plain Old Java Object) class'larını , annotation'lar kullanarak kolay bir şekilde web kaynağı haline gitirilmesi
  • HTTP ve URI merkezli olmak
  • Text, json, xml gibi içerik tiplerinden bağımsız olabilme. Farklı içerik tipleri ile çalışabilme
  • Çalıştırma ortamlarından (container) bağımsızlık
  • JEE standartlarının bir parçası olma

 


Veri

Rest Mimarisindeki HTTP Metodları

Rest mimarisindeki HTTP metodları aşağıdaki gibidir : 

  • GET : Bir kaynağı (resource) elde etmek için kullanılır
  • POSTVarolan bir kaynağı (resource) değiştirmek için kullanılır
  • PUT Yeni bir kaynak (resource) oluşturmak için kullanılır
  • DELETE: Varolan bir kaynağı (resource) silmek için kullanılır

 


Örnek

RESTful ile Hello World Sınıf Örneği

RESTful ile basit bir Hello Wordl class örneği görülmektedir : 

 

@Path("/helloworld")

public class HelloWorldResource {

@GET 

@Produces("text/plain")

public String getClichedMessage() {

return "Hello World";

}

}

 

Yukarıdaki class bir root resource (kök kaynak)'tır. İçinde veri çekmek amacıyla bir ClickedMessage alt kaynağı yaratılmıştır. ../helloword adresi GET method'u ile çağrıldığında cevap olarak Hello World değeri alınacaktır


Kavram

Resource Class - Kaynak Sınıfı

Tanım: RESTful web servis teknolojilerinde , annotation'lar ile web kaynağı (web servisi) haline getirilen java sınıfı. Sınıf içerisinde başka kaynaklar (alt kaynak-sub resource) bulunabilir

Kavram

Root Resource Class - Kök Kaynak Sınıfı

Tanım: RESTful web servis teknolojilerinde, üzerinde @PATH annotation'ı koyularak tanımlanan kaynak sınıf. Sınıf içerisinde HTTP metod annotation'ları (@GET,@POST,@PUT,@DELETE ..) ve/veya başka kaynaklar (alt kaynak-sub resource) bulunabilir

Kavram

Resource - Kaynak

Tanım: RESTful web servis teknolojilerinde , her bir web servis için kullanılan ifade (web servis : web üzerinden erişilebilen uygulama veya fonskiyon)

Kavram

Root Resource - Kök Kaynak

Tanım: RESTful web servis teknolojilerinde, bir sınıf üzerinde @PATH annotation'ı koyularak tanımlanan kaynak. Sınıf içerisinde HTTP metod annotation'ları (@GET,@POST,@PUT,@DELETE ..) ve/veya başka kaynaklar (alt kaynak-sub resource) bulunabilir

Kavram

Resource Method - Kaynak Metodu

Tanım: Kaynak sınıfı (resource class) içinde , HTTP metod annotation'ları (@GET,@POST,@PUT,@DELETE ..) ile tanımlanan kaynaklar . Bu kaynaklar üzerinde @PATH kullanılır ise alt kaynak metodu (subresource method) haline gelir ve farklı bir path ile erişilir

Kavram

Subresource - Alt Kaynak

Tanım: Kaynak sınıfı (resource class) içinde @PATH kullanılarak tanımlanan kaynak. Alt kaynağın path'i kök kaynağın (sınıfın) path'ine eklenerek , kök_kaynak_path/alt_kaynak_path şeklinde olmaktadır. Eğer @GET,@POST,@PUT,@DELETE gibi metodlar kullanılır ise Alt Kaynak Metodu (Subresource Metod ) , kullanılmayıp sadece başka bir kaynak döndürüyor ise Alt Kaynak Bulucusu (Subresource Locator) adı verilmektedir

Kavram

Subresource Metod - Alt Kaynak Metodu

Tanım: Kaynak sınıfı (resource class) içinde @PATH ile tanımlanan ve HTTP metod annotation'ları (@GET,@POST,@PUT,@DELETE ..) bulunanan alt kaynak. Eğer HTTP metod annotation'ları (@GET,@POST,@PUT,@DELETE ..) kullanılmaz ise Alt Kaynak Bulucusu (Subresource Locator) olarak kullanıldığı anlamına gelmektedir

Kavram

Subresource Locator - Alt Kaynak Bulucusu

Tanım: Kaynak sınıfı (resource class) içinde @PATH ile tanımlanan ve başka bir kaynağı (resource) döndüren alt kaynak (sub resource). HTTP metod annotation'larının (@GET,@POST,@PUT,@DELETE ..) kullanılmamaktadır (bu nedenle GET,PUT gibi bir işlemler desteklenmez)

Kavram

Request Method Designator - İstek Metodu İşaretleyici

Tanım: RESTful teknolojinde , genel HTTP metodları (GET,PUT,POST,HEAD gibi) ile Java metodlarını eşleştirme amacıyla kullanılan annotation'lar. @GET, @POST, @PUT,@DELETE, @HEAD ön tanımlı olarak bulunan İstek Metodu İşaretleyici'dir (Request Method Designator). @HttpMethod kullanılarak yeni bir istek metodu işaretleyici yaratılabilir

Kavram

@Path Annotation

Tanım: RESTFul teknolojisinde, bir class'ı root resource (kök kaynak) veya bir java metodunu subresource (alt kaynak) haline getirmek ve erişim path'ini vermek için kullanılan annotation. @Path parametre olarak dışarıdan erişilmesi için gerekli olan path (yol,adres) bilgisini alır . Örneğin @Path("/helloworld") şeklinde tanımlandığında web üzerinden http:///helloworld şeklinde servise erişilmektedir

Kavram

@GET Annotation

Tanım: RESTFul teknolojisinde, java metodları ile HTTP GET metodunu eşleştirmeyi sağlayan annotation. Sunucudan bir kaynağı almak için kullanılır

Kavram

@POST Annotation

Tanım: RESTFul teknolojisinde, java metodları ile HTTP POST metodunu eşleştirmeyi sağlayan annotation. Sunucuda bir kaynağı oluşturmak (yaratmak) için kullanılır

Kavram

@PUT Annotation

Tanım: RESTFul teknolojisinde, java metodları ile HTTP PUT metodunu eşleştirmeyi sağlayan annotation. Sunucuda bir kaynağı değiştirmek (güncellemek) için kullanılır

Kavram

@DELETE Annotation

Tanım: RESTFul teknolojisinde, java metodları ile HTTP DELETE metodunu eşleştirmeyi sağlayan annotation. Bir kaynağı silmek için kullanılır

Kavram

@HEAD Annotation

Tanım: RESTFul teknolojisinde, java metodları ile HTTP HEAD metodunu eşleştirmeyi sağlayan annotation. Bir kaynak ile ilgili meta bilgiyi (genel bilgiler) almak için kullanılır

Kavram

@Produces Annotation

Tanım: RESTFul teknolojisinde, bir kaynağın üreteceği (veya döndürdüğü) verinin tipini(media type) belirtmek için kullanılan annotation . text/plain, text/xml, text/html, application/json, application/widgets+xml vb.. tipler desteklenmektedir

Kavram

@Consumes Annotation

Tanım: RESTFul teknolojisinde, bir kaynağın kabul edeceği (veya alacağı) verinin tipini(media type) belirtmek için kullanılan annotation. text/plain, text/xml, text/html, application/json, application/widgets+xml vb.. tipler desteklenmektedir

Veri

RESTFul Servislerinde Kaynak , Metod, Alt Kaynak Gibi Ana Kavramlar

RESTFul'de bir web servis, kaynak sınıfı (resource class) ve istekleri işleyen istek metodlarından (request metod) oluşur. En basit kaynak sınıfı (resource class) ve istek metodu (request metod) için aşağıdaki gibi bir örnek verilebilir :

@Path("/helloworld")

public class HelloWorldResource {

 @GET 

 @Produces("text/plain")

 public String getClichedMessage() {

return "Hello World";

 }

}

Burada HelloWorldResource class'ı bir kaynak sınıfıdır ve getClichedMessage ise bir @GET metodunu destekleyen bir kaynak metodudur. Kaynak sınıfı olması için class ifaesinin üzerine @Path("/helloworld") annotation'su eklenir. @GET ile de (@GET,@POST,@PUT,@DELETE gibi metodlara "Request Method Designator" adı verilmektedir) bir request metodu oluşturulur.

Bir kaynak sınıfı içinde @Path ve @GET,@POST,@PUT,@DELETE gibi (Request Method Designator) tanımlaman bir metod Alt Kaynak Metodu oluşturur. Alt kaynak <üst kaynak pathi>/<alt kaynak path'i> şeklinde erişilir. Aşağıda , yukarıdaki örneği bir alt kaynak metodu eklenmiştir : 

@Path("/helloworld")

public class HelloWorldResource {

@GET
@Produces("text/plain")
public String getClichedMessage() {

return "Hello World";

}  

@GET
@Produces("text/plain")
@Path("/tr")
public String getClichedMessageTr() {

return "Merhaba";

}

}

Burada getClichedMessageTr adındaki fonksiyon bir alt kaynak metodudur ve bu kaynağa <root>/helloworld/tr ile erişilebilinir. Alt kaynak eğer @GET,@POST,@PUT,@DELETE gibi bir annatation yok ise bunun adına Subresource Locator - Alt Kaynak Bulucusu adı verilir. "Alt Kaynak Bulucusu" başka bir kaynak olan bir nesne döndüren bir alt kaynaktır. Aşağıda Subresource Locator - Alt Kaynak Bulucusu bulunanan bir örnek görülmektedir : 

import javax.ws.rs.*;

@Path("/helloworld")

public class HelloWorldResource {

private String languageCode="en";

public HelloWorldResource() {}

public HelloWorldResource(String languageCode) {

this.languageCode=languageCode;

}

@GET
@Produces("text/plain")
public String getClichedMessage() {

if(languageCode.equals("en")){

return "Hello World";

}else if(languageCode.equals("tr")){

return "Merhaba Dunya";

} else{

return "Hello World";

}

}

 @GET
@Produces("text/plain")
@Path("/tr")
public String getClichedMessageTr() {

return "Merhaba";

}

@Path("/lng/{language}")
public HelloWorldResource findHelloWorldResource(
@PathParam("language") String languageCode){

return new HelloWorldResource(languageCode);

}

}

Görüldüğü gibi findHelloWorldResource metodu eklenmiştir. findHelloWorldResource bir Alt Kaynak Bulucusudur (Subresource Locator). @Path annotation bölümünde {language} parametresi dinamik yapılmıştır. <root>/helloworld/lng/tr şeklinde çağrıldığında "Merhaba Dunya" , <root>/helloworld/lng/en şeklinde çağrıldığında "Hello World" mesajı dönülecektir. findHelloWorldResource metodu HelloWorldResource kaynağını döndürmektedir. @GET işlevini HelloWorldResource yerine getirmektedir

 


Örnek

RESTFul Servislerinde Kaynak ve Alt Kaynak Metod (Subresource Metod) Örneği

Aşağıda bir kaynak ve alt kaynak olan bir örnek gösterilmektedir : 

@Path("/helloworld")

public class HelloWorldResource {

@GET
@Produces("text/plain")
public String getClichedMessage() {

return "Hello World";

}

 

@GET
@Produces("text/plain")
@Path("/tr")
 public String getClichedMessageTr() {

return "Merhaba Dunya";

}

}

Yukarıdaki örnekte HelloWorldResource bir kaynak (root resource) class'ıdır ve <root>/helloworld şeklinde GET ile erişildiğinde getClichedMessage çalışacaktır. getClichedMessageTr ise bir alt kaynağı gösterir ve bu alt kaynağa GET ile <root>/helloworld/tr şeklinde erişilebilir


Örnek

RESTFul Servislerinde Alt Kaynak Bulucusu (Subresource Locator) Örneği

Aşağıda Alt Kaynak Bulucusu (Subresource Locator) kullanıldığı bir örnek gösterilmektedir :

package com.test;

import javax.ws.rs.*;

@Path("/helloworld")

public class HelloWorldResource {

private String languageCode="en";

public HelloWorldResource() {}

public HelloWorldResource(String languageCode) {

this.languageCode=languageCode;

}

@GET
@Produces("text/plain")
 public String getClichedMessage() {

if(languageCode.equals("en")){

return "Hello World";

}else if(languageCode.equals("tr")){

return "Merhaba Dunya";

} else{

return "Hello World";

}

}  

@Path("/lng/{language}")
public HelloWorldResource findHelloWorldResource(
@PathParam("language") String languageCode){

return new HelloWorldResource(languageCode);

}

}

HelloWorldResource bir kaynaktır ve verilen dile göre hello wordl ifadesini metin olarak göndermektedir. HelloWorldResource kaynağına <root>/helloworld GET ile erişildiğinde getClichedMessage metodu çağrılır ve varsayılan olarak ingilizce mesaj dönder. Eğer <root>/helloworld/lng/tr çağrılırsa Türkçe "Merhaba Dunya",  <root>/helloworld/lng/en şeklinde çağrılırsa da "Hello World" dönülecektir. findHelloWorldResource metodu bir Alt Kaynak Bulucusu'dur (Subresource Locator) ve geriye HelloWorldResource kaynağını dönmektedir. @Path annotation'da görüldüğü gibi {language} şeklinde dinamik bir değişken görülmektedir ve bu metoda parametre olarak aktarılmaktadır


Örnek

Jersey İle Bir JSON Döndüren RESTful Web Servisini GET İle Çağırma

Aşağıdaki örnek JSON döndüren bir RESTful web servisini çağırmakta ve dönene JSON string'ini ekrana basmaktadır : 

 

Client c = Client.create();
WebResource r = c.resource("http://www.xxx.com/rs/madde_adi/GET");
String response=r.accept(MediaType.APPLICATION_JSON).get(String.class);
System.out.println("response:"+response);

Not : Jersey Client API'sinin 1.14 versiyonu kullanılmıştır

 


Kavram

Jersey

Tanım: Java'da JAX-RS (Java API for RESTful Web Services) API'sinin referans implemantasyonu. Java'da RESTful web servisleri yaratmak veya kullanmak için kullanılan bir API'dir

Öneri

HEAD ve GET ile Veri Güncellemeyiniz

RESTfull servislerinizde GET ve HEAD ile kesinlikle herhangi bir veri ekleme veya güncelleme yapmayınız. Bu metodlar ile sadece veri alınabilmelidir

 


Kaynak

JSR 311, Java Specification Requests - JAX-RS: The JavaTM API for RESTful Web Services

JAX-RS: The JavaTM API for RESTful Web Services ile ilgili JSR (Java Specification Requests) standartı belirten belge

Kaynak

Jersey API

Java'da JAX-RS (Java API for RESTful Web Services) API'sinin referans implemantasyonu olan Jersey projesinin resmi web sitesi

Kavram

RESTEasy

Tanım: JAX-RS (Java API for RESTful Web Services) spesifikasyonuna uyumlu olarak geliştirilmiş bir Jboss Restfull projesi

İpucu

com.sun.jersey.api.container.ContainerException Hatası

Jersey API'si kullanımı sırasında aşağıdaki gibi bir hata alabilirsiniz :
com.sun.jersey.api.container.ContainerException: The ResourceConfig instance does not contain any root resource classes
Bu hata web.xml'de Jersey için ServletContainer tanımında com.sun.jersey.config.property.packages özelliği tanımlanmamış veya içerisinde root resourse (kök kaynağı) bulunmayan bir paket verilmiş anlamına gelir. Aşağıda bir com.test paketi için tanımlama görülmekte :
<servlet>
<servlet-name>Jersey REST Service</servlet-name></strong></p>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.test</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet></strong>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>

İpucu

Java'da Jersey API'si ile RESTful Servisi (Kaynağı) Yaratma ve Çağırma

Java'da RESTful servisi geliştirmek için Jersey API kullanılmaktadır. Öncelikle bir web projesi yapılmalı ve aşağıdaki jar'lar eklenmelidir :
  • asm.jar
  • jersey-core.jar
  • jersey-server.jar
  • jersey-servlet.jar
Eğer maven kullanıyor ise artifactId'si jersey-servlet için bağımlılıkların pom.xml'e eklenmesi yeterlidir : 
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-servlet</artifactId>
<version>1.14</version>
</dependency>
RESTful isteklerini işlenebilmesi için ServletContainer eklenmesi gerekir. Bunun için web.xml'e aşağıdaki Servlet eklenmelidir :
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.test</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
com.sun.jersey.spi.container.servlet.ServletContainer sınıfı RESTful servislerine gelen istekleri işlemek için gereklidir. com.sun.jersey.config.property.packages özelliği ise web servislerin (daha doğrusu root resource'ların) bulunacağı paket verilmektedir. url-pattern ile /rest/* şeklinde URL'ler rest servisi olarak kabul edilecek ve uygun servis (root resource) aranacaktır
Aşağıdaki gibi bir root resource (kök kaynağı) class'ı yaratabiliriz : 
package com.test;
import javax.ws.rs.*;
@Path("/helloworld")
public class HelloWorldResource {
 @GET 
 @Produces("text/plain")
 public String getClichedMessage() {
   return "Hello World";
 
 }
}
Yukarıda helloworld path'i ile erişilebilecek bir RESTful servisi yaratılmıştır. Servis GET metodu ile çağrıldığında getClichedMessage fonkisyonu çağrılacaktır ve cevap text olarak geri dönecektir. web.xml'de /rest ile path'i verildiği için helloworld servisinin çağrılması için http://://rest/helloworld ile (Eğer 8080 portu ile localhost'ta deniyorsanız adres http://localhost:8080//rest/helloworldşeklinde olacaktır) adresi çağrımlası gerekecektir.
com.test paketi içinde olması gerekmektedir. Aşağıda ana paket belirtilmiştir :
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.test</param-value>
</init-param>

İpucu

org.jboss.resteasy.spi.NotAcceptableException: No match for accept header Hatası

Resteasy ile (veya başka bir Restfull API'si ile) yaratılan bir servis çağırldığı zaman aşağıdaki hata sunucusunuz loglarında görülebilir :


org.jboss.resteasy.spi.NotAcceptableException: No match for accept header
at org.jboss.resteasy.core.registry.Segment.match(Segment.java:119) [resteasy-jaxrs-2.3.2.Final.jar:]
at org.jboss.resteasy.core.registry.PathParamSegment.matchPattern(PathParamSegment.java:192) [resteasy-jaxrs-2.3.2.Final.jar:]
at org.jboss.resteasy.core.registry.RootSegment.matchChildren(RootSegment.java:339) [resteasy-jaxrs-2.3.2.Final.jar:]
at org.jboss.resteasy.core.registry.SimpleSegment.matchSimple(SimpleSegment.java:44) [resteasy-jaxrs-2.3.2.Final.jar:]
at org.jboss.resteasy.core.registry.RootSegment.matchChildren(RootSegment.java:327) [resteasy-jaxrs-2.3.2.Final.jar:]
at org.jboss.resteasy.core.registry.RootSegment.matchRoot(RootSegment.java:374) [resteasy-jaxrs-2.3.2.Final.jar:]
at org.jboss.resteasy.core.registry.RootSegment.matchRoot(RootSegment.java:367) [resteasy-jaxrs-2.3.2.Final.jar:]
at org.jboss.resteasy.core.ResourceMethodRegistry.getResourceInvoker(ResourceMethodRegistry.java:307) [resteasy-jaxrs-2.3.2.Final.jar:]
at org.jboss.resteasy.core.SynchronousDispatcher.getInvoker(SynchronousDispatcher.java:173) [resteasy-jaxrs-2.3.2.Final.jar:]
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:118) [resteasy-jaxrs-2.3.2.Final.jar:]
at org.jboss.seam.resteasy.ResteasyResourceAdapter$1.process(ResteasyResourceAdapter.java:145) [jboss-seam-resteasy.jar:2.3.1.CR1]
at org.jboss.seam.servlet.ContextualHttpServletRequest.run(ContextualHttpServletRequest.java:65) [jboss-seam.jar:2.3.1.CR1]
at org.jboss.seam.resteasy.ResteasyResourceAdapter.getResource(ResteasyResourceAdapter.java:120) [jboss-seam-resteasy.jar:2.3.1.CR1]
at org.jboss.seam.servlet.SeamResourceServlet.service(SeamResourceServlet.java:80) [jboss-seam.jar:2.3.1.CR1]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:847) [jboss-servlet-api_3.0_spec-1.0.0.Final.jar:1.0.0.Final]

Bu hata client'ın Http Header'a eklediği Accept ile sunucunun tanımladığı (öneğin json için @Produces("application/json") şeklinde tanımlanır) Accept header'ın birbirini tutmadığı anlamına gelir Sunucu application/json olarak tanımladıysa client'da header'a (setHeader("Accept", "application/json") gibi) Accept değerini application/json olarak eklemelidir

İpucu

Restfull Servislerinde UTF-8

Restfull servislerinde karakter sorunu yaşamamak için UTF-8 output üretmesi sağlanabilir. Bunun için @Producer tanımında charset UTF-8 olarak belirtilebilir :


@Produces("application/json;charset=UTF-8")
public String test(){..}


Yukarıda görüldüğü gibi charset değeri UTF-8 yapılmıştır. Bu servisi çağıran client'da Accept header'ının değerini application/json;charset=UTF-8 şeklinde çağırmalıdır


Kavram

DropWizard

Tanım: Java'da Jetty sunucusu üzerinde çalışan, RESTful web servisleri gelitime işini kolaylaştıran araçlar sağlayan bir java framework

İpucu

Jersey'de Client'da Date Tipinin Kullanımı

Jersey'de size gelen bir nesnenin tarih formatı dd.MM.yyyy HH:mm şeklinde veya buna benzer olabilir. Bu şekilde gelen tarih değerini Date tipine otomatik olarak atanmasını isteyebilirsiniz. Örneğin aşağıdaki gibi servis çağırım kodu olsun:
ClientConfig config = new DefaultClientConfig();
config.getClasses().add(MOXyJsonProvider.class);
Client client = Client.create(config);
WebResource service = client.resource(UriBuilder.fromUri(url).build());
MyEntity[] services = service.accept(MediaType.APPLICATION_JSON)
		.get(new GenericType<MyEntity[]>() {});
Bu örnekte bir servisten MyEntity dizisi alıyoruz. MyEntity sınıfı aşağıdaki gibi olsun:
import java.io.*;
import java.util.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
public class IadeEntity implements Serializable{

	private long id;
	
	@XmlJavaTypeAdapter(DateAdapter.class)
	private Date myDate;

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public Date getMyDate() {
		return myDate;
	}

	public void setMyDate(Date myDate) {
		this.myDate = myDate;
	}
	
}
DateAdapter sınıfı da aşağıdaki gibidir:
import java.text.SimpleDateFormat;
import java.util.Date;
 
import javax.xml.bind.annotation.adapters.XmlAdapter;

public class DateAdapter extends XmlAdapter<String, Date> {
 
    private SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm");
 
    @Override
    public String marshal(Date date) throws Exception {
        return dateFormat.format(date);
    }
 
    @Override
    public Date unmarshal(String string) throws Exception {
        return dateFormat.parse(string);
    }

}
Bu şekilde web servis cevabındaki dd.MM.yyyy HH:mm formatındaki tarih, MyEntity sınıfındaki mYDate nesnesine bind edilecektir.

Veri

Jersey İle Rest Servisi Çağrılması

Jersey ile REST servisleri hazırlandığı gibi varolan bir REST servisini çağırmak için de kullanılabilir. Bunun için aşağıdaki jar'ları ekleyebilirsiniz :
  • jersey-client.jar
  • jersey-core.jar
Eğer Maven kullanıyorsanız aşağıdaki gibi bağımlığı ekleyebilirsiniz :
  <dependency>
    <groupId>com.sun.jersey</groupId>
   <artifactId>jersey-client</artifactId>
   <version>1.14</version>
</dependency>
GET ile çağrıldığında bir String döndüren bir REST servisini çağıralım:
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;

public class RestClientTest {

	public static void main(String[] args) {

		Client client = Client.create();

		WebResource webResource = client.resource("http://localhost:8080/book.rest/rest/helloworld");

		ClientResponse response = webResource.accept("text/plain").get(ClientResponse.class);

		if (response.getStatus() != 200) {
			System.out.println("fail:" + response.getStatus());
		}

		String output = response.getEntity(String.class);
		
		System.out.println("response : "+output);

	}

}
http://localhost:8080/book.rest/rest/helloworld GET ile çağrıldığında size Hello World yazısını dönen bir servistir. Cevap text/plain şeklinde dönmektedir. Önce Client nesnesi yaratılıyor. Client'den yeni bir kaynak nesnesi oluşturuluyor. Cevap olarak ClientResponse alacak şekilde get method'u çağrılıyor. Ardından 200 cevabı dönüldüyse (HTTP status kodu 200 başarılı anlamına geliyor) gelen cevap alınıp çıktıya basılıyor.

Örnek

Gömülü Jetty ve Jersey İle Rest Servisi

Jetty ile standalone (tek başına çalışan java uygulaması) bir rest uygulaması yapılabilir. Bunun için aşağıdaki gibi gerekli bağımlılıklar pom.xml'e eklenir:
<dependencies>
<dependency>
	<groupId>org.eclipse.jetty</groupId>
	<artifactId>jetty-server</artifactId>
	<version>9.4.6.v20170531</version>
</dependency>
<dependency>
	<groupId>org.eclipse.jetty</groupId>
	<artifactId>jetty-servlet</artifactId>
	<version>9.4.6.v20170531</version>
</dependency>
<dependency>
	<groupId>org.eclipse.jetty</groupId>
	<artifactId>jetty-util</artifactId>
	<version>9.4.6.v20170531</version>
</dependency>
<dependency>
	<groupId>org.glassfish.jersey.core</groupId>
	<artifactId>jersey-server</artifactId>
	<version>2.25.1</version>
</dependency>
<dependency>
	<groupId>org.glassfish.jersey.containers</groupId>
	<artifactId>jersey-container-servlet-core</artifactId>
	<version>2.25.1</version>
</dependency>
<dependency>
	<groupId>org.glassfish.jersey.containers</groupId>
	<artifactId>jersey-container-jetty-http</artifactId>
	<version>2.25.1</version>
</dependency>
<dependency>
	<groupId>org.glassfish.jersey.media</groupId>
	<artifactId>jersey-media-json-jackson</artifactId>
	<version>2.25.1</version>
</dependency>
</dependencies>
Jetty ve Jersey ile ilgili bağımlılıklar eklenmiştir.
Servis kodumuz aşağıdaki gibi olsun
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("msg")
public class MyMessage {
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String getMessage() {
        
        return "My message\n";
    }
    
    @GET
    @Path("j")
    @Produces(MediaType.APPLICATION_JSON)
    public String getMessageJson() {
        
        return "{\"message\":\"My message\"}";
    }
    
    @GET
    @Path("jo")
    @Produces(MediaType.APPLICATION_JSON)
    public MsgObj getMessageJsonObject() {
        
    	MsgObj msgObj=new MsgObj();
    	msgObj.setName("World");
    	msgObj.setTitle("Hello");
    	
        return msgObj;
    }
}
Üç tane servis metodu tanımlanmıştır ve GET ile mesaj ve nesne döndürmektedirler. Servis hazır olduğunda göre artık uygulamayı yapabiliriz :
import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.servlet.ServletContainer;
public class Main {
    public static void main(String[] args) {
        Server server = new Server(8080);
        ServletContextHandler ctx = 
                new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
                
        ctx.setContextPath("/");
        server.setHandler(ctx);
        ServletHolder serHol = ctx.addServlet(ServletContainer.class, "/rest/*");
        serHol.setInitOrder(1);
        serHol.setInitParameter("jersey.config.server.provider.packages", 
                "com.fibiler.services");
        try {
            server.start();
            server.join();
        } catch (Exception ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            server.destroy();
        }
    }
}

Uygulama çalıştırıldığında artık 8080 portu dinlenecektir. Tüm servislerin /rest/ şeklinde çağrılması gerekir. com.fibiler.services ile belirtilen servislerin bulunduğu pakettir. Servislerin paketi eklenmesi gerekmektedir. Uygulama çalıştığında ekranda aşağıdaki gibi bir mesaj gözükecektir:
2017-06-21 13:38:22.617:INFO::main: Logging initialized @552ms to org.eclipse.jetty.util.log.StdErrLog
2017-06-21 13:38:22.712:INFO:oejs.Server:main: jetty-9.4.6.v20170531
2017-06-21 13:38:23.421:INFO:oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler@51bf5add{/,null,AVAILABLE}
2017-06-21 13:38:23.656:INFO:oejs.AbstractConnector:main: Started ServerConnector@72ef8d15{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
2017-06-21 13:38:23.656:INFO:oejs.Server:main: Started @1593ms
Artık servisleri
http://localhost:8080/rest/msg
http://localhost:8080/rest/msg/j
http://localhost:8080/rest/msg/jo
şeklinde çağırdığınızda cevap alabileceksiniz.
Bu örnek http://zetcode.com/articles/jerseyembeddedjetty/ sayfasından alınmıştır.

Örnek

Rest Api Excepition Yakalayan Bir Örnek

Jax-RS exception'ları genel olarak yakalamanızı ve özelleştirmenizi sağlamaktadır. Aşağıdaki gibi bir sınıfın eklenmesi yeterlidir:
import java.io.PrintWriter;
import java.io.StringWriter;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mercury.rest.api.ApiResponse;
import com.mercury.rest.api.Error;

@Provider
public class GenericExceptionMapper implements ExceptionMapper<Throwable>{
	
	private static Logger logger=LoggerFactory.getLogger(GenericExceptionMapper.class);

	@Override
	public Response toResponse(Throwable e) {
		
		logger.error(e.getLocalizedMessage(),e);
		
		ApiResponse apiResponse=new ApiResponse();
		
		setHttpStatus(e, apiResponse);
		apiResponse.setErrorCode("GENERIC_ERROR");
		
		apiResponse.setErrorDesc(e.getMessage());
		
		StringWriter errorStackTrace = new StringWriter();
		e.printStackTrace(new PrintWriter(errorStackTrace));
		apiResponse.setErrorDetails(errorStackTrace.toString());
		
		apiResponse.setErrorMessage("Bir hata olustu");
		
		return Response.status(apiResponse.getStatus())
				.entity(apiResponse)
				.type(MediaType.APPLICATION_JSON)
				.build();	
	}
	
	private void setHttpStatus(Throwable ex, ApiResponse errorMessage) {
		if(ex instanceof WebApplicationException ) {
			errorMessage.setStatus(((WebApplicationException)ex).getResponse().getStatus());
		} else {
			errorMessage.setStatus(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
		}
	}

}
ExceptionMapper sınıfını implement ederek tüm Throwable'dan türeyen exception'ların işleneceği bildirilmiştir. Bir hata oluştuğunda Response toResponse(Throwable e) yöntemi çağrılacaktır. Bu yöntemde exception'dan bir cevap üretilip verilmesi gerekir. ApiResponse sınıfını ekleyelim :
public class ApiResponse {

	private int status;
	private String errorCode;
	private String errorDesc; // develoepr icin kisa aciklama
	private String errorMessage; //kullanici icin
	private String errorDetails; // developer icin detail

	public int getStatus() {
		return status;
	}
	public void setStatus(int status) {
		this.status = status;
	}
	public String getErrorCode() {
		return errorCode;
	}
	public void setErrorCode(String errorCode) {
		this.errorCode = errorCode;
	}
	public String getErrorDesc() {
		return errorDesc;
	}
	public void setErrorDesc(String errorDesc) {
		this.errorDesc = errorDesc;
	}
	public String getErrorMessage() {
		return errorMessage;
	}
	public void setErrorMessage(String errorMessage) {
		this.errorMessage = errorMessage;
	}
	public String getErrorDetails() {
		return errorDetails;
	}
	public void setErrorDetails(String errorDetails) {
		this.errorDetails = errorDetails;
	}
	
}
Bu sınıf cevaplar için kullanılan bir sınıftır. Response.status(apiResponse.getStatus()) .entity(apiResponse) .type(MediaType.APPLICATION_JSON) .build(); ile apiResponse cevap olarak dönülmektedir.

Örnek

Jersey ile BASIC HTTP Athentication Örneği

Bu örnekte GET ile bir URL'den json alınmaktadır. Sadece BASIC HTTP Auhentication ile kullanıcı adı ve şifresi verilmektedir. Örnek aşağıdaki gibidir:
Client client = ClientBuilder.newClient();

HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic("username","testpassword"));
client.register(feature);

X result = client.target("http://example.com/get").request(MediaType.APPLICATION_JSON).get(X.class);
HttpAuthenticationFeature sınıfın basic() yöntemi ile BASIC HTTP Authentication yapılacağı belirtilir ve HttpAuthenticationFeature nesnesi alınır. Bu nesne Client nesnesine register edilir. En son satır ise http://example.com/get URL'ini çağrılır ve sonuç olarak X sınıfına uygun JSON alınır.

Örnek

Jersey'de Servis Adlarını ve Parametreleri Ekrana Basan Bir Örnek

Bu örnekte Jersey'de bir web projesinde hangi resource'ların olduğunu ekrana basacağız.
List<AbstractResource> list=new ArrayList<>();
AbstractResource resource1 = IntrospectionModeller.createResource(MyService1.class);
AbstractResource resource2 = IntrospectionModeller.createResource(MyService2.class);
list=Arrays.asList(resource1, resource2);
for (AbstractResource abstractResource : list) {
	
	String resourcePath=abstractResource.getPath().getValue(); 	
	
	for (AbstractSubResourceMethod srm :abstractResource.getSubResourceMethods())
	{
		String uri =  resourcePath + "/" + srm.getPath().getValue();
		
		// Query Param'lar Eklenir
		List<Parameter> parameters= srm.getParameters();			   
		for (Parameter parameter : parameters) {
		
			if(parameter.getSource()==null){
				continue;			    		
			}			    	
			if(parameter.getSource().name().equals("QUERY")){
				if(uri.indexOf("?")==-1){
					uri+="?";	
				}else{
					uri+="&";	
				}
				uri+=parameter.getSourceName()+"=";
			}			    	
		}
		
		System.out.println("("+srm.getHttpMethod()+")" + uri + " [return type : " + srm.getReturnType().getName()+"]");
	}
	
	System.out.println();
}
MyService1, MyService2 şeklinde iki tane resource sınıfı var.
IntrospectionModeller.createResource(MyService.class);
ile bir Resource sınıfın modeli yüklenir. getSubResourceMethods() yöntemi ile resource sınıfındaki method'ların (sub resource) listesi alınabilir. AbstractSubResourceMethod sınıfından ise yöntemin method'u (GET, POST vb.), yöntemin dönüş tip, parametereleri alınabilir. Biz yukarıdaki örnekte sadece QueryParam tipindeki parametreleri aldık. ENTITY, MATRIX, PATH, COOKIE, HEADER, CONTEXT, FORM gibi farklı parametreler de eklenebilir. Örneği çalıştırdığımızda aşağıdaki gibi bir çıktı elde edebilir :
(GET)/category/search?q= [return type : java.lang.String]
(GET)/category/child/{categoryId} [return type : java.lang.String]
(PUT)/question//session?member_id= [return type : java.lang.String]
(PUT)/question//session/{session_id}/{question_id}/answer/{option_id} [return type : java.lang.String]

İpucu

Maven Shade Plugin ve MessageBodyReader Not Found for Media Type Hatası

Jersey-client kullanılan bir projede über jar üretildiğinde org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=application/json, type=class [a.b.] hatası
Jersey-client kullanılan bir projede über jar (fat jar) yarattığınızda soruna yol açabilmektedir. Uber jar yaratmak için genellikle Maven'da maven-shade-plugin kullanılır :
<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-shade-plugin</artifactId>
	<version>3.5.1</version>
	<executions>
		<execution>
			<phase>package</phase>
			<goals>
				<goal>shade</goal>
			</goals>
			<configuration>
				<artifactSet>
					<excludes>
						<exclude>junit:junit</exclude>
						<exclude>jmock:*</exclude>
						<exclude>*:xml-apis</exclude>
						<exclude>org.apache.maven:lib:tests</exclude>
					</excludes>
				</artifactSet>
			</configuration>
		</execution>
	</executions>
</plugin>
Bu plugin ile ürettiğiniz jar'ı kullanarak çalıştırdığınızda aşağıdaki hata alınabilir:
org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=application/json, type=class [com.fibiler.Test;, genericType=class [Lcom.fibiler.Test
Bunun sebebi maven-shade-plugin, jersey ile ilişkili jar'lardaki META-INF içindeki services klasöründeki tanımları birleştirmemesidir. Bunu çözmek için ServicesResourceTransformer kullanılır. Bu eklediğinde maven-shade-plugin şu hale döner :
<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-shade-plugin</artifactId>
	<version>3.5.1</version>
	<executions>
		<execution>
			<phase>package</phase>
			<goals>
				<goal>shade</goal>
			</goals>
			<configuration>
				<artifactSet>
					<excludes>
						<exclude>junit:junit</exclude>
						<exclude>jmock:*</exclude>
						<exclude>*:xml-apis</exclude>
						<exclude>org.apache.maven:lib:tests</exclude>
					</excludes>
				</artifactSet>
				<transformers>
					<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
				</transformers>							
			</configuration>
		</execution>
	</executions>
</plugin>

Bu şekilde über jar ürettiğinizde hata düzelmiş olması gerekir.



Bu Sayfayı Paylaş:

İletişim Bilgileri

Takip Et

Her Hakkı Saklıdır. Bu sitede yayınlanan tüm bilgi ve fikirlerin kullanımından fibiler.com sorumlu değildir. Bu sitede üretilmiş , derlenmiş içerikleri, fibiler.com'u kaynak göstermek koşuluyla kendi sitenizde kullanılabilirsiniz. Ancak telif hakkı olan içeriklerin hakları sahiplerine aittir