GetClientAsync() {
20 | var state = await AuthenticationStateTask;
21 | if(state.User is ClaimsPrincipal principal && principal.Identity.IsAuthenticated){
22 | Client.BearerToken = principal.Claims.Where(c => c.Type == "token").FirstOrDefault()?.Value;
23 | }
24 | return Client;
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/MyApp.Client/MyApp.Client.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net6.0
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/MyApp.Client/NavigationManagerExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Specialized;
3 | using System.Web;
4 | using Microsoft.AspNetCore.Components;
5 |
6 | namespace MyApp.Client {
7 |
8 | //https://jasonwatmore.com/post/2020/08/09/blazor-webassembly-get-query-string-parameters-with-navigation-manager
9 | public static class NavigationManagerExtensions
10 | {
11 | public static NameValueCollection QueryString(this NavigationManager navigationManager)
12 | {
13 | string query = new Uri(navigationManager.Uri).Query;
14 | return HttpUtility.ParseQueryString(query);
15 | }
16 |
17 | public static string QueryString(this NavigationManager navigationManager, string key)
18 | {
19 | return navigationManager.QueryString()[key];
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/MyApp.Client/Pages/CallHello.razor:
--------------------------------------------------------------------------------
1 | @page "/callhello"
2 | @attribute [Authorize()]
3 | @inherits StackBaseComponent
4 | @using System.Collections.Generic
5 |
6 | Call Hello
7 |
8 | Protected Service Response: @Response
9 |
10 |
11 | Call Hello
12 |
13 | @code {
14 | private string Response {get;set;}
15 |
16 |
17 | private async Task CallHelloService()
18 | {
19 |
20 | var client = await GetClientAsync();
21 | var response = await client.GetAsync(new ServiceModel.Hello() {
22 | Name = "World"
23 | });
24 | Response = response.Result;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/MyApp.Client/Pages/Counter.razor:
--------------------------------------------------------------------------------
1 | @page "/counter"
2 | @attribute [Authorize()]
3 | @inherits StackBaseComponent
4 | @using System.Collections.Generic
5 |
6 | Counter
7 |
8 | Current count: @currentCount
9 |
10 | Click me
11 |
12 | @code {
13 | private int currentCount = 0;
14 |
15 | private async Task IncrementCount()
16 | {
17 | currentCount++;
18 |
19 | var client = await GetClientAsync();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/MyApp.Client/Pages/FetchData.razor:
--------------------------------------------------------------------------------
1 | @page "/fetchdata"
2 | @inject HttpClient Http
3 |
4 | Weather forecast
5 |
6 | This component demonstrates fetching data from the server.
7 |
8 | @if (forecasts == null)
9 | {
10 | Loading...
11 | }
12 | else
13 | {
14 |
15 |
16 |
17 | Date
18 | Temp. (C)
19 | Temp. (F)
20 | Summary
21 |
22 |
23 |
24 | @foreach (var forecast in forecasts)
25 | {
26 |
27 | @forecast.Date.ToShortDateString()
28 | @forecast.TemperatureC
29 | @forecast.TemperatureF
30 | @forecast.Summary
31 |
32 | }
33 |
34 |
35 | }
36 |
37 | @code {
38 | private WeatherForecast[] forecasts;
39 |
40 | protected override async Task OnInitializedAsync()
41 | {
42 | forecasts = await Http.GetFromJsonAsync("sample-data/weather.json");
43 | }
44 |
45 | public class WeatherForecast
46 | {
47 | public DateTime Date { get; set; }
48 |
49 | public int TemperatureC { get; set; }
50 |
51 | public string Summary { get; set; }
52 |
53 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/MyApp.Client/Pages/Index.razor:
--------------------------------------------------------------------------------
1 | @page "/"
2 |
3 | Hello, world!
4 |
5 | Welcome to your new app.
6 |
7 |
8 |
--------------------------------------------------------------------------------
/MyApp.Client/Pages/Login.razor:
--------------------------------------------------------------------------------
1 | @page "/login"
2 | @inject ServiceStackStateProvider provider
3 | @inject ILogger logger;
4 | @inject NavigationManager NavigationManager;
5 |
6 |
33 |
34 |
35 | @code {
36 | private string Email { get; set; }
37 | private string Password { get; set; }
38 |
39 | private string EmailError { get; set; }
40 |
41 | private string PasswordError { get; set; }
42 |
43 | private string AuthenticationError { get; set; }
44 |
45 | private string FormClass { get; set; }
46 |
47 | private async Task LoginAsync()
48 | {
49 | try
50 | {
51 | var emailEmpty = Email.IsNullOrEmpty();
52 | var passwordEmpty = Password.IsNullOrEmpty();
53 | if (emailEmpty)
54 | {
55 | EmailError = "Email is required";
56 | }
57 | if (passwordEmpty)
58 | {
59 | PasswordError = "Password is required";
60 | }
61 | FormClass = "was-validated";
62 | if (emailEmpty || passwordEmpty) return;
63 | await provider.Login(Email, Password);
64 | Console.WriteLine("returnUrl", NavigationManager.QueryString("return"));
65 | if (NavigationManager.QueryString("return") is string returnUrl)
66 | {
67 | returnUrl = returnUrl.IsNullOrEmpty() ? "/" : returnUrl;
68 | NavigationManager.NavigateTo(returnUrl, true);
69 | }
70 | }
71 | catch (HttpRequestException ex)
72 | {
73 | logger.LogError(ex.ToString());
74 | if (ex.StatusCode is HttpStatusCode code && code == HttpStatusCode.Unauthorized)
75 | {
76 | AuthenticationError = "Not Authorized wrong credentials.";
77 | }
78 | }
79 | catch (Exception ex)
80 | {
81 | AuthenticationError = "Service error";
82 | Console.WriteLine("Exception Type :", ex.GetType().Name);
83 | logger.LogDebug(ex, "login");
84 | }
85 | }
86 | }
--------------------------------------------------------------------------------
/MyApp.Client/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Http;
3 | using System.Collections.Generic;
4 | using System.Threading.Tasks;
5 | using System.Text;
6 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
7 | using Microsoft.Extensions.Configuration;
8 | using Microsoft.Extensions.DependencyInjection;
9 | using Microsoft.Extensions.Logging;
10 | using Microsoft.AspNetCore.Components.Authorization;
11 | using ServiceStack;
12 | using Blazored.LocalStorage;
13 | using Blazor.Extensions.Logging;
14 |
15 | namespace MyApp.Client
16 | {
17 | public class Program
18 | {
19 | public static async Task Main(string[] args)
20 | {
21 | var builder = WebAssemblyHostBuilder.CreateDefault(args);
22 | builder.Services.AddLogging(builder => builder
23 | .AddBrowserConsole()
24 | .SetMinimumLevel(LogLevel.Trace)
25 | );
26 | builder.RootComponents.Add("#app");
27 | builder.Services.AddOptions();
28 | builder.Services.AddAuthorizationCore();
29 |
30 | builder.Services.AddBlazoredLocalStorage(config =>
31 | config.JsonSerializerOptions.WriteIndented = true);
32 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
33 | builder.Services.AddScoped(s => s.GetRequiredService());
34 |
35 | builder.Services.AddScoped(_ => BlazorClient.Create(builder.HostEnvironment.BaseAddress));
36 | builder.Services.AddScoped();
37 |
38 |
39 | await builder.Build().RunAsync();
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/MyApp.Client/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:56811",
7 | "sslPort": 44344
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
15 | "environmentVariables": {
16 | "ASPNETCORE_ENVIRONMENT": "Development"
17 | }
18 | },
19 | "MyApp": {
20 | "commandName": "Project",
21 | "dotnetRunMessages": "true",
22 | "launchBrowser": true,
23 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
24 | "applicationUrl": "https://localhost:5001;http://localhost:5000",
25 | "environmentVariables": {
26 | "ASPNETCORE_ENVIRONMENT": "Development"
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/MyApp.Client/Shared/MainLayout.razor:
--------------------------------------------------------------------------------
1 | @inherits LayoutComponentBase
2 |
3 |
4 |
5 |
8 |
9 |
10 |
13 |
14 | @Body
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/MyApp.Client/Shared/MainLayout.razor.css:
--------------------------------------------------------------------------------
1 | .page {
2 | position: relative;
3 | display: flex;
4 | flex-direction: column;
5 | }
6 |
7 | .main {
8 | flex: 1;
9 | }
10 |
11 | .sidebar {
12 | background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
13 | }
14 |
15 | .top-row {
16 | background-color: #f7f7f7;
17 | border-bottom: 1px solid #d6d5d5;
18 | justify-content: flex-end;
19 | height: 3.5rem;
20 | display: flex;
21 | align-items: center;
22 | }
23 |
24 | .top-row ::deep a, .top-row .btn-link {
25 | white-space: nowrap;
26 | margin-left: 1.5rem;
27 | }
28 |
29 | .top-row a:first-child {
30 | overflow: hidden;
31 | text-overflow: ellipsis;
32 | }
33 |
34 | @media (max-width: 767.98px) {
35 | .top-row:not(.auth) {
36 | display: none;
37 | }
38 |
39 | .top-row.auth {
40 | justify-content: space-between;
41 | }
42 |
43 | .top-row a, .top-row .btn-link {
44 | margin-left: 0;
45 | }
46 | }
47 |
48 | @media (min-width: 768px) {
49 | .page {
50 | flex-direction: row;
51 | }
52 |
53 | .sidebar {
54 | width: 250px;
55 | height: 100vh;
56 | position: sticky;
57 | top: 0;
58 | }
59 |
60 | .top-row {
61 | position: sticky;
62 | top: 0;
63 | z-index: 1;
64 | }
65 |
66 | .main > div {
67 | padding-left: 2rem !important;
68 | padding-right: 1.5rem !important;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/MyApp.Client/Shared/NavMenu.razor:
--------------------------------------------------------------------------------
1 | @inject ServiceStackStateProvider AuthStateProvider;
2 | @inject NavigationManager NavigationManager;
3 |
4 |
5 |
MyApp
6 |
7 |
8 |
9 |
10 |
11 |
47 |
48 | @code {
49 | private bool collapseNavMenu = true;
50 |
51 | private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
52 |
53 | private void ToggleNavMenu()
54 | {
55 | collapseNavMenu = !collapseNavMenu;
56 | }
57 |
58 | [CascadingParameter]
59 | private Task authenticationStateTask { get; set; }
60 |
61 | protected bool IsAuthenticated { get; set; }
62 |
63 | private string LoginUrl { get; set; }
64 |
65 |
66 | protected override async Task OnInitializedAsync()
67 | {
68 | LoginUrl = $"/login?return={NavigationManager.ToBaseRelativePath(NavigationManager.Uri)}";
69 |
70 | }
71 |
72 | protected override async Task OnParametersSetAsync()
73 | {
74 | var state = await authenticationStateTask;
75 | IsAuthenticated = state.User.Identity.IsAuthenticated;
76 | }
77 |
78 | private async Task LogoutAsync()
79 | {
80 | await AuthStateProvider.Logout();
81 | }
82 | }
--------------------------------------------------------------------------------
/MyApp.Client/Shared/NavMenu.razor.css:
--------------------------------------------------------------------------------
1 | .navbar-toggler {
2 | background-color: rgba(255, 255, 255, 0.1);
3 | }
4 |
5 | .top-row {
6 | height: 3.5rem;
7 | background-color: rgba(0,0,0,0.4);
8 | }
9 |
10 | .navbar-brand {
11 | font-size: 1.1rem;
12 | }
13 |
14 | .oi {
15 | width: 2rem;
16 | font-size: 1.1rem;
17 | vertical-align: text-top;
18 | top: -2px;
19 | }
20 |
21 | .nav-item {
22 | font-size: 0.9rem;
23 | padding-bottom: 0.5rem;
24 | }
25 |
26 | .nav-item:first-of-type {
27 | padding-top: 1rem;
28 | }
29 |
30 | .nav-item:last-of-type {
31 | padding-bottom: 1rem;
32 | }
33 |
34 | .nav-item ::deep a {
35 | color: #d7d7d7;
36 | border-radius: 4px;
37 | height: 3rem;
38 | display: flex;
39 | align-items: center;
40 | line-height: 3rem;
41 | }
42 |
43 | .nav-item ::deep a.active {
44 | background-color: rgba(255,255,255,0.25);
45 | color: white;
46 | }
47 |
48 | .nav-item ::deep a:hover {
49 | background-color: rgba(255,255,255,0.1);
50 | color: white;
51 | }
52 |
53 | @media (min-width: 768px) {
54 | .navbar-toggler {
55 | display: none;
56 | }
57 |
58 | .collapse {
59 | /* Never collapse the sidebar for wide screens */
60 | display: block;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/MyApp.Client/Shared/SurveyPrompt.razor:
--------------------------------------------------------------------------------
1 |
2 |
3 |
@Title
4 |
5 |
6 | Please take our
7 | brief survey
8 |
9 | and tell us what you think.
10 |
11 |
12 | @code {
13 | // Demonstrates how a parent component can supply parameters
14 | [Parameter]
15 | public string Title { get; set; }
16 | }
17 |
--------------------------------------------------------------------------------
/MyApp.Client/_Imports.razor:
--------------------------------------------------------------------------------
1 | @using System.Net.Http
2 | @using System.Net.Http.Json
3 | @using Microsoft.AspNetCore.Components.Forms
4 | @using Microsoft.AspNetCore.Components.Routing
5 | @using Microsoft.AspNetCore.Components.Web
6 | @using Microsoft.AspNetCore.Components.WebAssembly.Http
7 | @using Microsoft.AspNetCore.Authorization
8 | @using Microsoft.AspNetCore.Components.Authorization
9 | @using Microsoft.JSInterop
10 | @using MyApp.Client
11 | @using MyApp.Client.Shared
12 | @using ServiceStack
13 | @using Microsoft.Extensions.Logging;
14 | @using System.Net;
--------------------------------------------------------------------------------
/MyApp.Client/wwwroot/css/app.css:
--------------------------------------------------------------------------------
1 | @import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
2 |
3 | html, body {
4 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
5 | }
6 |
7 | a, .btn-link {
8 | color: #0366d6;
9 | }
10 |
11 | .btn-primary {
12 | color: #fff;
13 | background-color: #1b6ec2;
14 | border-color: #1861ac;
15 | }
16 |
17 | .content {
18 | padding-top: 1.1rem;
19 | }
20 |
21 | .valid.modified:not([type=checkbox]) {
22 | outline: 1px solid #26b050;
23 | }
24 |
25 | .invalid {
26 | outline: 1px solid red;
27 | }
28 |
29 | .validation-message {
30 | color: red;
31 | }
32 |
33 | #blazor-error-ui {
34 | background: lightyellow;
35 | bottom: 0;
36 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
37 | display: none;
38 | left: 0;
39 | padding: 0.6rem 1.25rem 0.7rem 1.25rem;
40 | position: fixed;
41 | width: 100%;
42 | z-index: 1000;
43 | }
44 |
45 | #blazor-error-ui .dismiss {
46 | cursor: pointer;
47 | position: absolute;
48 | right: 0.75rem;
49 | top: 0.5rem;
50 | }
51 |
--------------------------------------------------------------------------------
/MyApp.Client/wwwroot/css/open-iconic/FONT-LICENSE:
--------------------------------------------------------------------------------
1 | SIL OPEN FONT LICENSE Version 1.1
2 |
3 | Copyright (c) 2014 Waybury
4 |
5 | PREAMBLE
6 | The goals of the Open Font License (OFL) are to stimulate worldwide
7 | development of collaborative font projects, to support the font creation
8 | efforts of academic and linguistic communities, and to provide a free and
9 | open framework in which fonts may be shared and improved in partnership
10 | with others.
11 |
12 | The OFL allows the licensed fonts to be used, studied, modified and
13 | redistributed freely as long as they are not sold by themselves. The
14 | fonts, including any derivative works, can be bundled, embedded,
15 | redistributed and/or sold with any software provided that any reserved
16 | names are not used by derivative works. The fonts and derivatives,
17 | however, cannot be released under any other type of license. The
18 | requirement for fonts to remain under this license does not apply
19 | to any document created using the fonts or their derivatives.
20 |
21 | DEFINITIONS
22 | "Font Software" refers to the set of files released by the Copyright
23 | Holder(s) under this license and clearly marked as such. This may
24 | include source files, build scripts and documentation.
25 |
26 | "Reserved Font Name" refers to any names specified as such after the
27 | copyright statement(s).
28 |
29 | "Original Version" refers to the collection of Font Software components as
30 | distributed by the Copyright Holder(s).
31 |
32 | "Modified Version" refers to any derivative made by adding to, deleting,
33 | or substituting -- in part or in whole -- any of the components of the
34 | Original Version, by changing formats or by porting the Font Software to a
35 | new environment.
36 |
37 | "Author" refers to any designer, engineer, programmer, technical
38 | writer or other person who contributed to the Font Software.
39 |
40 | PERMISSION & CONDITIONS
41 | Permission is hereby granted, free of charge, to any person obtaining
42 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
43 | redistribute, and sell modified and unmodified copies of the Font
44 | Software, subject to the following conditions:
45 |
46 | 1) Neither the Font Software nor any of its individual components,
47 | in Original or Modified Versions, may be sold by itself.
48 |
49 | 2) Original or Modified Versions of the Font Software may be bundled,
50 | redistributed and/or sold with any software, provided that each copy
51 | contains the above copyright notice and this license. These can be
52 | included either as stand-alone text files, human-readable headers or
53 | in the appropriate machine-readable metadata fields within text or
54 | binary files as long as those fields can be easily viewed by the user.
55 |
56 | 3) No Modified Version of the Font Software may use the Reserved Font
57 | Name(s) unless explicit written permission is granted by the corresponding
58 | Copyright Holder. This restriction only applies to the primary font name as
59 | presented to the users.
60 |
61 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
62 | Software shall not be used to promote, endorse or advertise any
63 | Modified Version, except to acknowledge the contribution(s) of the
64 | Copyright Holder(s) and the Author(s) or with their explicit written
65 | permission.
66 |
67 | 5) The Font Software, modified or unmodified, in part or in whole,
68 | must be distributed entirely under this license, and must not be
69 | distributed under any other license. The requirement for fonts to
70 | remain under this license does not apply to any document created
71 | using the Font Software.
72 |
73 | TERMINATION
74 | This license becomes null and void if any of the above conditions are
75 | not met.
76 |
77 | DISCLAIMER
78 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
79 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
80 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
81 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
82 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
83 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
84 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
85 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
86 | OTHER DEALINGS IN THE FONT SOFTWARE.
87 |
--------------------------------------------------------------------------------
/MyApp.Client/wwwroot/css/open-iconic/ICON-LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Waybury
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/MyApp.Client/wwwroot/css/open-iconic/README.md:
--------------------------------------------------------------------------------
1 | [Open Iconic v1.1.1](http://useiconic.com/open)
2 | ===========
3 |
4 | ### Open Iconic is the open source sibling of [Iconic](http://useiconic.com). It is a hyper-legible collection of 223 icons with a tiny footprint—ready to use with Bootstrap and Foundation. [View the collection](http://useiconic.com/open#icons)
5 |
6 |
7 |
8 | ## What's in Open Iconic?
9 |
10 | * 223 icons designed to be legible down to 8 pixels
11 | * Super-light SVG files - 61.8 for the entire set
12 | * SVG sprite—the modern replacement for icon fonts
13 | * Webfont (EOT, OTF, SVG, TTF, WOFF), PNG and WebP formats
14 | * Webfont stylesheets (including versions for Bootstrap and Foundation) in CSS, LESS, SCSS and Stylus formats
15 | * PNG and WebP raster images in 8px, 16px, 24px, 32px, 48px and 64px.
16 |
17 |
18 | ## Getting Started
19 |
20 | #### For code samples and everything else you need to get started with Open Iconic, check out our [Icons](http://useiconic.com/open#icons) and [Reference](http://useiconic.com/open#reference) sections.
21 |
22 | ### General Usage
23 |
24 | #### Using Open Iconic's SVGs
25 |
26 | We like SVGs and we think they're the way to display icons on the web. Since Open Iconic are just basic SVGs, we suggest you display them like you would any other image (don't forget the `alt` attribute).
27 |
28 | ```
29 |
30 | ```
31 |
32 | #### Using Open Iconic's SVG Sprite
33 |
34 | Open Iconic also comes in a SVG sprite which allows you to display all the icons in the set with a single request. It's like an icon font, without being a hack.
35 |
36 | Adding an icon from an SVG sprite is a little different than what you're used to, but it's still a piece of cake. *Tip: To make your icons easily style able, we suggest adding a general class to the* `` *tag and a unique class name for each different icon in the* `` *tag.*
37 |
38 | ```
39 |
40 |
41 |
42 | ```
43 |
44 | Sizing icons only needs basic CSS. All the icons are in a square format, so just set the `` tag with equal width and height dimensions.
45 |
46 | ```
47 | .icon {
48 | width: 16px;
49 | height: 16px;
50 | }
51 | ```
52 |
53 | Coloring icons is even easier. All you need to do is set the `fill` rule on the `` tag.
54 |
55 | ```
56 | .icon-account-login {
57 | fill: #f00;
58 | }
59 | ```
60 |
61 | To learn more about SVG Sprites, read [Chris Coyier's guide](http://css-tricks.com/svg-sprites-use-better-icon-fonts/).
62 |
63 | #### Using Open Iconic's Icon Font...
64 |
65 |
66 | ##### …with Bootstrap
67 |
68 | You can find our Bootstrap stylesheets in `font/css/open-iconic-bootstrap.{css, less, scss, styl}`
69 |
70 |
71 | ```
72 |
73 | ```
74 |
75 |
76 | ```
77 |
78 | ```
79 |
80 | ##### …with Foundation
81 |
82 | You can find our Foundation stylesheets in `font/css/open-iconic-foundation.{css, less, scss, styl}`
83 |
84 | ```
85 |
86 | ```
87 |
88 |
89 | ```
90 |
91 | ```
92 |
93 | ##### …on its own
94 |
95 | You can find our default stylesheets in `font/css/open-iconic.{css, less, scss, styl}`
96 |
97 | ```
98 |
99 | ```
100 |
101 | ```
102 |
103 | ```
104 |
105 |
106 | ## License
107 |
108 | ### Icons
109 |
110 | All code (including SVG markup) is under the [MIT License](http://opensource.org/licenses/MIT).
111 |
112 | ### Fonts
113 |
114 | All fonts are under the [SIL Licensed](http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web).
115 |
--------------------------------------------------------------------------------
/MyApp.Client/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css:
--------------------------------------------------------------------------------
1 | @font-face{font-family:Icons;src:url(../fonts/open-iconic.eot);src:url(../fonts/open-iconic.eot?#iconic-sm) format('embedded-opentype'),url(../fonts/open-iconic.woff) format('woff'),url(../fonts/open-iconic.ttf) format('truetype'),url(../fonts/open-iconic.otf) format('opentype'),url(../fonts/open-iconic.svg#iconic-sm) format('svg');font-weight:400;font-style:normal}.oi{position:relative;top:1px;display:inline-block;speak:none;font-family:Icons;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.oi:empty:before{width:1em;text-align:center;box-sizing:content-box}.oi.oi-align-center:before{text-align:center}.oi.oi-align-left:before{text-align:left}.oi.oi-align-right:before{text-align:right}.oi.oi-flip-horizontal:before{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.oi.oi-flip-vertical:before{-webkit-transform:scale(1,-1);-ms-transform:scale(-1,1);transform:scale(1,-1)}.oi.oi-flip-horizontal-vertical:before{-webkit-transform:scale(-1,-1);-ms-transform:scale(-1,1);transform:scale(-1,-1)}.oi-account-login:before{content:'\e000'}.oi-account-logout:before{content:'\e001'}.oi-action-redo:before{content:'\e002'}.oi-action-undo:before{content:'\e003'}.oi-align-center:before{content:'\e004'}.oi-align-left:before{content:'\e005'}.oi-align-right:before{content:'\e006'}.oi-aperture:before{content:'\e007'}.oi-arrow-bottom:before{content:'\e008'}.oi-arrow-circle-bottom:before{content:'\e009'}.oi-arrow-circle-left:before{content:'\e00a'}.oi-arrow-circle-right:before{content:'\e00b'}.oi-arrow-circle-top:before{content:'\e00c'}.oi-arrow-left:before{content:'\e00d'}.oi-arrow-right:before{content:'\e00e'}.oi-arrow-thick-bottom:before{content:'\e00f'}.oi-arrow-thick-left:before{content:'\e010'}.oi-arrow-thick-right:before{content:'\e011'}.oi-arrow-thick-top:before{content:'\e012'}.oi-arrow-top:before{content:'\e013'}.oi-audio-spectrum:before{content:'\e014'}.oi-audio:before{content:'\e015'}.oi-badge:before{content:'\e016'}.oi-ban:before{content:'\e017'}.oi-bar-chart:before{content:'\e018'}.oi-basket:before{content:'\e019'}.oi-battery-empty:before{content:'\e01a'}.oi-battery-full:before{content:'\e01b'}.oi-beaker:before{content:'\e01c'}.oi-bell:before{content:'\e01d'}.oi-bluetooth:before{content:'\e01e'}.oi-bold:before{content:'\e01f'}.oi-bolt:before{content:'\e020'}.oi-book:before{content:'\e021'}.oi-bookmark:before{content:'\e022'}.oi-box:before{content:'\e023'}.oi-briefcase:before{content:'\e024'}.oi-british-pound:before{content:'\e025'}.oi-browser:before{content:'\e026'}.oi-brush:before{content:'\e027'}.oi-bug:before{content:'\e028'}.oi-bullhorn:before{content:'\e029'}.oi-calculator:before{content:'\e02a'}.oi-calendar:before{content:'\e02b'}.oi-camera-slr:before{content:'\e02c'}.oi-caret-bottom:before{content:'\e02d'}.oi-caret-left:before{content:'\e02e'}.oi-caret-right:before{content:'\e02f'}.oi-caret-top:before{content:'\e030'}.oi-cart:before{content:'\e031'}.oi-chat:before{content:'\e032'}.oi-check:before{content:'\e033'}.oi-chevron-bottom:before{content:'\e034'}.oi-chevron-left:before{content:'\e035'}.oi-chevron-right:before{content:'\e036'}.oi-chevron-top:before{content:'\e037'}.oi-circle-check:before{content:'\e038'}.oi-circle-x:before{content:'\e039'}.oi-clipboard:before{content:'\e03a'}.oi-clock:before{content:'\e03b'}.oi-cloud-download:before{content:'\e03c'}.oi-cloud-upload:before{content:'\e03d'}.oi-cloud:before{content:'\e03e'}.oi-cloudy:before{content:'\e03f'}.oi-code:before{content:'\e040'}.oi-cog:before{content:'\e041'}.oi-collapse-down:before{content:'\e042'}.oi-collapse-left:before{content:'\e043'}.oi-collapse-right:before{content:'\e044'}.oi-collapse-up:before{content:'\e045'}.oi-command:before{content:'\e046'}.oi-comment-square:before{content:'\e047'}.oi-compass:before{content:'\e048'}.oi-contrast:before{content:'\e049'}.oi-copywriting:before{content:'\e04a'}.oi-credit-card:before{content:'\e04b'}.oi-crop:before{content:'\e04c'}.oi-dashboard:before{content:'\e04d'}.oi-data-transfer-download:before{content:'\e04e'}.oi-data-transfer-upload:before{content:'\e04f'}.oi-delete:before{content:'\e050'}.oi-dial:before{content:'\e051'}.oi-document:before{content:'\e052'}.oi-dollar:before{content:'\e053'}.oi-double-quote-sans-left:before{content:'\e054'}.oi-double-quote-sans-right:before{content:'\e055'}.oi-double-quote-serif-left:before{content:'\e056'}.oi-double-quote-serif-right:before{content:'\e057'}.oi-droplet:before{content:'\e058'}.oi-eject:before{content:'\e059'}.oi-elevator:before{content:'\e05a'}.oi-ellipses:before{content:'\e05b'}.oi-envelope-closed:before{content:'\e05c'}.oi-envelope-open:before{content:'\e05d'}.oi-euro:before{content:'\e05e'}.oi-excerpt:before{content:'\e05f'}.oi-expand-down:before{content:'\e060'}.oi-expand-left:before{content:'\e061'}.oi-expand-right:before{content:'\e062'}.oi-expand-up:before{content:'\e063'}.oi-external-link:before{content:'\e064'}.oi-eye:before{content:'\e065'}.oi-eyedropper:before{content:'\e066'}.oi-file:before{content:'\e067'}.oi-fire:before{content:'\e068'}.oi-flag:before{content:'\e069'}.oi-flash:before{content:'\e06a'}.oi-folder:before{content:'\e06b'}.oi-fork:before{content:'\e06c'}.oi-fullscreen-enter:before{content:'\e06d'}.oi-fullscreen-exit:before{content:'\e06e'}.oi-globe:before{content:'\e06f'}.oi-graph:before{content:'\e070'}.oi-grid-four-up:before{content:'\e071'}.oi-grid-three-up:before{content:'\e072'}.oi-grid-two-up:before{content:'\e073'}.oi-hard-drive:before{content:'\e074'}.oi-header:before{content:'\e075'}.oi-headphones:before{content:'\e076'}.oi-heart:before{content:'\e077'}.oi-home:before{content:'\e078'}.oi-image:before{content:'\e079'}.oi-inbox:before{content:'\e07a'}.oi-infinity:before{content:'\e07b'}.oi-info:before{content:'\e07c'}.oi-italic:before{content:'\e07d'}.oi-justify-center:before{content:'\e07e'}.oi-justify-left:before{content:'\e07f'}.oi-justify-right:before{content:'\e080'}.oi-key:before{content:'\e081'}.oi-laptop:before{content:'\e082'}.oi-layers:before{content:'\e083'}.oi-lightbulb:before{content:'\e084'}.oi-link-broken:before{content:'\e085'}.oi-link-intact:before{content:'\e086'}.oi-list-rich:before{content:'\e087'}.oi-list:before{content:'\e088'}.oi-location:before{content:'\e089'}.oi-lock-locked:before{content:'\e08a'}.oi-lock-unlocked:before{content:'\e08b'}.oi-loop-circular:before{content:'\e08c'}.oi-loop-square:before{content:'\e08d'}.oi-loop:before{content:'\e08e'}.oi-magnifying-glass:before{content:'\e08f'}.oi-map-marker:before{content:'\e090'}.oi-map:before{content:'\e091'}.oi-media-pause:before{content:'\e092'}.oi-media-play:before{content:'\e093'}.oi-media-record:before{content:'\e094'}.oi-media-skip-backward:before{content:'\e095'}.oi-media-skip-forward:before{content:'\e096'}.oi-media-step-backward:before{content:'\e097'}.oi-media-step-forward:before{content:'\e098'}.oi-media-stop:before{content:'\e099'}.oi-medical-cross:before{content:'\e09a'}.oi-menu:before{content:'\e09b'}.oi-microphone:before{content:'\e09c'}.oi-minus:before{content:'\e09d'}.oi-monitor:before{content:'\e09e'}.oi-moon:before{content:'\e09f'}.oi-move:before{content:'\e0a0'}.oi-musical-note:before{content:'\e0a1'}.oi-paperclip:before{content:'\e0a2'}.oi-pencil:before{content:'\e0a3'}.oi-people:before{content:'\e0a4'}.oi-person:before{content:'\e0a5'}.oi-phone:before{content:'\e0a6'}.oi-pie-chart:before{content:'\e0a7'}.oi-pin:before{content:'\e0a8'}.oi-play-circle:before{content:'\e0a9'}.oi-plus:before{content:'\e0aa'}.oi-power-standby:before{content:'\e0ab'}.oi-print:before{content:'\e0ac'}.oi-project:before{content:'\e0ad'}.oi-pulse:before{content:'\e0ae'}.oi-puzzle-piece:before{content:'\e0af'}.oi-question-mark:before{content:'\e0b0'}.oi-rain:before{content:'\e0b1'}.oi-random:before{content:'\e0b2'}.oi-reload:before{content:'\e0b3'}.oi-resize-both:before{content:'\e0b4'}.oi-resize-height:before{content:'\e0b5'}.oi-resize-width:before{content:'\e0b6'}.oi-rss-alt:before{content:'\e0b7'}.oi-rss:before{content:'\e0b8'}.oi-script:before{content:'\e0b9'}.oi-share-boxed:before{content:'\e0ba'}.oi-share:before{content:'\e0bb'}.oi-shield:before{content:'\e0bc'}.oi-signal:before{content:'\e0bd'}.oi-signpost:before{content:'\e0be'}.oi-sort-ascending:before{content:'\e0bf'}.oi-sort-descending:before{content:'\e0c0'}.oi-spreadsheet:before{content:'\e0c1'}.oi-star:before{content:'\e0c2'}.oi-sun:before{content:'\e0c3'}.oi-tablet:before{content:'\e0c4'}.oi-tag:before{content:'\e0c5'}.oi-tags:before{content:'\e0c6'}.oi-target:before{content:'\e0c7'}.oi-task:before{content:'\e0c8'}.oi-terminal:before{content:'\e0c9'}.oi-text:before{content:'\e0ca'}.oi-thumb-down:before{content:'\e0cb'}.oi-thumb-up:before{content:'\e0cc'}.oi-timer:before{content:'\e0cd'}.oi-transfer:before{content:'\e0ce'}.oi-trash:before{content:'\e0cf'}.oi-underline:before{content:'\e0d0'}.oi-vertical-align-bottom:before{content:'\e0d1'}.oi-vertical-align-center:before{content:'\e0d2'}.oi-vertical-align-top:before{content:'\e0d3'}.oi-video:before{content:'\e0d4'}.oi-volume-high:before{content:'\e0d5'}.oi-volume-low:before{content:'\e0d6'}.oi-volume-off:before{content:'\e0d7'}.oi-warning:before{content:'\e0d8'}.oi-wifi:before{content:'\e0d9'}.oi-wrench:before{content:'\e0da'}.oi-x:before{content:'\e0db'}.oi-yen:before{content:'\e0dc'}.oi-zoom-in:before{content:'\e0dd'}.oi-zoom-out:before{content:'\e0de'}
--------------------------------------------------------------------------------
/MyApp.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nukedbit/blazor-wasm-servicestack/865e7823bdf7ea7ea2b885a0e6f19ceab00b63e6/MyApp.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot
--------------------------------------------------------------------------------
/MyApp.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nukedbit/blazor-wasm-servicestack/865e7823bdf7ea7ea2b885a0e6f19ceab00b63e6/MyApp.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf
--------------------------------------------------------------------------------
/MyApp.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 | Created by FontForge 20120731 at Tue Jul 1 20:39:22 2014
9 | By P.J. Onori
10 | Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net)
11 |
12 |
13 |
14 |
27 |
28 |
30 |
32 |
34 |
36 |
38 |
40 |
42 |
45 |
47 |
49 |
51 |
53 |
55 |
57 |
59 |
61 |
63 |
65 |
67 |
69 |
71 |
74 |
76 |
79 |
81 |
84 |
86 |
88 |
91 |
93 |
95 |
98 |
100 |
102 |
104 |
106 |
109 |
112 |
115 |
117 |
121 |
123 |
125 |
127 |
130 |
132 |
134 |
136 |
138 |
141 |
143 |
145 |
147 |
149 |
151 |
153 |
155 |
157 |
159 |
162 |
165 |
167 |
169 |
172 |
174 |
177 |
179 |
181 |
183 |
185 |
189 |
191 |
194 |
196 |
198 |
200 |
202 |
205 |
207 |
209 |
211 |
213 |
215 |
218 |
220 |
222 |
224 |
226 |
228 |
230 |
232 |
234 |
236 |
238 |
241 |
243 |
245 |
247 |
249 |
251 |
253 |
256 |
259 |
261 |
263 |
265 |
267 |
269 |
272 |
274 |
276 |
280 |
282 |
285 |
287 |
289 |
292 |
295 |
298 |
300 |
302 |
304 |
306 |
309 |
312 |
314 |
316 |
318 |
320 |
322 |
324 |
326 |
330 |
334 |
338 |
340 |
343 |
345 |
347 |
349 |
351 |
353 |
355 |
358 |
360 |
363 |
365 |
367 |
369 |
371 |
373 |
375 |
377 |
379 |
381 |
383 |
386 |
388 |
390 |
392 |
394 |
396 |
399 |
401 |
404 |
406 |
408 |
410 |
412 |
414 |
416 |
419 |
421 |
423 |
425 |
428 |
431 |
435 |
438 |
440 |
442 |
444 |
446 |
448 |
451 |
453 |
455 |
457 |
460 |
462 |
464 |
466 |
468 |
471 |
473 |
477 |
479 |
481 |
483 |
486 |
488 |
490 |
492 |
494 |
496 |
499 |
501 |
504 |
506 |
509 |
512 |
515 |
517 |
520 |
522 |
524 |
526 |
529 |
532 |
534 |
536 |
539 |
542 |
543 |
544 |
--------------------------------------------------------------------------------
/MyApp.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nukedbit/blazor-wasm-servicestack/865e7823bdf7ea7ea2b885a0e6f19ceab00b63e6/MyApp.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf
--------------------------------------------------------------------------------
/MyApp.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nukedbit/blazor-wasm-servicestack/865e7823bdf7ea7ea2b885a0e6f19ceab00b63e6/MyApp.Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff
--------------------------------------------------------------------------------
/MyApp.Client/wwwroot/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nukedbit/blazor-wasm-servicestack/865e7823bdf7ea7ea2b885a0e6f19ceab00b63e6/MyApp.Client/wwwroot/favicon.ico
--------------------------------------------------------------------------------
/MyApp.Client/wwwroot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | MyApp
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Loading...
17 |
18 |
19 | An unhandled error has occurred.
20 |
Reload
21 |
🗙
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/MyApp.Client/wwwroot/sample-data/weather.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "date": "2018-05-06",
4 | "temperatureC": 1,
5 | "summary": "Freezing"
6 | },
7 | {
8 | "date": "2018-05-07",
9 | "temperatureC": 14,
10 | "summary": "Bracing"
11 | },
12 | {
13 | "date": "2018-05-08",
14 | "temperatureC": -13,
15 | "summary": "Freezing"
16 | },
17 | {
18 | "date": "2018-05-09",
19 | "temperatureC": -16,
20 | "summary": "Balmy"
21 | },
22 | {
23 | "date": "2018-05-10",
24 | "temperatureC": -2,
25 | "summary": "Chilly"
26 | }
27 | ]
28 |
--------------------------------------------------------------------------------
/MyApp.ServiceInterface/MyApp.ServiceInterface.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/MyApp.ServiceInterface/MyServices.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ServiceStack;
3 | using MyApp.ServiceModel;
4 |
5 | namespace MyApp.ServiceInterface
6 | {
7 | public class MyServices : Service
8 | {
9 | public object Any(Hello request)
10 | {
11 | return new HelloResponse { Result = $"Hello, {request.Name}!" };
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/MyApp.ServiceModel/Hello.cs:
--------------------------------------------------------------------------------
1 | using ServiceStack;
2 |
3 | namespace MyApp.ServiceModel
4 | {
5 | [Route("/hello")]
6 | [Route("/hello/{Name}")]
7 | public class Hello : IReturn
8 | {
9 | public string Name { get; set; }
10 | }
11 |
12 | public class HelloResponse
13 | {
14 | public string Result { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/MyApp.ServiceModel/MyApp.ServiceModel.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/MyApp.ServiceModel/Types/README.md:
--------------------------------------------------------------------------------
1 | As part of our [Physical Project Structure](https://docs.servicestack.net/physical-project-structure) convention we recommend maintaining any shared non Request/Response DTOs in the `ServiceModel.Types` namespace.
--------------------------------------------------------------------------------
/MyApp.Tests/IntegrationTest.cs:
--------------------------------------------------------------------------------
1 | using Funq;
2 | using ServiceStack;
3 | using NUnit.Framework;
4 | using MyApp.ServiceInterface;
5 | using MyApp.ServiceModel;
6 |
7 | namespace MyApp.Tests
8 | {
9 | public class IntegrationTest
10 | {
11 | const string BaseUri = "http://localhost:2000/";
12 | private readonly ServiceStackHost appHost;
13 |
14 | class AppHost : AppSelfHostBase
15 | {
16 | public AppHost() : base(nameof(IntegrationTest), typeof(MyServices).Assembly) { }
17 |
18 | public override void Configure(Container container)
19 | {
20 | }
21 | }
22 |
23 | public IntegrationTest()
24 | {
25 | appHost = new AppHost()
26 | .Init()
27 | .Start(BaseUri);
28 | }
29 |
30 | [OneTimeTearDown]
31 | public void OneTimeTearDown() => appHost.Dispose();
32 |
33 | public IServiceClient CreateClient() => new JsonServiceClient(BaseUri);
34 |
35 | [Test]
36 | public void Can_call_Hello_Service()
37 | {
38 | var client = CreateClient();
39 |
40 | var response = client.Get(new Hello { Name = "World" });
41 |
42 | Assert.That(response.Result, Is.EqualTo("Hello, World!"));
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/MyApp.Tests/MyApp.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | portable
6 | Library
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/MyApp.Tests/UnitTest.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using ServiceStack;
3 | using ServiceStack.Testing;
4 | using MyApp.ServiceInterface;
5 | using MyApp.ServiceModel;
6 |
7 | namespace MyApp.Tests
8 | {
9 | public class UnitTest
10 | {
11 | private readonly ServiceStackHost appHost;
12 |
13 | public UnitTest()
14 | {
15 | appHost = new BasicAppHost().Init();
16 | appHost.Container.AddTransient();
17 | }
18 |
19 | [OneTimeTearDown]
20 | public void OneTimeTearDown() => appHost.Dispose();
21 |
22 | [Test]
23 | public void Can_call_MyServices()
24 | {
25 | var service = appHost.Container.Resolve();
26 |
27 | var response = (HelloResponse)service.Any(new Hello { Name = "World" });
28 |
29 | Assert.That(response.Result, Is.EqualTo("Hello, World!"));
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/MyApp.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31320.298
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyApp", "MyApp\MyApp.csproj", "{5F817400-1A3A-48DF-98A6-E7E5A3DC762F}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyApp.ServiceInterface", "MyApp.ServiceInterface\MyApp.ServiceInterface.csproj", "{5B8FFF01-1E0B-477D-9D7F-93016C128B23}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyApp.ServiceModel", "MyApp.ServiceModel\MyApp.ServiceModel.csproj", "{0127B6CA-1B79-46A6-8307-B36836D107F0}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyApp.Tests", "MyApp.Tests\MyApp.Tests.csproj", "{455EC1EF-134F-4CD4-9C78-E813E4E6D8F6}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyApp.Client", "MyApp.Client\MyApp.Client.csproj", "{320C1E1A-F198-420D-88DF-2D929A656551}"
15 | EndProject
16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Files", "Files", "{672D91B4-2CE5-43CA-9AEF-66E6884A6E0D}"
17 | ProjectSection(SolutionItems) = preProject
18 | NuGet.Config = NuGet.Config
19 | README.md = README.md
20 | EndProjectSection
21 | EndProject
22 | Global
23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
24 | Debug|Any CPU = Debug|Any CPU
25 | Release|Any CPU = Release|Any CPU
26 | EndGlobalSection
27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
28 | {5F817400-1A3A-48DF-98A6-E7E5A3DC762F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {5F817400-1A3A-48DF-98A6-E7E5A3DC762F}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {5F817400-1A3A-48DF-98A6-E7E5A3DC762F}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {5F817400-1A3A-48DF-98A6-E7E5A3DC762F}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {5B8FFF01-1E0B-477D-9D7F-93016C128B23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {5B8FFF01-1E0B-477D-9D7F-93016C128B23}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {5B8FFF01-1E0B-477D-9D7F-93016C128B23}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {5B8FFF01-1E0B-477D-9D7F-93016C128B23}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {0127B6CA-1B79-46A6-8307-B36836D107F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {0127B6CA-1B79-46A6-8307-B36836D107F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {0127B6CA-1B79-46A6-8307-B36836D107F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {0127B6CA-1B79-46A6-8307-B36836D107F0}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {455EC1EF-134F-4CD4-9C78-E813E4E6D8F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {455EC1EF-134F-4CD4-9C78-E813E4E6D8F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {455EC1EF-134F-4CD4-9C78-E813E4E6D8F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {455EC1EF-134F-4CD4-9C78-E813E4E6D8F6}.Release|Any CPU.Build.0 = Release|Any CPU
44 | {320C1E1A-F198-420D-88DF-2D929A656551}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {320C1E1A-F198-420D-88DF-2D929A656551}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 | {320C1E1A-F198-420D-88DF-2D929A656551}.Release|Any CPU.ActiveCfg = Release|Any CPU
47 | {320C1E1A-F198-420D-88DF-2D929A656551}.Release|Any CPU.Build.0 = Release|Any CPU
48 | EndGlobalSection
49 | GlobalSection(SolutionProperties) = preSolution
50 | HideSolutionNode = FALSE
51 | EndGlobalSection
52 | GlobalSection(ExtensibilityGlobals) = postSolution
53 | SolutionGuid = {02854F2A-8EF4-468E-80A3-CD64BBAF5D15}
54 | EndGlobalSection
55 | EndGlobal
56 |
--------------------------------------------------------------------------------
/MyApp/Configure.Auth.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using ServiceStack;
4 | using ServiceStack.Auth;
5 | using ServiceStack.FluentValidation;
6 |
7 | namespace MyApp
8 | {
9 | // Add any additional metadata properties you want to store in the Users Typed Session
10 | public class CustomUserSession : AuthUserSession
11 | {
12 | }
13 |
14 | // Custom Validator to add custom validators to built-in /register Service requiring DisplayName and ConfirmPassword
15 | public class CustomRegistrationValidator : RegistrationValidator
16 | {
17 | public CustomRegistrationValidator()
18 | {
19 | RuleSet(ApplyTo.Post, () =>
20 | {
21 | RuleFor(x => x.DisplayName).NotEmpty();
22 | RuleFor(x => x.ConfirmPassword).NotEmpty();
23 | });
24 | }
25 | }
26 |
27 | public class ConfigureAuth : IConfigureAppHost, IConfigureServices
28 | {
29 | public void Configure(IServiceCollection services)
30 | {
31 | //services.AddSingleton(new MemoryCacheClient()); //Store User Sessions in Memory Cache (default)
32 | }
33 |
34 | public void Configure(IAppHost appHost)
35 | {
36 | var AppSettings = appHost.AppSettings;
37 | appHost.Plugins.Add(new AuthFeature(() => new CustomUserSession(),
38 | new IAuthProvider[] {
39 | new JwtAuthProvider(AppSettings)
40 | {
41 | AuthKeyBase64 = AppSettings.GetString("AuthKeyBase64") ?? "cARl12kvS/Ra4moVBIaVsrWwTpXYuZ0mZf/gNLUhDW5=",
42 | },
43 | new CredentialsAuthProvider(AppSettings), /* Sign In with Username / Password credentials */
44 | new FacebookAuthProvider(AppSettings), /* Create App https://developers.facebook.com/apps */
45 | new GoogleAuthProvider(AppSettings), /* Create App https://console.developers.google.com/apis/credentials */
46 | new MicrosoftGraphAuthProvider(AppSettings), /* Create App https://apps.dev.microsoft.com */
47 | }){
48 | IncludeDefaultLogin = false
49 | });
50 |
51 | appHost.Plugins.Add(new RegistrationFeature()); //Enable /register Service
52 |
53 | //override the default registration validation with your own custom implementation
54 | appHost.RegisterAs>();
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/MyApp/Configure.AuthRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using ServiceStack;
5 | using ServiceStack.Web;
6 | using ServiceStack.Data;
7 | using ServiceStack.Auth;
8 | using ServiceStack.Configuration;
9 | using ServiceStack.OrmLite;
10 |
11 | namespace MyApp
12 | {
13 | // Custom User Table with extended Metadata properties
14 | public class AppUser : UserAuth
15 | {
16 | public string ProfileUrl { get; set; }
17 | public string LastLoginIp { get; set; }
18 | public DateTime? LastLoginDate { get; set; }
19 | }
20 |
21 | public class AppUserAuthEvents : AuthEvents
22 | {
23 | public override void OnAuthenticated(IRequest req, IAuthSession session, IServiceBase authService,
24 | IAuthTokens tokens, Dictionary authInfo)
25 | {
26 | var authRepo = HostContext.AppHost.GetAuthRepository(req);
27 | using (authRepo as IDisposable)
28 | {
29 | var userAuth = (AppUser)authRepo.GetUserAuth(session.UserAuthId);
30 | userAuth.ProfileUrl = session.GetProfileUrl();
31 | userAuth.LastLoginIp = req.UserHostAddress;
32 | userAuth.LastLoginDate = DateTime.UtcNow;
33 | authRepo.SaveUserAuth(userAuth);
34 | }
35 | }
36 | }
37 |
38 | public class ConfigureAuthRepository : IConfigureAppHost, IConfigureServices, IPreInitPlugin
39 | {
40 | public void Configure(IServiceCollection services)
41 | {
42 | services.AddSingleton(c =>
43 | new OrmLiteAuthRepository(c.Resolve()) {
44 | UseDistinctRoleTables = true
45 | });
46 | }
47 |
48 | public void Configure(IAppHost appHost)
49 | {
50 | var authRepo = appHost.Resolve();
51 | authRepo.InitSchema();
52 |
53 | CreateUser(authRepo, "admin@localhost.local", "Admin User", "p@55wOrd", roles:new[]{ RoleNames.Admin });
54 | }
55 |
56 | public void BeforePluginsLoaded(IAppHost appHost)
57 | {
58 | appHost.AssertPlugin().AuthEvents.Add(new AppUserAuthEvents());
59 | }
60 |
61 | // Add initial Users to the configured Auth Repository
62 | public void CreateUser(IAuthRepository authRepo, string email, string name, string password, string[] roles)
63 | {
64 | if (authRepo.GetUserAuthByUserName(email) == null)
65 | {
66 | var newAdmin = new AppUser { Email = email, DisplayName = name };
67 | var user = authRepo.CreateUserAuth(newAdmin, password);
68 | authRepo.AssignRoles(user, roles);
69 | }
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/MyApp/Configure.Db.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Microsoft.Extensions.Configuration;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using ServiceStack;
6 | using ServiceStack.Auth;
7 | using ServiceStack.Data;
8 | using ServiceStack.DataAnnotations;
9 | using ServiceStack.OrmLite;
10 |
11 | namespace MyApp
12 | {
13 | public class MyTable
14 | {
15 | [AutoIncrement]
16 | public int Id { get; set; }
17 | public string Name { get; set; }
18 | }
19 |
20 | public class ConfigureDb : IConfigureServices, IConfigureAppHost
21 | {
22 | IConfiguration Configuration { get; }
23 | public ConfigureDb(IConfiguration configuration) => Configuration = configuration;
24 |
25 | public void Configure(IServiceCollection services)
26 | {
27 | services.AddSingleton(new OrmLiteConnectionFactory(
28 | Configuration.GetConnectionString("DefaultConnection")
29 | ?? ":memory:",
30 | SqliteDialect.Provider));
31 | }
32 |
33 | public void Configure(IAppHost appHost)
34 | {
35 | appHost.GetPlugin()?.ScriptMethods.Add(new DbScriptsAsync());
36 |
37 | using (var db = appHost.Resolve().Open())
38 | {
39 | if (db.CreateTableIfNotExists())
40 | {
41 | db.Insert(new MyTable { Name = "Seed Data for new MyTable" });
42 | }
43 | }
44 | }
45 | }
46 |
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/MyApp/MyApp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | MyApp
6 | Exe
7 | MyApp
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/MyApp/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Reflection;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Builder;
6 | using Microsoft.AspNetCore.Hosting;
7 | using Microsoft.Extensions.Hosting;
8 | using Microsoft.Extensions.DependencyInjection;
9 | using Funq;
10 | using Microsoft.Extensions.Configuration;
11 | using ServiceStack;
12 | using MyApp.ServiceInterface;
13 | using MyApp.ServiceModel;
14 |
15 | namespace MyApp
16 | {
17 | public class Program
18 | {
19 | public static void Main(string[] args)
20 | {
21 | CreateWebHostBuilder(args).Build().Run();
22 | }
23 |
24 | public static IHostBuilder CreateWebHostBuilder(string[] args) =>
25 | Host.CreateDefaultBuilder(args)
26 | .ConfigureAppConfiguration((hostingContext, config) =>
27 | {
28 | var env = hostingContext.HostingEnvironment;
29 | config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
30 | .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
31 | config.AddEnvironmentVariables();
32 | })
33 | .ConfigureWebHostDefaults(webBuilder =>
34 | {
35 |
36 | webBuilder.UseModularStartup();
37 | });
38 | }
39 |
40 | public class StartupActivator : ModularStartupActivator
41 | {
42 | public StartupActivator(IConfiguration configuration) : base(configuration)
43 | {
44 |
45 | }
46 | }
47 |
48 | public class Startup : ModularStartup
49 | {
50 | // This method gets called by the runtime. Use this method to add services to the container.
51 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
52 | public new void ConfigureServices(IServiceCollection services)
53 | {
54 | services.AddControllersWithViews();
55 | services.AddRazorPages();
56 | }
57 |
58 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
59 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
60 | {
61 | if (env.IsDevelopment())
62 | {
63 | app.UseWebAssemblyDebugging();
64 | }
65 | else
66 | {
67 | app.UseExceptionHandler("/Error");
68 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
69 | app.UseHsts();
70 | }
71 | app.UseHttpsRedirection();
72 | app.UseBlazorFrameworkFiles();
73 | app.UseStaticFiles();
74 |
75 | app.UseRouting();
76 |
77 | app.UseServiceStack(new AppHost());
78 | app.UseEndpoints(endpoints =>
79 | {
80 | endpoints.MapRazorPages();
81 | endpoints.MapControllers();
82 | endpoints.MapFallbackToFile("index.html");
83 | });
84 | }
85 | }
86 |
87 | public class AppHost : AppHostBase
88 | {
89 | public AppHost()
90 | : base("MyApp", typeof(MyServices).Assembly) {
91 | typeof(Hello).AddAttributes(new AuthenticateAttribute());
92 | }
93 |
94 | public override void Configure(Container container)
95 | {
96 | Plugins.Add(new CorsFeature(allowedHeaders: "Content-Type,Authorization"));
97 |
98 |
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/MyApp/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "MyApp": {
4 | "commandName": "Project",
5 | "dotnetRunMessages": "true",
6 | "launchBrowser": true,
7 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
8 | "applicationUrl": "https://localhost:5003;http://localhost:5002",
9 | "environmentVariables": {
10 | "ASPNETCORE_ENVIRONMENT": "Development"
11 | }
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > This template is now unmantained, ServiceStack as released a new Officially supported template with a better native integration check it out
2 | [ServiceStack v6 Release Notes](https://docs.servicestack.net/releases/v6#blazor-webassembly)
3 |
4 |
5 |
6 |
7 | [](https://github.com/nukedbit/blazor-wasm-servicestack)
8 |
9 | # Blazor WASM & ServiceStack for .NET 6.0 RTM
10 |
11 | This template integrate ServiceStack with Blazor on .NET 6 RTM and [ServiceStack 5.13](https://docs.servicestack.net/releases/v5.13).
12 |
13 |
14 | The blazor wasm app is served through the main ServiceStack web host so you can deploy a single package with both api and client ux.
15 |
16 | ## Quick Start
17 |
18 | Use the x tool to install this template
19 |
20 | $ dotnet tool install -g x
21 |
22 | $ x new nukedbit/blazor-wasm-servicestack ProjectName
23 |
24 | This template come with sqlite db preconfigured so it work out of the box.
25 |
26 |
27 | The blazor example provide a login page with the following default credentials.
28 |
29 | admin@localhost.local
30 | p@55wOrd
31 |
32 | Once logged in you can test calling protected endpoints on call hello page.
33 |
34 | ServiceStack auth is integrated with blazor on ``ServiceStackStateProvider`` and I have provided a StackBaseComponent to use as a base component in your project, so it provide the JsonHttpClient with the bearer token once authenticated.
35 |
36 | Inside the client project you can see this.
37 |
38 | builder.Services.AddScoped(s =>
39 | {
40 | return BlazorClient.Create("https://localhost:5001");
41 | });
42 |
43 | Maybe i will make this automatically get the base url in the future, any contribution is appreciated :).
44 |
45 | ## Code Sharing
46 |
47 | Right now I have got dto sharing by using ``MyApp.ServiceModel`` project with success as suggested on [Physical Project Structure](https://docs.servicestack.net/physical-project-structure)
48 |
49 | ## Running your app
50 |
51 | There are two launch profiles for VSCode, the firt one it will launch the app with blazor debugging, the other one it will launch only the WebHost Debugging.
52 |
53 | 1) Launch and Debug Blazor WebAssembly
54 | 2) .NET Core Launch (web)
55 |
56 |
57 | If you don't know ServiceStack give it a try it come with a AGPL 3 license, but you can get a commercial license for cheap and it will make your day more productive and enjoyable :)
58 |
59 | Check the following links for more info
60 |
61 |
62 | ## Executing Blazor and ServiceStack as a Standalone Desktop app
63 |
64 | [@mythz](https://github.com/mythz) Published an update to the [app dotnet tool](https://docs.servicestack.net/netcore-windows-desktop) that allow you to embedd this in a Windows Chromium Desktop App very easily with the latest v0.0.81+ app tool.
65 |
66 | $ dotnet tool update -g app
67 | $ x new nukedbit/blazor-wasm-servicestack Acme
68 | $ cd Acme\Acme
69 | $ dotnet public -c Release
70 | $ cd bin\Release\net6.0\publish
71 | $ app Acme.dll
72 |
73 | When it will launch this is the end result.
74 |
75 | [](https://github.com/nukedbit/blazor-wasm-servicestack)
76 |
77 | As he suggested [here](https://forums.servicestack.net/t/blazor-web-assembly-template/8950/4)
78 |
79 | You can also create a Windows Desktop Shortcut.
80 |
81 | $ app shortcut Acme.dll
82 |
83 |
84 | ## References
85 |
86 | * https://docs.servicestack.net
87 | * https://servicestack.net
88 |
89 |
90 |
--------------------------------------------------------------------------------
/blazor-servicestack-desktop-app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nukedbit/blazor-wasm-servicestack/865e7823bdf7ea7ea2b885a0e6f19ceab00b63e6/blazor-servicestack-desktop-app.png
--------------------------------------------------------------------------------
/blazor-wasm-servicestack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nukedbit/blazor-wasm-servicestack/865e7823bdf7ea7ea2b885a0e6f19ceab00b63e6/blazor-wasm-servicestack.png
--------------------------------------------------------------------------------