Точное определение restful web service можно найти по первой ссылке в Google, тут я расскажу про задачи, которые могут быть решены с использованием таких сервисов, а также покажу реализацию 4-х методов, которые должны быть в restful сервисах (Get, Post, Put, Delete).
Итак, задача: у нас есть web приложение, которое отвечает за управление группировкой космических аппаратов, но кроме этого приложения существуют еще сторонние программы, которым необходим доступ к начальным условиям. Для решения этой задачи был сделан api controller, который реализовывал все необходимые операции. Контроллер возвращает либо JSON либо XML, в зависимости от заголовка запроса, но на реализации это никак не сказывается.
[Authorize] public class NuController : ApiController { private readonly IMapper _nuMapper = new NuMapper(); [Inject] public IUnitOfWork UnitOfWork { get; set; } // GET api/Nu public IEnumerable<NuViewModel> GetNUs() { var nus = UnitOfWork.NuRepository.Get(); var nuViewModels = nus.Select(nu => (NuViewModel)_nuMapper.Map(nu, typeof(NU), typeof(NuViewModel))).ToList(); return nuViewModels; } //// GET api/Nu/5 public NuViewModel GetNu(int id) { var nu = UnitOfWork.NuRepository.GetById(id); if (nu == null) { throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); } var nuViewModel = (NuViewModel)_nuMapper.Map(nu, typeof (NU), typeof (NuViewModel)); return nuViewModel; } //// GET api/Nu/GetSpacecrartNu/3 public IEnumerable<NuViewModel> GetSpacecraftNu(int id) { var nus = UnitOfWork.NuRepository.Get().Where(p => p.SpacecraftInitialData_ID == id); var nuViewModels = nus.Select(nu => (NuViewModel)_nuMapper.Map(nu,typeof(NU), typeof(NuViewModel))).ToList(); return nuViewModels; } //// PUT api/Nu/5 [Authorize(Roles = "admin")] public HttpResponseMessage PutNu(int id, NuViewModel nuVm) { if (!ModelState.IsValid || nuVm == null) { return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Bad request"); } if (id != nuVm.ID_NU) { return Request.CreateResponse(HttpStatusCode.BadRequest); } var nu = (NU)_nuMapper.Map(nuVm, typeof (NuViewModel), typeof (NU)); UnitOfWork.NuRepository.Update(nu); try { UnitOfWork.Save(); } catch (DbUpdateConcurrencyException ex) { return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex); } return Request.CreateResponse(HttpStatusCode.OK); } //// POST api/Nu [Authorize(Roles = "admin")] public HttpResponseMessage PostNu(NuViewModel nuVm) { if (ModelState.IsValid && nuVm != null) { var nu = (NU)_nuMapper.Map(nuVm, typeof(NuViewModel), typeof(NU)); UnitOfWork.NuRepository.Insert(nu); UnitOfWork.Save(); HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, nu); var link = Url.Link("DefaultApi", new {id = nu.ID_NU}); if (link != null) { response.Headers.Location = new Uri(link); } return response; } return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Bad request"); } //// DELETE api/Nu/5 [Authorize(Roles = "admin")] public HttpResponseMessage DeleteNu(int id) { var nu = UnitOfWork.NuRepository.GetById(id); if (nu == null) { return Request.CreateResponse(HttpStatusCode.NotFound); } UnitOfWork.NuRepository.Delete(nu); try { UnitOfWork.Save(); } catch (DbUpdateConcurrencyException ex) { return Request.CreateErrorResponse(HttpStatusCode.NotFound, ex); } return Request.CreateResponse(HttpStatusCode.OK, nu); } }
Тут есть несколько особенностей:
во-первых: Ninject из коробки не работает с api контроллером, так как api контроллер и обычный контроллер создаются по-разному. Для решения этой проблемы были написаны два класса: NinjectScope:
public class NinjectScope : IDependencyScope { protected IResolutionRoot resolutionRoot; public NinjectScope(IResolutionRoot kernel) { resolutionRoot = kernel; } public object GetService(Type serviceType) { IRequest request = resolutionRoot.CreateRequest(serviceType, null, new Parameter[0], true, true); return resolutionRoot.Resolve(request).SingleOrDefault(); } public IEnumerable<object> GetServices(Type serviceType) { IRequest request = resolutionRoot.CreateRequest(serviceType, null, new Parameter[0], true, true); return resolutionRoot.Resolve(request).ToList(); } public void Dispose() { var disposable = (IDisposable) resolutionRoot; if (disposable != null) { disposable.Dispose(); } resolutionRoot = null; } }
И NinjectResolver:
public class NinjectResolver : NinjectScope, IDependencyResolver { private readonly IKernel _kernel; public NinjectResolver(IKernel kernel) : base(kernel) { _kernel = kernel; } public IDependencyScope BeginScope() { return new NinjectScope(_kernel.BeginBlock()); } }
После этого дописываем строчку в NingectWebCommon.cs в метод:
private static IKernel CreateKernel(){ GlobalConfiguration.Configuration.DependencyResolver = new NinjectResolver(kernel); }
И все работает.
Во-вторых: роутинг к api контроллеру не поддерживает namespace, поэтому сделать контроллер в какой либо области приложения не получится и во избежание ошибок в Global.asax необходимо, чтобы строка WebApiConfig.Register(GlobalConfiguration.Configuration); была выше строк с регистрацией областей (Admin, Default). По умолчанию в аpi контроллере всего 4 action-a, для добавления нового action в контроллер необходимо добавить роут:
config.Routes.MapHttpRoute("NuApi", "api/{controller}/{action}/{id}", new {id = RouteParameter.Optional}, new {controller = "Nu"} );
Доступ к нему осуществляется по полному адресу.
Тестирование и доступ: Юнит-тесты api контроллера практически не отличаются от тестирования обычного:
Тестирование и доступ: Юнит-тесты api контроллера практически не отличаются от тестирования обычного:
private NuController _nuController; [SetUp] public void Setup() { _nuController = DependencyResolver.Current.GetService<NuController>(); } [Test] public void GetNus_Execute_ResultIsListNuVieModel() { var result = _nuController.GetNUs(); Assert.That(result, Is.InstanceOf<List<NuViewModel>>()); }
Так же запросы методами put и delete можно проверить программой Fiddler.
Через Html – helpers форму к Api контроллеру можно отправить следующим образом:
@using (@Html.BeginRouteForm("NuAp", new { controller = "Spacecraft", httproute = "true" }, FormMethod.Post, new { @role = "form" }))где DefaultApi – имя роута.
Отправка форм с помощью Ajax (JQuery).
Метод post: сначала необходимо изменить стандартное поведение при нажатии кнопки submit (unbind или off), затем в метод $.post передаем сериализованные данные формы:
Метод post: сначала необходимо изменить стандартное поведение при нажатии кнопки submit (unbind или off), затем в метод $.post передаем сериализованные данные формы:
this.formSubmitt = function onSubmit() { $('#form').unbind('submit').bind('submit', function () { $.post(this.action, $('#form').serialize()) .success(function (result) { $('#message').html(result.Message); $('#successResult').show(); }).error(function() { $('#errorResult').show(); }); return false; }); };Put и delete отличаются только методами, вместо $.post необходимо писать полный ajax запрос:
$.ajax({ url: api/Nu/5, type: put, data: $('#form').serialize(), success: function (result) { $('#message').html(result.Message); $('#successResult').show(); } })
Комментариев нет:
Отправить комментарий