یکی از مسایلی که در حین توسعه API به آن برخواهیم خورد تکامل یافتن API است و برای هر مرحله از تکامل یک ورژن قرار میدهیم تا از تداخل کارکردی API جلوگیری کند ممکن است در یک ورژن یک ریسورس جدید معرفی کرده باشیم که بهینه تر از مدل قبلی اش است ولی کلاینت هایی وجود داشته باشند که با همان ورژن قدیمی کار میکنند اینجا وجود ورژن بندی کلاینت را آگاه میکند که از API درستی استفاده میکند و همچنین در سمت سرور هم توسعه آن قابل مدیریت است
http://host/v1/users http://host/v1/privileges http://host/v2/users http://host/v2/privileges
===> GET /users/3 HTTP/1.1 Accept: application/vnd.myname.v1+json <=== HTTP/1.1 200 OK Content-Type: application/vnd.myname.v1+json { "user": { "name": "John Smith" } }
استفاده از Media Type سفارشی (custom) برای ارائه ورژن بندی API :
فرض کنید API ما دارای دو ورژن است و قصد داریم آنرا در سرویس پیاده سازی کنیم
API Version 1 :
یک مثال ساده را فرض کنید که قرار است API یک Resource را بوسیله id برگرداند که این Resource ورژن 1 است. در این مثال ما از هدر اختصاصی application/vnd.baeldung.api.v1+json نیز استفاده خواهیم کرد و کللاینت موقع درخواست این هدر را accept خواهد داد
endpoint طراحی شده :
@RequestMapping( method = RequestMethod.GET, value = "/public/api/items/{id}", produces = "application/vnd.baeldung.api.v1+json" ) @ResponseBody public BaeldungItem getItem( @PathVariable("id") String id ) { return new BaeldungItem("itemId1"); }
* دقت کنید که پارامتر producer با نوع خاصی از media type مشخص شده است که API به آن رسیدگی میکند
resource ای که قرار است به کلاینت ارسال شود :
public class BaeldungItem { private String itemId; // standard getters and setters }
تست کردن API که version 1 میباشد :
@Test public void givenServiceEndpoint_whenGetRequestFirstAPIVersion_then200() { given() .accept("application/vnd.baeldung.api.v1+json") .when() .get(URL_PREFIX + "/public/api/items/1") .then() .contentType(ContentType.JSON).and().statusCode(200); }
و حالا طراحی API version 2 :
فرض کنید ما در Resource تغییراتی ایجاد کردیم که کلاینت هایی وجود دارند که نیاز به این تغییرات جدید دارند ولی همچنان میخواهیم کلاینت هایی که version 1 را استفاده میکنند هم داشته باشیم
مثلا این تغییرات جدید شامل itemName شده است :
public class BaeldungItemV2 { private String itemName; // standard getters and setters }
طراحی endpoint برای version 2 با media type سفارشی جدید:
@RequestMapping( method = RequestMethod.GET, value = "/public/api/items/{id}", produces = "application/vnd.baeldung.api.v2+json" ) @ResponseBody public BaeldungItemV2 getItemSecondAPIVersion(@PathVariable("id") String id) { return new BaeldungItemV2("itemName"); }
هر وقت کلاینت درخواست خود را با accept کردن application/vnd.baeldung.api.v1+json ارسال کرد به endpoint ورژن 1 از Resource را دریافت میکند و هرگاه درخواست شامل accept کردن application/vnd.baeldung.api.v2+json بود Resource ورژن 2 را دریافت میکند
تست API Version 2 :
@Test public void givenServiceEndpoint_whenGetRequestSecondAPIVersion_then200() { given() .accept("application/vnd.baeldung.api.v2+json") .when() .get(URL_PREFIX + "/public/api/items/2") .then() .contentType(ContentType.JSON).and().statusCode(200); }
Media Type های سفارشی بر روی Class level :
در طراحی ها میتوانیم برای طراحی کنترلر های هر یک از version ها یک کلاس مجزا در نظر بگیریم و RequestMapping@ را در سطح کلاس اعمال کنیم و پارامتر producer را برای کل درخواست هایی که قرار است در ورژن خاصی هندل شود اعمال کنیم :
@RestController @RequestMapping( value = "/", produces = "application/vnd.baeldung.api.v1+json" ) public class CustomMediaTypeController