Хороший момент был сделан @blowdart в his comment о том, следует ли ожидать возвращения 401/403. В любом случае, я пробовал использовать другой подход для выполнения задач, заданных OP, изменяя поведение фильтров авторизации MVC по умолчанию, чтобы мы возвращали json, когда пользователь несанкционирован.
Первое, что я сделал, это создать новый IAsyncAuthorizationFilter
, который отформатирует несанкционированный результат как json для запроса ajax. Это будет в основном:
- Wrap существующий фильтр
- Выполнить обернутый фильтр
- В случае, если пользователь не авторизован на обернутой фильтром, возвращает JSON для AJAX-запросы
Это будет CustomJsonAuthorizationFilter
класс:
public class CustomJsonAuthorizationFilter : IAsyncAuthorizationFilter
{
private AuthorizeFilter wrappedFilter;
public CustomJsonAuthorizationFilter(AuthorizeFilter wrappedFilter)
{
this.wrappedFilter = wrappedFilter;
}
public async Task OnAuthorizationAsync(Microsoft.AspNet.Mvc.Filters.AuthorizationContext context)
{
await this.wrappedFilter.OnAuthorizationAsync(context);
if(context.Result != null && IsAjaxRequest(context))
{
context.Result = new JsonResult(new
{
success = false,
error = "You must be signed in."
});
}
return;
}
//This could be an extension method of the HttpContext/HttpRequest
private bool IsAjaxRequest(Microsoft.AspNet.Mvc.Filters.AuthorizationContext filterContext)
{
return filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest";
}
}
Затем я создал IApplicationModelProvider
, чтобы обернуть все существующие AuthorizeFilter
новым пользовательским фильтром. AuthroizeFilter
добавляется AuthorizationApplicationModelProvider
, но новый провайдер будет запущен по умолчанию, так как порядок поставщика по умолчанию -990.
public class CustomFilterApplicationModelProvider : IApplicationModelProvider
{
public int Order
{
get { return 0; }
}
public void OnProvidersExecuted(ApplicationModelProviderContext context)
{
//Do nothing
}
public void OnProvidersExecuting(ApplicationModelProviderContext context)
{
this.ReplaceFilters(context.Result.Filters);
foreach(var controller in context.Result.Controllers)
{
this.ReplaceFilters(controller.Filters);
foreach (var action in controller.Actions)
{
this.ReplaceFilters(action.Filters);
}
}
}
private void ReplaceFilters(IList<IFilterMetadata> filters)
{
var authorizationFilters = filters.OfType<AuthorizeFilter>().ToList();
foreach (var filter in authorizationFilters)
{
filters.Remove(filter);
filters.Add(new CustomJsonAuthorizationFilter(filter));
}
}
}
Наконец, обновление ConfigureServices
в запуске с новым поставщиком модели приложения:
services.TryAddEnumerable(
ServiceDescriptor.Transient<IApplicationModelProvider, CustomFilterApplicationModelProvider>());
из интереса, что случилось с пропускающими 401 или 403 сделать его обратно на сторону клиента и реагировать соответствующим образом? Это та сцена, к которой мы стремимся (и не используем cookie auth для ajax, из-за проблем CSRF). Вот почему трудно делать то, что вы хотите, это не то, что мы ожидаем от людей. – blowdart
Поскольку в моем javascript обратные вызовы с ошибкой jquery являются терминальными ошибками, которые останавливают рабочий процесс, а 200 запросов с ошибкой = true являются ожидаемыми ошибками, которые являются частью рабочего процесса. Я не ожидаю ошибки. Это упрощает мой код на стороне клиента. Это не опубликованный/публичный API, поэтому я в порядке, не придерживаясь конвенций. –
Кроме того, когда речь идет о закрытых приложениях с неопубликованными API-интерфейсами, вы не должны использовать соглашения для соглашений. Угадайте, что делает? ;) http://i.imgur.com/qqTFGWP.png –