Точное определение 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();
}
})
Комментариев нет:
Отправить комментарий