programing

ASP.NET MVC 검증 폼(Angular 포함)JS

minimums 2023. 4. 3. 21:22
반응형

ASP.NET MVC 검증 폼(Angular 포함)JS

MVC 4와 Angular JS(+ twitter 부트스트랩)의 프로젝트에서 일하고 있습니다.저는 MVC 프로젝트 'jQuery'에서 주로 사용합니다.검증", "Data Annotations" 및 "Razor"입니다.그런 다음 web.config에서 다음 키를 활성화하여 클라이언트의 모델 속성을 확인합니다.

<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />

예를 들어 모델에 다음과 같은 기능이 있는 경우:

[Required]
[Display(Name = "Your name")]
public string Name { get; set; }

이 Cshtml의 경우:

@Html.LabelFor(model => model.Name)
@Html.TextBoxFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name)

html 결과는 다음과 같습니다.

<label for="Name">Your name</label>
<input data-val="true" data-val-required="The field Your name is required." id="Name" name="Name" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="Name" data-valmsg-replace="true"></span>

그러나 AngularJS를 사용할 때는 다음과 같이 렌더링합니다.

<label for="Name">Your name</label>
<input type="text" ng-model="Name" id="Name" name="Name" required />
<div ng-show="form.Name.$invalid">
   <span ng-show="form.Name.$error.required">The field Your name is required</span>
</div>

이 문제를 해결할 도우미 또는 "데이터 주석"이 있는지 알 수 없습니다.Angular는 이해한다.JS에는 다음과 같은 기능이 많이 있습니다.

<div ng-show="form.uEmail.$dirty && form.uEmail.$invalid">Invalid:
    <span ng-show="form.uEmail.$error.required">Tell us your email.</span>
    <span ng-show="form.uEmail.$error.email">This is not a valid email.</span>
</div>

음, 구체적으로.Angular를 사용하여 클라이언트에 표시할 속성(데이터 주석)을 해결하기 위해 도우미 또는 "데이터 주석"이 필요합니다.JS.

아직 존재하지 않는 경우, Razor For Angular와 같이 해야 할 때일 수 있습니다.JS

편집

ASP를 사용하는 가장 좋은 방법이라고 생각합니다. MVC한다(JS).front-end수작업으로

ASP를 만든 사람으로서요Net/Angular 웹 사이트에서는 HTML을 렌더링하기 위해 Razor를 사용하는 것을 그만두는 것이 훨씬 낫다고 말할 수 있습니다.

프로젝트에서는 메인 페이지를 렌더링하기 위해 면도기 뷰를 1개 설정했습니다(각도로 작성된 단일 페이지 앱을 사용하고 있습니다).그 후, Straight .html 파일의 폴더를 가지고 있어, 각도의 템플릿으로 사용하고 있습니다.

나머지는 ASP로 처리됩니다.내 경우 Net Web API 호출이지만 MVC 액션을 JSON 결과와 함께 사용할 수도 있습니다.

이 아키텍처로 바꾸자마자, 개발의 지혜로움이 많은 저에게 있어서, 일은 훨씬 더 순조롭게 진행되었습니다.

면도기에서 손을 떼는 것에 대한 블레쉬 아이디어에 동의하지만, 페이지를 보다 빠르게 작성할 수 있는 도구를 만들 수 있습니다.IMHO는 공구 세트에서 면도기를 제거하는 대신 필요한 부분에 면도기 기능을 사용하는 것이 좋습니다.

그나저나 ngval을 보세요.데이터 주석을 angularjs 검증자로 클라이언트 측에 가져옵니다.html 도우미와 각진 모듈이 있습니다.그 프로젝트는 초기 개발 단계에 있다는 것을 말씀드려야겠습니다.

MVC에서 AngularJs로의 이행을 원활하게 하기 위한 지령을 작성했습니다.마크업은 다음과 같습니다.

<validated-input name="username" display="User Name" ng-model="model.username" required>

