前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ASP.NET Core分布式项目实战(Consent 代码重构)--学习笔记

ASP.NET Core分布式项目实战(Consent 代码重构)--学习笔记

作者头像
郑子铭
发布2021-01-13 15:39:19
2630
发布2021-01-13 15:39:19
举报

任务23:Consent 代码重构

新建一个 Sercices 文件夹,在文件夹下新建一个 ConsentService,专门用于处理 Consent 的逻辑,我们会把 controller 中不是 action 的方法移到 service 中

先将 ConsentController 私有变量和构造函数搬到 ConsentService 中

ConsentService
代码语言:javascript
复制
private readonly IClientStore _clientSotre;
private readonly IResourceStore _resourceStore;
private readonly IIdentityServerInteractionService _identityServerInteractionService;

public ConsentService(
    IClientStore clientStore,
    IResourceStore resourceStore,
    IIdentityServerInteractionService identityServerInteractionService)
{
    _clientSotre = clientStore;
    _resourceStore = resourceStore;
    _identityServerInteractionService = identityServerInteractionService;
}

接着搬移私有方法,并将 BuildConsentViewModel 修改为 public

ConsentService
代码语言:javascript
复制
#region Private Methods

private ConsentViewModel CreateConsentViewModel(AuthorizationRequest request, Client client, Resources resources)
{
    var vm = new ConsentViewModel();
    vm.ClientName = client.ClientName;
    vm.ClientLogoUrl = client.LogoUri;
    vm.ClientUrl = client.ClientUri;
    vm.RememberConsent = client.AllowRememberConsent;
    vm.IdentityScopes = resources.IdentityResources.Select(i => CreateScopeViewModel(i));
    vm.ResourceScopes = resources.ApiResources.SelectMany(i => i.Scopes).Select(x => CreateScopeViewModel(x));

    return vm;
}

private ScopeViewModel CreateScopeViewModel(IdentityResource identityResource)
{
    return new ScopeViewModel
    {
        Name = identityResource.Name,
        DisplayName = identityResource.DisplayName,
        Description = identityResource.Description,
        Required = identityResource.Required,
        Checked = identityResource.Required,
        Emphasize = identityResource.Emphasize,
    };
}

private ScopeViewModel CreateScopeViewModel(Scope scope)
{
    return new ScopeViewModel
    {
        Name = scope.Name,
        DisplayName = scope.DisplayName,
        Description = scope.Description,
        Required = scope.Required,
        Checked = scope.Required,
        Emphasize = scope.Emphasize,
    };
}

#endregion

public async Task<ConsentViewModel> BuildConsentViewModel(string returnUrl)
{
    var request = await _identityServerInteractionService.GetAuthorizationContextAsync(returnUrl);
    if (request == null)
        return null;

    var client = await _clientSotre.FindEnabledClientByIdAsync(request.ClientId);
    var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested);

    var vm = CreateConsentViewModel(request, client, resources);
    vm.ReturnUrl = returnUrl;
    return vm;
}

接着将 ConsentService 注入到 ConsentController 中并调用 BuildConsentViewModel

ConsentController
代码语言:javascript
复制
private readonly ConsentService _consentService;
public ConsentController(ConsentService consentService)
{
    _consentService = consentService;
}

[HttpGet]
public async Task<IActionResult> Index(string returnUrl)
{
    var model = await _consentService.BuildConsentViewModel(returnUrl);

    if (model == null)
    {

    }

    return View(model);
}

接着将 ConsentController 中 post 的逻辑搬到 ConsentService 的一个方法 ProcessConsent 中

这里不能直接调用 Redirect 所以需要一个新建一个ViewModel 作为返回

ProcessConsentResult
代码语言:javascript
复制
public class ProcessConsentResult
{
    public string RedirectUrl { get; set; }
    public bool IsRedirect => RedirectUrl != null;
}
ConsentService
代码语言:javascript
复制
public async Task<ProcessConsentResult> ProcessConsent(InputConsentViewModel viewModel)
{
    ConsentResponse consentResponse = null;
    var result = new ProcessConsentResult();

    if (viewModel.Button == "no")
    {
        consentResponse = ConsentResponse.Denied;
    }
    else if (viewModel.Button == "yes")
    {
        if (viewModel.ScopesConsented != null && viewModel.ScopesConsented.Any())
        {
            consentResponse = new ConsentResponse
            {
                RememberConsent = viewModel.RememberConsent,
                ScopesConsented = viewModel.ScopesConsented,
            };
        }
    }

    if (consentResponse != null)
    {
        var request = await _identityServerInteractionService.GetAuthorizationContextAsync(viewModel.ReturnUrl);
        await _identityServerInteractionService.GrantConsentAsync(request, consentResponse);

        result.RedirectUrl = viewModel.ReturnUrl;
    }

    return result;
}

接着在 ConsentController 的 post 逻辑中调用 ProcessConsent

ConsentController
代码语言:javascript
复制
[HttpPost]
public async Task<IActionResult> Index(InputConsentViewModel viewModel)
{
    var result = await _consentService.ProcessConsent(viewModel);
    if (result.IsRedirect)
    {
        return Redirect(result.RedirectUrl);
    }

    return View(viewModel);
}

因为在视图层 index 中使用的是 ConsentViewModel,不能直接把 InputConsentViewModel 传过去,因为是无法识别的,所以我们需要在 ConsentService 中转换一下

首先在 ProcessConsentResult 中添加一个 ConsentViewModel

ProcessConsentResult
代码语言:javascript
复制
public class ProcessConsentResult
{
    public string RedirectUrl { get; set; }
    public bool IsRedirect => RedirectUrl != null;
    public ConsentViewModel viewModel { get; set; }
}

