ASP.NET MVC:Filter 와 Action 의 실행 안내

15092 단어 FilterAction
contrller 의 이름 에 따라 contrller 대상 을 정확하게 예화 하 였 습 니 다.MVC Handler 의 BeginProcessRequest 방법 으로 돌아 가면 controller 대상 을 얻 은 후 IAsyncController 인지 아 닌 지 를 먼저 판단 하고,그렇다면 비동기 실행 을 위 한 의뢰 를 만 들 수 있 습 니 다.일반적으로 우 리 는 Controller 류 를 계승 하 는데 이것 은 IAsyncController 가 아니 기 때문에 Controller 의 Execute 방법 을 직접 집행 합 니 다.Execute 방법 은 Controller 의 기본 클래스 Controller Base 에서 정 의 된 것 입 니 다.이 방법 은 안전 검 사 를 제외 하고 Controller Context(Controller Base 와 Request 정보 포함)를 초기 화 했 습 니 다.핵심 은 Execute Core 방법 을 호출 한 것 입 니 다.이것 은 Controller Base 에서 이미지 추출 방법 으로 Controller 류 에서 이 루어 집 니 다.
 
protected override void ExecuteCore() {
PossiblyLoadTempData();
try {
string actionName = RouteData.GetRequiredString("action");
if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) {
HandleUnknownAction(actionName);
}
}
finally {
PossiblySaveTempData();
}}
이 방법 은 비교적 간단 합 니 다.우선 임시 데 이 터 를 불 러 옵 니 다.이것 은 child action 일 때 만 나타 나 며 토론 하지 않 습 니 다.다음은 action 의 이름 을 얻 은 다음 에 Invoke Action 입 니 다.여기 있 는 Action Invoker 는 Controller Action Invoker 유형의 대상 입 니 다.우 리 는 그의 Invoke Action 방법 을 살 펴 보 겠 습 니 다.
 
public virtual bool InvokeAction(ControllerContext controllerContext, string actionName) {
if (controllerContext == null) {
throw new ArgumentNullException("controllerContext");
}
if (String.IsNullOrEmpty(actionName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
}
ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);
ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);
if (actionDescriptor != null) {
FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);
try {
AuthorizationContext authContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);
if (authContext.Result != null) {
// the auth filter signaled that we should let it short-circuit the request
InvokeActionResult(controllerContext, authContext.Result);
}
else {
if (controllerContext.Controller.ValidateRequest) {
ValidateRequest(controllerContext);
}
IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor);
ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters);
InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result);
}
}
catch (ThreadAbortException) {
// This type of exception occurs as a result of Response.Redirect(), but we special-case so that
// the filters don't see this as an error.
throw;
}
catch (Exception ex) {
// something blew up, so execute the exception filters
ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);
if (!exceptionContext.ExceptionHandled) {
throw;
}
InvokeActionResult(controllerContext, exceptionContext.Result);
}
return true;
}
// notify controller that no method matched
return false;}
이것 은 매우 핵심 적 인 방법 으로 많은 작업 을 이 안에서 완성 합 니 다.ASP.NET MVC 에는 Descriptor 로 끝 나 는 유형 이 몇 개 있 습 니 다.먼저 Controller Descriptor 를 얻 었 습 니 다.이것 은 비교적 간단 합 니 다.실제 되 돌아 온 것 은 Reflected Controller Descriptor 대상 입 니 다.두 번 째 단 계 는 실제 적 으로 Reflected Controller Descriptor 의 FindAction 방법 을 호출 하여 Action Descriptor 를 얻 었 습 니 다.Action Descriptor 의 가장 중요 한 속성 은 MethodInfo 입 니 다.이것 이 바로 현재 action name 에 대응 하 는 Action 방법 입 니 다.FindAction 방법 내 부 는 실제 적 으로 Action MethodSelector 의 FindAction Method 를 호출 하여 MethodInfo 를 얻 었 습 니 다.이 방법 은 controller 의 모든 방법의 이름 을 반사 한 다음 에 action name 과 일치 할 것 이 라 고 상상 할 수 있 습 니 다.실제로 ASP.NET 은 추가 적 인 기능 도 지원 합 니 다.주로:1.Action Name Attribute 속성 을 통 해 action 의 이름 을 바 꿉 니 다.2.ActionMethodSelector Attribute 를 지원 하여 action 방법 을 선별 합 니 다.예 를 들 어[HttpPost]와 같은 것 입 니 다.다음은 ActionMethodSelector 의 실현 을 간단하게 살 펴 보고 크게 4 단계 로 나 뉜 다.먼저 구조 함수 에서 다음 과 같은 방법 으로 contrller 의 모든 action 방법 을 반사 했다.
 