이는 필드를 수정할 때까지 유효성 검사를 지연하는 등 레이저 관례와 동일하게 작동합니다.시간이 지남에 따라 마크업을 유지하는 것이 직관적이고 간단하다는 것을 알게 되었습니다.

그 주제에 대한 나의 기사

플린커

내 생각에 네가 원하는 것을 할 수 있는 방법은 아마 여섯 가지가 있을 거예요.가장 쉬운 방법은 jquery.validation 마크업을 인식하는 Angular 지시어를 사용하는 것입니다.

다음은 이러한 프로젝트입니다.https://github.com/mdekrey/unobtrusive-angular-validation

여기 또 있습니다.https://github.com/danicomas/angular-jquery-validate

저는 개인적으로 jquery.validation.unoblusive 속성 대신 MVC 출력 각도 검증 속성을 만드는 코드를 작성하여 이 문제를 해결했습니다.

세 번째 옵션은 서버 측 검증에만 의존하는 것입니다.이 방법은 분명히 더 느리지만 더 복잡한 검증 시나리오에서는 이 방법이 유일한 옵션일 수 있습니다.이 경우 웹 API 컨트롤러가 보통 반환하는 ModelStateDictionary 객체를 해석하기 위해 javascript를 작성하기만 하면 됩니다.이를 Angular에 통합하는 방법에 대한 몇 가지 예가 있습니다.JS의 네이티브 검증 모델.

ModelStateDictionary를 해석하기 위한 불완전한 코드를 다음에 나타냅니다.

````

angular.module('app')
    .directive('joshServerValidate', ['$http', function ($http) {
        return {
            require: 'ngModel',
            link: function (scope, ele, attrs, c) {
                console.info('wiring up ' + attrs.ngModel + ' to controller ' + c.$name);
                scope.$watch('modelState', function () {
                    if (scope.modelState == null) return;
                    var modelStateKey = attrs.joshServerValidate || attrs.ngModel;
                    modelStateKey = modelStateKey.replace(attrs.joshServerValidatePrefix, '');
                    modelStateKey = modelStateKey.replace('$index', scope.$index);
                    modelStateKey = modelStateKey.replace('model.', '');
                    console.info('validation for ' + modelStateKey);
                    if (scope.modelState[modelStateKey]) {
                        c.$setValidity('server', false);
                        c.$error.server = scope.modelState[modelStateKey];
                    } else {
                        c.$setValidity('server', true);
                    }
                });
            }
        };
    }]);

````

여기에 나와 있는 다른 답변에 다소 실망했습니다.이메일 주소보다 조금 더 어려운 것을 검증하려고 할 때 "Don't do it"은 그다지 좋은 제안이 아닙니다.

나는 이것을 조금 다른 방법으로 해결했다.Json 시리얼라이저 면도 템플릿을 뷰 위치에 삽입하여 검색하는 커스텀 뷰 엔진과 필터를 통해 애플리케이션/json 콘텐츠 유형에 대응하도록 MVC 애플리케이션을 수정했습니다.

이는 동일한 컨트롤러/액션에 대해 jQuery UI, Bootstrap 및 Json 응답을 사용하여 당사 웹 사이트를 스킨링할 수 있도록 하기 위해 수행되었습니다.

다음은 json 결과의 예입니다.

{
  "sid": "33b336e5-733a-435d-ad11-a79fdc1e25df",
  "form": {
    "id": 293021,
    "disableValidation": false,
    "phone": null,
    "zipCode": "60610",
    "firstName": null,
    "lastName": null,
    "address": null,
    "unit": null,
    "state": "IL",
    "email": null,
    "yearsAtAddress": null,
    "monthsAtAddress": null,
    "howHeard": null
  },
  "errors": [
    "The first name is required",
    "The last name is required",
    "Please enter a phone number",
    "Please enter an email address"
  ],
  "viewdata": {
    "cities": [
      {
        "selected": false,
        "text": "CHICAGO",
        "value": "CHICAGO"
      }
    ],
    "counties": [
      {
        "selected": false,
        "text": "COOK"
      }
    ]
  }
}

