Merhabalar.

Bu yazıda Content Negotiation konusuna değineceğiz.

Content Negotiation konusu dilimize çevirmek istersek istemci ile sunucu arasındaki içerik anlaşması denilebilir. HTTP protokolünün inşa edildiği şekile bakıldığında kullanıcının ne tür veri göndereceği ve sunucunun ne tür veri kabul edeceğinin özelleştirilmesidir. JAX-RS içerisinde kabul edilecek içerik türü @Consumes notasyonu ile, üretilecek içerik de @Produces notasyonu ile belirleniyor.

Örneğin şöyle bir kod bloğumuz olduğunu kabul edelim. Bu kabule göre yazılan web servisin öncelikli tercihi sınıfın da işaretli olduğu text/‘dır. Fakat bu web servisi kullanan bir müşteri text/xml tipinde bir veri gönderse sistem bunu reddetmeyecek ve ikinci sıradaki *jaxbBook ismindeki POST metoduna yönlendirecektir. Örneğin kullanıcı text/plain tipinde bir veri gönderirse birinci kurala (text/*) uyduğu için birinci POST metoduna girecektir. Ama text/xml tipinde veri gönderirse bu özel olarak ikinci POST metoduna uyduğu için işlem orada yapılacaktır. Content Negotiation konusunun asıl amacı da budur zaten. Aynı URL üzerinden farklı veri tiplerinde hizmet verebilmektir.

@Consumes("text/*")
@Path("/library")
public class Library {
    @POST
    public String stringBook(String book) {...}
    
    @Consumes("text/xml")
    @POST
    public String jaxbBook(Book book) {...}
}

Olaya bir de @Produces notasyonu açısından bakalım. Örneğin bir GET metodu vasıtası ile bir çağırım yapacağız ve bir türe göre veri elde edeceğiz. Şu kod parçasına göre eğer o GET isteğini şu çağırımla yaparsak JSON tipinde veri elde etmiş oluruz:

@Produces("text/*")
@Path("/library")
public class Library {

@GET
@Produces("application/json")
public String getJSON() {...}

@GET
public String get() {...}
}

GET /library Accept: application/json

Fakat RestEasy’nin dökümanına göre yukarıdaki çağrım tüm istemciler tarafından yapılamayabilir. Bazı istemciler Accept header’ını kabul etmeyebilir veya kullanmıyor olabilir. Bunun için bu içerik tercih meselesini proje içinde bizim çözmemiz lazım. RestEasy geliştiriciliri de bunun için 2 yol yapmışlar.

  • URL Based Negotiation
  • Query String Parameter-based negotiation

Şimdi bunlara bir bakalım.

URL Based Negotiation

RestEasy içindeki 2 adet context-param’ı web.xml dosyamız içerisinde kaydettiğimizde istemciden bir Accept header’ı gelsin gelmesin onu ezeriz ve olası sorunların büyük ölçüde önüne geçmiş oluruz. Buradaki URL based olayı ise şurdan gelmekteki adres üzerinden farklı tipteki üretilen veriler alınabilir.

Şimdi küçükü bir örnek uygulama yapalım.

Projemizin web.xml dosyasına iki adet context-param eklenecek. O eklenen context-param’lar şu şekildedir:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">
	<display-name>RestEasyTutorials</display-name>
	<context-param>
		<param-name>resteasy.media.type.mappings</param-name>
		<param-value>html : text/html, json : application/json, xml : application/xml</param-value>
	</context-param>
</web-app>

web.xml dosyamıza işlediğimiz hali ile 3 adet tipte veri alabiliriz. html, json ve xml. Üstelik bu 3 tipteki veriyi @Produces notasyonunu kullanmadan, direk URL üzerinden elde edebiliceğiz.

Web service üretecek Java sınıfımız da şu şekilde, hiçbir @Produces notasyonu yok ve üretilecek veri tipi tanımlamadık:

package restPackage;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("/library")
public class ContentNegotiation {
	@GET
	public String get() {
		return "Message Returned!";
	}
}

Şimdi ben http://localhost:8080/RestEasyTutorials/restService/library.xml adresine istekte bulunuyorum ve şununla karşılaşıyorum:

Şimdi de http://localhost:8080/RestEasyTutorials/restService/library.json adresine istekte bulunuyorum:

Gördüğümüz gibi URL üzerinden getirmek istediğimiz tipte bir veriyi sadece ilgili path’in sonuna uzantı gibi yazarak elde ediyoruz.

Query String Parameter-based Negotiation

RestEasy bizim verimizi istediğimiz tipte elde etmemizi URL tabanlı yapmasının yanında URL parametresi ile de yapabilir. Daha önce query string parametrelerini görmüştük. Şimdi aynı durumu negotiation meselesi için de kullanabiliriz. Bunun için web.xml dosyamızı güncellememiz lazım. Şöyle güncelleyelim:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">
	<display-name>RestEasyTutorials</display-name>
	<context-param>
		<param-name>resteasy.media.type.param.mapping</param-name>
		<param-value>valueFormat</param-value>
	</context-param>
</web-app>

web.xml’e tanımladığımız valueFormat param-value değerini URL’den bir parametre olarak göndereceğiz ve bu parametrenin değeri de veriyi ne tipte almak istediğimiz olacak. Java kodu üzerinde bir oynama yapmıyoruz. Şimdi http://localhost:8080/RestEasyTutorials/restService/library?valueFormat=application/json adresine istekte bulunduğumda header’lar şöyle dönüyor:

http://localhost:8080/RestEasyTutorials/restService/library?valueFormat=application/xml adresine istekte bulunduğumda ise header’lar şöyle:

Bu şekilde de URL’den gönderilen bir parametre vasıtası ile alınacak verinin formatını belirlemiş olduk.