private void PopulateLookupTables() {
MethodInfo[] allMethods = ControllerType.GetMethods(BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public);
MethodInfo[] actionMethods = Array.FindAll(allMethods, IsValidActionMethod);
AliasedMethods = Array.FindAll(actionMethods, IsMethodDecoratedWithAliasingAttribute);
NonAliasedMethods = actionMethods.Except(AliasedMethods).ToLookup(method => method.Name, StringComparer.OrdinalIgnoreCase);
}FindActionMethod :
public MethodInfo FindActionMethod(ControllerContext controllerContext, string actionName) {
List<MethodInfo> methodsMatchingName = GetMatchingAliasedMethods(controllerContext, actionName);
methodsMatchingName.AddRange(NonAliasedMethods[actionName]);
List<MethodInfo> finalMethods = RunSelectionFilters(controllerContext, methodsMatchingName);
switch (finalMethods.Count) {
case 0:
return null;
case 1:
return finalMethods[0];
default:
throw CreateAmbiguousMatchException(finalMethods, actionName);
} }
이 방법 은 매우 뚜렷 하 다.이름 을 바 꾼 후에 부합 되 는 것 을 찾 은 다음 에 모든 방법 으로 ActionMethodSelector Attribute 의 조건 을 만족 시 키 는 지 판단 한다.마지막 으로 일치 하 는 MethodInfo 를 되 돌려 주거 나 이상 을 던 지 거나 null 로 되 돌려 줍 니 다.세 단계 의 실현 은 결코 어렵 지 않 으 니 더 이상 분석 하지 않 겠 다.세 번 째 단 계 는 Filter 를 얻 는 것 이다.FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);실제 호출 된 것 은 FilterProviders.Providers.GetFilters(controllerContext,actionDescriptor)입 니 다.이곳 의 코드 스타일 은 이전 과 달리 각종 의뢰 를 즐겨 사용 하기 때문에 코드 를 읽 기 가 좀 어려워 서 같은 사람 이 쓴 것 이 아 닐 것 같 습 니 다.아래 의 분석 은 모두 실제 실 행 된 코드 를 직접 제시한다.먼저 FilterProvider 의 구조 함 수 를 살 펴 보 자.
 
static FilterProviders() {
Providers = new FilterProviderCollection();
Providers.Add(GlobalFilters.Filters);
Providers.Add(new FilterAttributeFilterProvider());
Providers.Add(new ControllerInstanceFilterProvider());
}
ASP.NET 에서 Action 에 filter 를 추가 하 는 방법 은 모두 다음 과 같다.1.applicationStart 등록 전역 filter 2.속성 을 통 해 Action 방법 이나 Controller 에 filter 3.Controller 류 자체 도 IAction Filter 등 몇 개의 인 터 페 이 스 를 실현 합 니 다.Controller 류 의 몇 가지 관련 방법 을 재 작성 하여 filter 를 추가 합 니 다.이 세 가지 방식 은 세 개의 FilterProvider 에 대응 하 는데 이 세 개의 Provider 의 실현 은 모두 어렵 지 않 고 분석 하지 않 는 다.지금까지 준비 작업 이 완료 되 었 습 니 다.다음은 Filter 와 Action 을 수행 할 것 입 니 다.ASP.NET 의 Filter 는 모두 4 가지 가 있 습 니 다.
Filter Type
Interface
Description
Authorization
IAuthorizationFilter
Runs first
Action
IActionFilter
Runs before and after the action method
Result
IResultFilter
Runs before and after the result is executed
Exception
IExceptionFilter
Runs if another filter or action method throws an exception 아래 소스 코드 의 실현 을 보십시오.먼저 Invoke Authorization Filters:
 
protected virtual AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor) {
AuthorizationContext context = new AuthorizationContext(controllerContext, actionDescriptor);
foreach (IAuthorizationFilter filter in filters) {
filter.OnAuthorization(context);
if (context.Result != null) {
break;
}
}
return context;}
는 IAuthorizationFilter 인 터 페 이 스 를 실현 할 때 검증 실 패 를 표시 하려 면 OnAuthorization 방법 에서 매개 변수 context 의 Result 를 Action Result 로 설정 해 야 합 니 다.인증 에 실 패 했 을 때 표시 해 야 할 페이지 를 표시 합 니 다.그 다음 에 검증 에 실패 하면 context 의 Result 를 실행 하고 성공 하면 GetParameterValues 를 실행 하여 Action 의 인 자 를 얻어 야 합 니 다.이 방법 내부 에서 Model Binding 을 진행 합 니 다.이것 도 ASP.NET 의 중요 한 특성 입 니 다.그 다음 에 InvokeAction MethodWithFilters 와 InvokeAction ResultWithFilters 를 각각 집행 할 것 이다.이 두 가지 방법의 구 조 는 유사 하 다.하 나 는 Action 방법 과 IAction Filter 를 집행 하 는 것 이 고 하 나 는 Action Result 와 IResultFilter 를 집행 하 는 것 이다.Invoke Action Method With Filters 를 예 로 들 어 분석 한 결과
 