필터는 리다이렉트 결과를 json 객체로 변환하기 위해 사용되며, json 객체는 다음 URL을 호출 프로그램에 전달합니다.

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);

        // if the request was application.json and the response is not json, return the current data session.
        if (filterContext.HttpContext.Request.ContentType.StartsWith("application/json") && 
            !(filterContext.Result is JsonResult || filterContext.Result is ContentResult))
        {
            if (!(filterContext.Controller is BaseController controller)) return;

            string url = filterContext.HttpContext.Request.RawUrl ?? "";
            if (filterContext.Result is RedirectResult redirectResult)
            {
                // It was a RedirectResult => we need to calculate the url
                url = UrlHelper.GenerateContentUrl(redirectResult.Url, filterContext.HttpContext);
            }
            else if (filterContext.Result is RedirectToRouteResult routeResult)
            {
                // It was a RedirectToRouteResult => we need to calculate
                // the target url
                url = UrlHelper.GenerateUrl(routeResult.RouteName, null, null, routeResult.RouteValues, RouteTable.Routes,
                    filterContext.RequestContext, false);
            }
            else
            {
                return;
            }
            var absolute = url;
            var currentUri = filterContext.HttpContext.Request.Url;
            if (url != null && currentUri != null && url.StartsWith("/"))
            {
                absolute = currentUri.Scheme + "://" + currentUri.Host + url;
            }

            var data = new {
                nextUrl =  absolute,
                uid = controller.UniqueSessionId(),
                errors = GetFlashMessage(filterContext.HttpContext.Session)
            };

            var settings = new JsonSerializerSettings
            {
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
                PreserveReferencesHandling = PreserveReferencesHandling.Objects,
                Formatting = Formatting.Indented,
                NullValueHandling = NullValueHandling.Ignore
            };
            filterContext.Result = new ContentResult
            {
                ContentType = "application/json",
                Content = JsonConvert.SerializeObject(data,settings)
            };
        }