在什么情况下会返回这个 ViewModel,当 ConsentService 的 ProcessConsent 方法中的 consentResponse 为 null 的时候,在这个时候我们需要给它封装一个 model

ConsentService
代码语言:javascript
复制
if (consentResponse != null)
{
...
}
{
    var consentViewModel = await BuildConsentViewModel(viewModel.ReturnUrl);
    result.viewModel = consentViewModel;
}

但是在 BuildConsentViewModel 的时候,ConsentViewModel 的 ScopeViewModel 里面有 Required 和 Checked,如果在填写的时候已经勾选了,我们需要把它的状态带过去,而在 viewModel.ScopesConsented 的时候已经知道勾选了哪些,所以我们需要把 model 传过去

ConsentService
代码语言:javascript
复制
var consentViewModel = await BuildConsentViewModel(viewModel.ReturnUrl, viewModel);

改造一下 BuildConsentViewModel,接收一个 InputConsentViewModel,默认为 null,如有它有值,可以知道客户的选中信息,然后传入 CreateConsentViewModel 中

ConsentService
代码语言:javascript
复制
public async Task<ConsentViewModel> BuildConsentViewModel(string returnUrl, InputConsentViewModel model = null)
{
    ...
    var vm = CreateConsentViewModel(request, client, resources, model);
    ...
}

所以在 CreateConsentViewModel 的时候对 Checked 赋值,或者已经选中的情况下就选中

ConsentService
代码语言:javascript
复制
private ScopeViewModel CreateScopeViewModel(IdentityResource identityResource, bool check)
{
    return new ScopeViewModel
    {
        Name = identityResource.Name,
        DisplayName = identityResource.DisplayName,
        Description = identityResource.Description,
        Required = identityResource.Required,
        Checked = check || identityResource.Required,
        Emphasize = identityResource.Emphasize,
    };
}

private ScopeViewModel CreateScopeViewModel(Scope scope, bool check)
{
    return new ScopeViewModel
    {
        Name = scope.Name,
        DisplayName = scope.DisplayName,
        Description = scope.Description,
        Required = scope.Required,
        Checked = check ||scope.Required,
        Emphasize = scope.Emphasize,
    };
}

接着处理一下 CreateConsentViewModel,传入一个 InputConsentViewModel,然后修改 RememberConsent,以及获取一个 selectedScopes

ConsentService
代码语言:javascript
复制
private ConsentViewModel CreateConsentViewModel(AuthorizationRequest request, Client client, Resources resources, InputConsentViewModel model)
{
    var rememberConsent = model?.RememberConsent ?? true;
    var selectedScopes = model?.ScopesConsented ?? Enumerable.Empty<string>();

    var vm = new ConsentViewModel();
    vm.ClientName = client.ClientName;
    vm.ClientLogoUrl = client.LogoUri;
    vm.ClientUrl = client.ClientUri;
    vm.RememberConsent = rememberConsent;
    vm.IdentityScopes = resources.IdentityResources.Select(i => CreateScopeViewModel(i, selectedScopes.Contains(i.Name) || model == null));
    vm.ResourceScopes = resources.ApiResources.SelectMany(i => i.Scopes).Select(x => CreateScopeViewModel(x, selectedScopes.Contains(x.Name) || model == null));

    return vm;
}

这一块就改进完了,接下来就是在不选中的情况下会有提示让我们选择,现在添加一些错误的提示,在 ProcessConsentResult 中添加一些信息

ProcessConsentResult
代码语言:javascript
复制
public string ValidationError { get; set; }

赋值 ValidationError

ConsentService
代码语言:javascript
复制
else if (viewModel.Button == "yes")
{
    if (viewModel.ScopesConsented != null && viewModel.ScopesConsented.Any())
    {
        consentResponse = new ConsentResponse
        {
            RememberConsent = viewModel.RememberConsent,
            ScopesConsented = viewModel.ScopesConsented,
        };
    }

    result.ValidationError = "请至少选中一个权限";
}

接着处理一下页面,将信息返回

ConsentController
代码语言:javascript
复制
[HttpPost]
public async Task<IActionResult> Index(InputConsentViewModel viewModel)
{
    var result = await _consentService.ProcessConsent(viewModel);
    if (result.IsRedirect)
    {
        return Redirect(result.RedirectUrl);
    }

    if (!string.IsNullOrEmpty(result.ValidationError))
    {
        ModelState.AddModelError("", result.ValidationError);
    }

    return View(result.viewModel);
}

加入验证信息之后需要修改视图把这块信息显示出来

Index
代码语言:javascript
复制
<input type="hidden" asp-for="ReturnUrl"/>

    <div class="alert alert-danger">
        <strong>Error""</strong>
        <div asp-validation-summary="All" class="danger"></div>
    </div>

添加依赖注入

startup
代码语言:javascript
复制
services.AddScoped<ConsentService>();

启动服务端,启动客户端

因为默认勾选第一个,无法看到错误信息,所以去掉 disabled 与 hidden 控件,两个选项都不勾选,点击同意就会看到错误信息

_ScopeListitem
代码语言:javascript
复制
disabled="@Model.Required"

@if (Model.Required)
{
    <input type="hidden" name="ScopesConsented" value="@Model.Name"/>
}

课程链接

http://video.jessetalk.cn/course/explore

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-06-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DotNet NB 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 任务23:Consent 代码重构
    • ConsentService
      • ConsentService
        • ConsentController
          • ProcessConsentResult
            • ConsentService
              • ConsentController
                • ProcessConsentResult
                  • ConsentService
                    • ConsentService
                      • ConsentService
                        • ConsentService
                          • ConsentService
                            • ProcessConsentResult
                              • ConsentService
                                • ConsentController
                                  • Index
                                    • startup
                                      • _ScopeListitem
                                      • 课程链接
                                      领券
                                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档