protected virtual ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext, IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters) {
ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters);
Func<ActionExecutedContext> continuation = () =>
new ActionExecutedContext(controllerContext, actionDescriptor, false /* canceled */, null /* exception */) {
Result = InvokeActionMethod(controllerContext, actionDescriptor, parameters)
};
// need to reverse the filter list because the continuations are built up backward
Func<ActionExecutedContext> thunk = filters.Reverse().Aggregate(continuation,
(next, filter) => () => InvokeActionMethodFilter(filter, preContext, next));
return thunk();
}
이 코드 는 약간 함수 적 인 스타일 이 고 이런 스타일 에 익숙 하지 않 은 사람 은 이해 하기 어 려 울 것 같다.함수 식 프로 그래 밍 언어 를 사용 하면 여기 Aggregate 는 사실 folder 입 니 다.folder:(a->b->b)->b->[a]->b folder 는 하나의 함 수 를 첫 번 째 매개 변수 로 받 아들 입 니 다.이 함수 의 매개 변 수 는 두 가지 가 있 습 니 다.유형 은 a,b 이 고 반환 유형 은 b 입 니 다.두 번 째 매개 변 수 는 유형 b 입 니 다.시작 값 으로 세 번 째 매개 변 수 는 a 의 배열 입 니 다.foldr 의 기능 은 배열 의 a 와 지난번 에 첫 번 째 매개 변수 함수(f)를 호출 한 반환 값 을 f 의 두 매개 변수 로 호출 하 는 것 입 니 다.첫 번 째 f 를 호출 할 때 시작 값 을 사용 합 니 다.C\#에 대해 대상 을 대상 으로 하 는 방식 으로 표시 하 는 것 은 IEnummerable 의 확장 방법 으로 이 루어 진 것 입 니 다.C\#함 수 를 함수 의 매개 변수 로 직접 전달 할 수 없 기 때문에 들 어 오 는 것 은 의뢰 입 니 다.말 하기 가 까다 롭 습 니 다.예 를 들 어
 
static void AggTest()
{
int[] data = { 1, 2, 3, 4 };
var res = data.Aggregate("String", (str, val) => str + val.ToString());
Console.WriteLine(res);
}
마지막 출력 결 과 는 String 1234 입 니 다.InvokeAction MethodWith Filters 의 실현 으로 돌아 갑 니 다.여기에 대응 하 는 유형 a 는 IAction Filter 이 고 유형 b 는 Func이 며 초기 값 은 continuation 입 니 다.만약 에 우리 가 3 개의 filter 가 있다 고 가정 하면[f1,f2,f3]thunk 가 최종 적 으로 무엇 인지 살 펴 보 겠 습 니 다.첫 번 째:next=continue,filter=f1,반환 값()=>InvokeAction MethodFilter(f1,preContext,continue)두 번 째:next=()=>InvokeAction MethodFilter(f1,preContext,continue),filter=f2 반환 값:()=>InvokeAction MethodFilter(f2,preContext,()=>InvokeActionMethodFilter(f1,preContext,continue),최종:thunk=()=>InvokeActionMethodFilter(f3,preContext,()=>InvokeActionMethodFilter(f2,preContext,()=>InvokeActionMethodFilter(f1,preContext,continue));return thunk()까지 모든 진정한 코드 가 실행 되 지 않 았 습 니 다.관건 은 thunk 이라는 의뢰 를 구축 하고 thunk 를 위의 모습 으로 펼 치 는 것 입 니 다.진정한 호출 순서 가 어떤 지 잘 알 아야 합 니 다.여기에 비교적 많은 필 묵 을 써 서 Aggregate 방법 을 통 해 호출 체인 을 어떻게 구성 하 는 지 소 개 했 는데 여기에 이것 을 전문 적 으로 소개 한 글 도 참고 할 수 있다.상상 해 보 세 요.만약 에 filter 의 기능 이 f 를 호출 하 는 Executing 방법 을 옮 겨 다 니 고 Action 방법 을 호출 한 다음 에 f 의 Executed 방법 을 순서대로 호출 하 는 것 이 라면 교체 로 실현 할 수 있 습 니 다.이렇게 추상 적 이 고 복잡 할 필요 가 없습니다.관건 은 ASP.NET MVC 가 filter 에서 이상 한 처리 에 대해 특별한 점 이 있 습 니 다.InvokeAction MethodFilter 의 실현 을 보 세 요.
 
internal static ActionExecutedContext InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func<ActionExecutedContext> continuation) {
filter.OnActionExecuting(preContext);
if (preContext.Result != null) {
return new ActionExecutedContext(preContext, preContext.ActionDescriptor, true /* canceled */, null /* exception */) {
Result = preContext.Result
};
}
bool wasError = false;
ActionExecutedContext postContext = null;
try {
postContext = continuation();
}
catch (ThreadAbortException) {
// This type of exception occurs as a result of Response.Redirect(), but we special-case so that
// the filters don't see this as an error.
postContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false /* canceled */, null /* exception */);
filter.OnActionExecuted(postContext);
throw;
}
catch (Exception ex) {
wasError = true;
postContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false /* canceled */, ex);
filter.OnActionExecuted(postContext);
if (!postContext.ExceptionHandled) {
throw;
}
}
if (!wasError) {
filter.OnActionExecuted(postContext);
}
return postContext;
}
코드 가 좀 길 어 요.먼저 filter 를 촉발 한 OnAction Executing 방법 입 니 다.이것 이 방법의 핵심 입 니 다.다음 포 인 트 는 post Context=continuation()입 니 다.마지막 으로 OnAction Executed 방법 입 니 다.위의 전개 식 과 결합 하면 실제 호출 순 서 는 다음 과 같 습 니 다.
 