뷰\Json\Serializer.cshml. 코드베이스의 간결성과 보안을 위해 제외된 스테이트먼트를 사용합니다.이렇게 하면 응답 반환이 3회 시도됩니다.첫 번째 방법은 원본 View{controller}{action.cshtml을 읽고 HTML 도우미를 구문 분석하여 폼과 필드에 배치하는 것입니다.두 번째 시도에서는 기본 제공 블로그 시스템(아래의 포스트 콘텐츠)에서 검색하여 해당 모델을 사용할 수 없는 요소를 찾습니다.

@model dynamic
@{
    Response.ContentType = "application/json";

    Layout = "";
    var session = new Object(); // removed for security purposes

    var messages = ViewBag.Messages as List<string>() ?? new List<string>();
    var className = "";
    if (!ViewData.ModelState.IsValid)
    {
        messages.AddRange(ViewData.ModelState.Values.SelectMany(val => val.Errors).Select(error => error.ErrorMessage));
    }


    dynamic result;
    string serial;

    try
    {
        Type tModel = Model == null ? typeof(Object) : Model.GetType();
        dynamic form = new ExpandoObject();
        dynamic fields = new ExpandoObject();

        var controller = ViewContext.RouteData.Values["controller"] as string ?? "";
        var action = ViewContext.RouteData.Values["action"] as string;

        var viewPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Views", controller, action + ".cshtml");
        if (File.Exists(viewPath))
        {
            string contents = File.ReadAllText(viewPath);
            var extracted = false;
            var patterns = new[]
            {
                @"@Html\.\w+For\(\w+ => \w+\.(.*?)[,\)]",
                @"@Html\.(\w+)For\(\w+ => \w+\.([\w\.]+)[, ]*(\(SelectList\))*(ViewBag\.\w+)*[^\)]*",
                "name=\"(.*?)\""
            };

            for (var i = 0; i < 3 && !extracted; i++)
            {
                switch (i)
                {
                    case 0:
                        form = contents.ExtractFields(patterns[0], Model as object, out extracted);
                        fields = contents.ExtractElements(patterns[1], Model as object, out extracted, ViewData);
                        break;
                    case 1:
                        form = Model as mvcApp.Models.Blog == null ? null : (Model.PostContent as string).ExtractFields(patterns[2], Model as object, out extracted);
                        break;
                    default:
                        form = Model;
                        break;
                }
            }
        }
        else if (Model == null)
        {
            // nothing to do here - safeModel will serialize to an empty object
        }
        else if (Model is IEnumerable)
        {
            form = new List<object>();

            foreach (var element in ((IEnumerable) Model).AsQueryable()
                    .Cast<dynamic>())
            {
                form.Add(CustomExtensions.SafeClone(element));
            }

        } else {
            form = Activator.CreateInstance(tModel);
            CustomExtensions.CloneMatching(form, Model);
        }

        // remove any data models from the viewbag to prevent
        // recursive serialization
        foreach (var key in ViewData.Keys.ToArray())
        {
            var value = ViewData[key];
            if (value is IEnumerable)
            {
                var enumerator = (value as IEnumerable).GetEnumerator();
                value = enumerator.MoveNext() ? enumerator.Current : null;
            }
            if (value != null)
            {
                var vtype = value.GetType();
                if (vtype.Namespace != null && (vtype.Namespace == "System.Data.Entity.DynamicProxies" || vtype.Namespace.EndsWith("Models")))
                {
                    ViewData[key] = null;
                }
            }
        }

        result = new
        {
            uid = session.UniqueId,
            form,
            fields,
            errors = messages.Count == 0 ? null : messages,
            viewdata = ViewBag
        };
        var setting = new JsonSerializerSettings
        {
            PreserveReferencesHandling = PreserveReferencesHandling.None,
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            ContractResolver = new CamelCasePropertyNamesContractResolver(),
            Formatting = Formatting.Indented
        };
        if (form is IEnumerable)
        {
            setting.NullValueHandling = NullValueHandling.Ignore;
        }
        serial = JsonConvert.SerializeObject(result, setting);
    }
    catch (Exception e)
    {
        result = new {
            uid = session.UniqueId,
            error = e.Message.Split('|')
        };
        serial = JsonConvert.SerializeObject(result);
    }
    @Html.Raw(serial)
}

복제 방법은 서로 다른 개체의 속성을 복제하는 가장 좋은 방법을 참조하십시오.

    public static dynamic ExtractFields(this string html, string pattern, object model, out bool extracted)
    {
        if (html == null || model == null)
        {
            extracted = false;
            return null;
        }
        dynamic safeModel = new ExpandoObject();
        var safeDict = (IDictionary<string, Object>)safeModel;

        var matches = new Regex(pattern).Matches(html);
        extracted = matches.Count > 0;

        if ( extracted )
        {
            foreach (Match match in matches)
            {
                var name = match.Groups[1].Value;
                var value = CustomExtensions.ValueForKey(model, name);
                var segments = name.Split('.');
                var obj = safeDict;
                for (var i = 0; i < segments.Length; i++)
                {
                    name = segments[i];
                    if (i == segments.Length - 1)
                    {
                        if (obj.ContainsKey(name))
                        {
                            obj[name] = value;
                        }
                        else
                        {
                            obj.Add(name, value);
                        }
                        continue;
                    }
                    if (!obj.ContainsKey(name))
                    {
                        obj.Add(name, new ExpandoObject());
                    }
                    obj = (IDictionary<string, Object>)obj[name];
                }
            }
        }
        return safeModel;
    }

다음은 자산 체인을 보다 쉽게 다루기 위한 주요 가치 코딩의 구현입니다.

/// <summary>
/// This borrows KeyValueCoding from Objective-C and makes working with long chains of properties more convenient. 
/// KeyValueCoding is null tolerant, and will stop if any element in the chain returns null instead of throwing a NullReferenceException. 
/// Additionally, the following Linq methods are supported: First, Last, Sum &amp; Average.
/// <br/>
/// KeyValueCoding flattens nested enumerable types, but will only aggregate the last element: "children.grandchildren.first" will return 
/// the first grandchild for each child. If you want to return a single grandchild, use "first.children.grandchildren". The same applies to
/// Sum and Average.
/// </summary>
/// <param name="source">any object</param>
/// <param name="keyPath">the path to a descendant property or method "child.grandchild.greatgrandchild".</param>
/// <param name="throwErrors">optional - defaults to supressing errors</param>
/// <returns>returns the specified descendant. If intermediate properties are IEnumerable (Lists, Arrays, Collections), the result *should be* IEnumerable</returns>
public static object ValueForKey(this object source, string keyPath, bool throwErrors = false)
{
    try
    {
        while (true)
        {
            if (source == null || keyPath == null) return null;
            if (keyPath == "") return source;

            var segments = keyPath.Split('.');
            var type = source.GetType();
            var first = segments.First();
            var property = type.GetProperty(first);
            object value = null;
            if (property == null)
            {
                var method = type.GetMethod(first);
                if (method != null)
                {
                    value = method.Invoke(source, null);
                }
            }
            else
            {
                value = property.GetValue(source, null);
            }

            if (segments.Length == 1) return value;


            var children = string.Join(".", segments.Skip(1));
            if (value is IEnumerable || "First|Last|Sum|Average".IndexOf(first, StringComparison.OrdinalIgnoreCase) > -1)
            {
                var firstChild = children.Split('.').First();
                var grandchildren = string.Join(".", children.Split('.').Skip(1));
                if (value == null) {
                    var childValue = source.ValueForKey(children);
                    value = childValue as IEnumerable<object>;
                    switch (first.Proper())
                    {
                        case "First":
                            return value == null ? childValue : ((IEnumerable<object>)value).FirstOrDefault();
                        case "Last":
                            return value == null ? childValue : ((IEnumerable<object>)value).LastOrDefault();
                        case "Count":
                            return value == null ? (childValue == null ? 0 : 1) : (int?)((IEnumerable<object>)value).Count();
                        case "Sum":
                            return value == null
                                ? Convert.ToDecimal(childValue ?? "0")
                                : ((IEnumerable<object>) value).Sum(obj => Convert.ToDecimal(obj ?? "0"));
                        case "Average":
                            return value == null
                                ? Convert.ToDecimal(childValue ?? "0")
                                : ((IEnumerable<object>) value).Average(obj => Convert.ToDecimal(obj ?? "0"));
                    }
                } else {
                    switch (firstChild.Proper())
                    {
                        case "First":
                            return ((IEnumerable<object>)value).FirstOrDefault().ValueForKey(grandchildren);
                        case "Last":
                            return ((IEnumerable<object>)value).LastOrDefault().ValueForKey(grandchildren);
                        case "Count":
                            if (!string.IsNullOrWhiteSpace(grandchildren))
                            {
                                value = value.ValueForKey(grandchildren);
                                if (value != null && ! (value is IEnumerable<object>))
                                {
                                    return 1;
                                }
                            }
                            return value == null ? 0 : ((IEnumerable<object>)value).Count();
                        case "Sum":
                            return ((IEnumerable<object>)value).Sum(obj => Convert.ToDecimal(obj.ValueForKey(grandchildren)??"0"));
                        case "Average":
                            return ((IEnumerable<object>)value).Average(obj => Convert.ToDecimal(obj.ValueForKey(grandchildren) ?? "0"));
                    }
                }
                if (value == null) return null;
                var flat = new List<object>();
                foreach (var element in (IEnumerable<object>)value)
                {
                    var child = element.ValueForKey(children);
                    if (child == null)
                    {
                        continue;
                    }
                    if (child is IEnumerable && !(child is string))
                    {
                        flat.AddRange((IEnumerable<object>) child);
                    }
                    else
                    {
                        flat.Add(child);
                    }
                }
                return flat.Count == 0? null: flat;
            }
            source = value;
            keyPath = children;
        }
    }
    catch (Exception)
    {
        if (throwErrors) throw;
    }
    return null;
}

언급URL : https://stackoverflow.com/questions/15046817/asp-net-mvc-validation-form-with-angularjs

반응형