f3.Executing->f2.Executing->f1.Exectuing->InvokeActionMethod->f1.Executed->f2->Executed->f3.Executed.
그러면 소스 코드 의 주석//need to reverse the filter list because the continuations are built up backward 의 뜻 도 분명 합 니 다.filter 를 거꾸로 배열 해 야 정확 한 실행 순서 입 니 다.또 하나의 filter 는 이상 이 발생 했 을 때 촉발 된다.InvokeAction 방법 에서 트리거 코드 를 catch 블록 에 넣 는 것 을 볼 수 있 습 니 다.IException Filter 의 트리거 프로 세 스 는 비교적 간단 하여 설명 을 많이 하지 않 습 니 다.유일 하 게 주의해 야 할 것 은 exception Handled 속성 이 true 로 설정 되 었 을 때 이상 을 던 지지 않 는 다 는 것 입 니 다.이 속성 은 각종 context 아래 에 있 습 니 다.그들의 효 과 는 같 습 니 다.예 를 들 어 OnAction Executed 방법 에서 도 그 를 true 로 설정 할 수 있 고 이상 을 던 지지 않 습 니 다.이 글 은 원본 코드 를 분석 하지 않 고 filter 프로 세 스 에서 이상 이 발생 한 후의 실행 순 서 를 상세 하 게 소개 했다.마지막 으로 Action Method 의 실행 에 대해 말씀 드 리 겠 습 니 다.앞에서 저 희 는 methodInfo 를 얻 었 고 data binding 을 통 해 인 자 를 얻 었 습 니 다.Action Method 를 호출 하 는 것 은 모든 것 이 준비 되 어 있 을 것 입 니 다.asp.net mvc 이쪽 의 처 리 는 비교적 복잡 합 니 다.Reflected Action Descriptor 는 Action MethodDispatcher 의 Execute 방법 을 호출 합 니 다.이 방법 은 다음 과 같 습 니 다.
 
public object Execute(ControllerBase controller, object[] parameters) {
return _executor(controller, parameters);
}
이곳 의executor 는 delegate object ActionExecutor(Controller Base controller,object[]parameters);exectuor 의 할당 값 은 하나의 방법 을 통 해 Expression 을 이용 하여 방법 체,매개 변 수 를 조합 하 는 것 입 니 다.코드 는(Action MethodDispatcher.cs):static Action Executor GetExecutor(MethodInfo methodInfo methodInfo)에 붙 이지 않 고 복잡 합 니 다.여기 서 이해 하기 어 려 운 것 은 MethodInfo 와 parameters 가 모두 있 으 니 직접 반사 하면 된다 는 것 이다.왜 이렇게 복잡 해 야 하 는 지 나 는 위의 Execute 방법 을 다음 과 같이 바 꾸 었 다.
 
public object Execute(ControllerBase controller, object[] parameters) {
return MethodInfo.Invoke(controller, parameters);
//return _executor(controller, parameters);
}
운행 결 과 는 완전히 같다.나 는 mvc 소스 코드 가 이렇게 실현 되 는 것 은 반드시 고려 가 있 을 것 이 라 고 믿는다.이것 은 계속 연구 해 야 한다.마지막 으로 이해 하기 위해 함수 호출 도 를 한 장 동봉 하 니 참고 하 시기 바 랍 니 다.그림 이 비교적 커서 클릭 하면 원 도 를 볼 수 있다.
BeginProcessRequest

좋은 웹페이지 즐겨찾기